From 56c41a614a5cb5ddcbbc6235c622d162d81d0220 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 26 Feb 2020 17:38:50 +0300 Subject: [PATCH 0001/2442] Remove CommentsPage component --- .../Online/TestSceneCommentsContainer.cs | 3 +- .../Visual/Online/TestSceneCommentsPage.cs | 232 ------------------ .../Overlays/Comments/CommentsContainer.cs | 134 ++++++++-- osu.Game/Overlays/Comments/CommentsPage.cs | 161 ------------ 4 files changed, 112 insertions(+), 418 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Online/TestSceneCommentsPage.cs delete mode 100644 osu.Game/Overlays/Comments/CommentsPage.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs index ece280659c..b9938ab25b 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs @@ -27,8 +27,7 @@ namespace osu.Game.Tests.Visual.Online typeof(OverlaySortTabControl<>), typeof(ShowChildrenButton), typeof(DeletedCommentsCounter), - typeof(VotePill), - typeof(CommentsPage), + typeof(VotePill) }; protected override bool UseOnlineAPI => true; diff --git a/osu.Game.Tests/Visual/Online/TestSceneCommentsPage.cs b/osu.Game.Tests/Visual/Online/TestSceneCommentsPage.cs deleted file mode 100644 index a28a0107a1..0000000000 --- a/osu.Game.Tests/Visual/Online/TestSceneCommentsPage.cs +++ /dev/null @@ -1,232 +0,0 @@ -// 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 osu.Game.Overlays.Comments; -using osu.Game.Overlays; -using osu.Framework.Allocation; -using osu.Game.Online.API.Requests.Responses; -using osu.Game.Users; -using osu.Game.Graphics.UserInterface; -using osu.Framework.Bindables; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics; -using osuTK; -using JetBrains.Annotations; -using NUnit.Framework; - -namespace osu.Game.Tests.Visual.Online -{ - public class TestSceneCommentsPage : OsuTestScene - { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(DrawableComment), - typeof(CommentsPage), - }; - - [Cached] - private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); - - private readonly BindableBool showDeleted = new BindableBool(); - private readonly Container content; - - private TestCommentsPage commentsPage; - - public TestSceneCommentsPage() - { - Add(new FillFlowContainer - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 10), - Children = new Drawable[] - { - new Container - { - AutoSizeAxes = Axes.Y, - Width = 200, - Child = new OsuCheckbox - { - Current = showDeleted, - LabelText = @"Show Deleted" - } - }, - content = new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - } - } - }); - } - - [Test] - public void TestAppendDuplicatedComment() - { - AddStep("Create page", () => createPage(getCommentBundle())); - AddAssert("Dictionary length is 10", () => commentsPage?.DictionaryLength == 10); - AddStep("Append existing comment", () => commentsPage?.AppendComments(getCommentSubBundle())); - AddAssert("Dictionary length is 10", () => commentsPage?.DictionaryLength == 10); - } - - [Test] - public void TestEmptyBundle() - { - AddStep("Create page", () => createPage(getEmptyCommentBundle())); - AddAssert("Dictionary length is 0", () => commentsPage?.DictionaryLength == 0); - } - - private void createPage(CommentBundle commentBundle) - { - commentsPage = null; - content.Clear(); - content.Add(commentsPage = new TestCommentsPage(commentBundle) - { - ShowDeleted = { BindTarget = showDeleted } - }); - } - - private CommentBundle getEmptyCommentBundle() => new CommentBundle - { - Comments = new List(), - }; - - private CommentBundle getCommentBundle() => new CommentBundle - { - Comments = new List - { - new Comment - { - Id = 1, - Message = "Simple test comment", - LegacyName = "TestUser1", - CreatedAt = DateTimeOffset.Now, - VotesCount = 5 - }, - new Comment - { - Id = 100, - Message = "This comment has \"load replies\" button because it has unloaded replies", - LegacyName = "TestUser1100", - CreatedAt = DateTimeOffset.Now, - VotesCount = 5, - RepliesCount = 2, - }, - new Comment - { - Id = 111, - Message = "This comment has \"Show More\" button because it has unloaded replies, but some of them are loaded", - LegacyName = "TestUser1111", - CreatedAt = DateTimeOffset.Now, - VotesCount = 100, - RepliesCount = 2, - }, - new Comment - { - Id = 112, - ParentId = 111, - Message = "I'm here to make my parent work", - LegacyName = "someone", - CreatedAt = DateTimeOffset.Now, - VotesCount = 2, - }, - new Comment - { - Id = 2, - Message = "This comment has been deleted :( but visible for admins", - LegacyName = "TestUser2", - CreatedAt = DateTimeOffset.Now, - DeletedAt = DateTimeOffset.Now, - VotesCount = 5 - }, - new Comment - { - Id = 3, - Message = "This comment is a top level", - LegacyName = "TestUser3", - CreatedAt = DateTimeOffset.Now, - RepliesCount = 2, - }, - new Comment - { - Id = 4, - ParentId = 3, - Message = "And this is a reply", - RepliesCount = 1, - LegacyName = "TestUser1", - CreatedAt = DateTimeOffset.Now, - }, - new Comment - { - Id = 15, - ParentId = 4, - Message = "Reply to reply", - LegacyName = "TestUser1", - CreatedAt = DateTimeOffset.Now, - }, - new Comment - { - Id = 6, - ParentId = 3, - LegacyName = "TestUser11515", - CreatedAt = DateTimeOffset.Now, - DeletedAt = DateTimeOffset.Now, - }, - new Comment - { - Id = 5, - Message = "This comment is voted and edited", - LegacyName = "BigBrainUser", - CreatedAt = DateTimeOffset.Now, - EditedAt = DateTimeOffset.Now, - VotesCount = 1000, - EditedById = 1, - } - }, - IncludedComments = new List(), - UserVotes = new List - { - 5 - }, - Users = new List - { - new User - { - Id = 1, - Username = "Good_Admin" - } - }, - }; - - private CommentBundle getCommentSubBundle() => new CommentBundle - { - Comments = new List - { - new Comment - { - Id = 1, - Message = "Simple test comment", - LegacyName = "TestUser1", - CreatedAt = DateTimeOffset.Now, - VotesCount = 5 - }, - }, - IncludedComments = new List(), - }; - - private class TestCommentsPage : CommentsPage - { - public TestCommentsPage(CommentBundle commentBundle) - : base(commentBundle) - { - } - - public new void AppendComments([NotNull] CommentBundle bundle) => base.AppendComments(bundle); - - public int DictionaryLength => CommentDictionary.Count; - } - } -} diff --git a/osu.Game/Overlays/Comments/CommentsContainer.cs b/osu.Game/Overlays/Comments/CommentsContainer.cs index 591a9dc86e..ef833b9345 100644 --- a/osu.Game/Overlays/Comments/CommentsContainer.cs +++ b/osu.Game/Overlays/Comments/CommentsContainer.cs @@ -9,10 +9,12 @@ using osu.Framework.Graphics; using osu.Framework.Bindables; using osu.Framework.Graphics.Shapes; using osu.Game.Online.API.Requests.Responses; -using System.Threading; using System.Linq; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Users; +using System.Collections.Generic; +using JetBrains.Annotations; +using osu.Game.Graphics.Sprites; namespace osu.Game.Overlays.Comments { @@ -30,7 +32,6 @@ namespace osu.Game.Overlays.Comments private IAPIProvider api { get; set; } private GetCommentsRequest request; - private CancellationTokenSource loadCancellation; private int currentPage; private FillFlowContainer content; @@ -151,9 +152,8 @@ namespace osu.Game.Overlays.Comments return; request?.Cancel(); - loadCancellation?.Cancel(); request = new GetCommentsRequest(id.Value, type.Value, Sort.Value, currentPage++, 0); - request.Success += onSuccess; + request.Success += response => Schedule(() => onSuccess(response)); api.PerformAsync(request); } @@ -166,44 +166,132 @@ namespace osu.Game.Overlays.Comments content.Clear(); } + private readonly Dictionary commentDictionary = new Dictionary(); + private void onSuccess(CommentBundle response) { - loadCancellation = new CancellationTokenSource(); - - LoadComponentAsync(new CommentsPage(response) + if (!response.Comments.Any()) { - ShowDeleted = { BindTarget = ShowDeleted }, - Sort = { BindTarget = Sort }, - Type = { BindTarget = type }, - CommentableId = { BindTarget = id } - }, loaded => + content.Add(new NoCommentsPlaceholder()); + return; + } + else { - content.Add(loaded); + appendComments(response); deletedCommentsCounter.Count.Value += response.Comments.Count(c => c.IsDeleted && c.IsTopLevel); + } - if (response.HasMore) + if (response.HasMore) + { + int loadedTopLevelComments = 0; + content.Children.OfType().ForEach(p => loadedTopLevelComments++); + + moreButton.Current.Value = response.TopLevelCount - loadedTopLevelComments; + moreButton.IsLoading = false; + } + else + { + moreButton.Hide(); + } + } + + /// + /// Appends retrieved comments to the subtree rooted of comments in this page. + /// + /// The bundle of comments to add. + private void appendComments([NotNull] CommentBundle bundle) + { + var orphaned = new List(); + + foreach (var comment in bundle.Comments.Concat(bundle.IncludedComments)) + { + // Exclude possible duplicated comments. + if (commentDictionary.ContainsKey(comment.Id)) + continue; + + addNewComment(comment); + } + + // Comments whose parents were seen later than themselves can now be added. + foreach (var o in orphaned) + addNewComment(o); + + void addNewComment(Comment comment) + { + var drawableComment = getDrawableComment(comment); + + if (comment.ParentId == null) { - int loadedTopLevelComments = 0; - content.Children.OfType().ForEach(p => loadedTopLevelComments += p.Children.OfType().Count()); - - moreButton.Current.Value = response.TopLevelCount - loadedTopLevelComments; - moreButton.IsLoading = false; + // Comments that have no parent are added as top-level comments to the flow. + content.Add(drawableComment); + } + else if (commentDictionary.TryGetValue(comment.ParentId.Value, out var parentDrawable)) + { + // The comment's parent has already been seen, so the parent<-> child links can be added. + comment.ParentComment = parentDrawable.Comment; + parentDrawable.Replies.Add(drawableComment); } else { - moreButton.Hide(); + // The comment's parent has not been seen yet, so keep it orphaned for the time being. This can occur if the comments arrive out of order. + // Since this comment has now been seen, any further children can be added to it without being orphaned themselves. + orphaned.Add(comment); } + } + } - commentCounter.Current.Value = response.Total; - }, loadCancellation.Token); + private DrawableComment getDrawableComment(Comment comment) + { + if (commentDictionary.TryGetValue(comment.Id, out var existing)) + return existing; + + return commentDictionary[comment.Id] = new DrawableComment(comment) + { + ShowDeleted = { BindTarget = ShowDeleted }, + Sort = { BindTarget = Sort }, + RepliesRequested = onCommentRepliesRequested + }; + } + + private void onCommentRepliesRequested(DrawableComment drawableComment, int page) + { + var request = new GetCommentsRequest(id.Value, type.Value, Sort.Value, page, drawableComment.Comment.Id); + + request.Success += response => Schedule(() => appendComments(response)); + + api.PerformAsync(request); } protected override void Dispose(bool isDisposing) { request?.Cancel(); - loadCancellation?.Cancel(); base.Dispose(isDisposing); } + + private class NoCommentsPlaceholder : CompositeDrawable + { + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + Height = 80; + RelativeSizeAxes = Axes.X; + AddRangeInternal(new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background4 + }, + new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding { Left = 50 }, + Text = @"No comments yet." + } + }); + } + } } } diff --git a/osu.Game/Overlays/Comments/CommentsPage.cs b/osu.Game/Overlays/Comments/CommentsPage.cs deleted file mode 100644 index 9b146b0a7d..0000000000 --- a/osu.Game/Overlays/Comments/CommentsPage.cs +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Allocation; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics; -using osu.Framework.Bindables; -using osu.Game.Online.API.Requests.Responses; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics.Sprites; -using System.Linq; -using osu.Game.Online.API.Requests; -using osu.Game.Online.API; -using System.Collections.Generic; -using JetBrains.Annotations; - -namespace osu.Game.Overlays.Comments -{ - public class CommentsPage : CompositeDrawable - { - public readonly BindableBool ShowDeleted = new BindableBool(); - public readonly Bindable Sort = new Bindable(); - public readonly Bindable Type = new Bindable(); - public readonly BindableLong CommentableId = new BindableLong(); - - [Resolved] - private IAPIProvider api { get; set; } - - private readonly CommentBundle commentBundle; - private FillFlowContainer flow; - - public CommentsPage(CommentBundle commentBundle) - { - this.commentBundle = commentBundle; - } - - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - AddRangeInternal(new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background5 - }, - flow = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - } - }); - - if (!commentBundle.Comments.Any()) - { - flow.Add(new NoCommentsPlaceholder()); - return; - } - - AppendComments(commentBundle); - } - - private DrawableComment getDrawableComment(Comment comment) - { - if (CommentDictionary.TryGetValue(comment.Id, out var existing)) - return existing; - - return CommentDictionary[comment.Id] = new DrawableComment(comment) - { - ShowDeleted = { BindTarget = ShowDeleted }, - Sort = { BindTarget = Sort }, - RepliesRequested = onCommentRepliesRequested - }; - } - - private void onCommentRepliesRequested(DrawableComment drawableComment, int page) - { - var request = new GetCommentsRequest(CommentableId.Value, Type.Value, Sort.Value, page, drawableComment.Comment.Id); - - request.Success += response => Schedule(() => AppendComments(response)); - - api.PerformAsync(request); - } - - protected readonly Dictionary CommentDictionary = new Dictionary(); - - /// - /// Appends retrieved comments to the subtree rooted of comments in this page. - /// - /// The bundle of comments to add. - protected void AppendComments([NotNull] CommentBundle bundle) - { - var orphaned = new List(); - - foreach (var comment in bundle.Comments.Concat(bundle.IncludedComments)) - { - // Exclude possible duplicated comments. - if (CommentDictionary.ContainsKey(comment.Id)) - continue; - - addNewComment(comment); - } - - // Comments whose parents were seen later than themselves can now be added. - foreach (var o in orphaned) - addNewComment(o); - - void addNewComment(Comment comment) - { - var drawableComment = getDrawableComment(comment); - - if (comment.ParentId == null) - { - // Comments that have no parent are added as top-level comments to the flow. - flow.Add(drawableComment); - } - else if (CommentDictionary.TryGetValue(comment.ParentId.Value, out var parentDrawable)) - { - // The comment's parent has already been seen, so the parent<-> child links can be added. - comment.ParentComment = parentDrawable.Comment; - parentDrawable.Replies.Add(drawableComment); - } - else - { - // The comment's parent has not been seen yet, so keep it orphaned for the time being. This can occur if the comments arrive out of order. - // Since this comment has now been seen, any further children can be added to it without being orphaned themselves. - orphaned.Add(comment); - } - } - } - - private class NoCommentsPlaceholder : CompositeDrawable - { - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) - { - Height = 80; - RelativeSizeAxes = Axes.X; - AddRangeInternal(new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background4 - }, - new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Margin = new MarginPadding { Left = 50 }, - Text = @"No comments yet." - } - }); - } - } - } -} From ae4f5cdf371fbc3d8be399e47bb440ce2cb0f39f Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 26 Feb 2020 18:00:48 +0300 Subject: [PATCH 0002/2442] Add async loading support --- .../Overlays/Comments/CommentsContainer.cs | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/osu.Game/Overlays/Comments/CommentsContainer.cs b/osu.Game/Overlays/Comments/CommentsContainer.cs index ef833b9345..988b8cc4d4 100644 --- a/osu.Game/Overlays/Comments/CommentsContainer.cs +++ b/osu.Game/Overlays/Comments/CommentsContainer.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Bindables; using osu.Framework.Graphics.Shapes; using osu.Game.Online.API.Requests.Responses; +using System.Threading; using System.Linq; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Users; @@ -32,6 +33,7 @@ namespace osu.Game.Overlays.Comments private IAPIProvider api { get; set; } private GetCommentsRequest request; + private CancellationTokenSource loadCancellation; private int currentPage; private FillFlowContainer content; @@ -152,6 +154,7 @@ namespace osu.Game.Overlays.Comments return; request?.Cancel(); + loadCancellation?.Cancel(); request = new GetCommentsRequest(id.Value, type.Value, Sort.Value, currentPage++, 0); request.Success += response => Schedule(() => onSuccess(response)); api.PerformAsync(request); @@ -164,6 +167,7 @@ namespace osu.Game.Overlays.Comments moreButton.Show(); moreButton.IsLoading = true; content.Clear(); + commentDictionary.Clear(); } private readonly Dictionary commentDictionary = new Dictionary(); @@ -173,26 +177,33 @@ namespace osu.Game.Overlays.Comments if (!response.Comments.Any()) { content.Add(new NoCommentsPlaceholder()); + moreButton.Hide(); return; } else { - appendComments(response); + var topLevelComments = appendComments(response); - deletedCommentsCounter.Count.Value += response.Comments.Count(c => c.IsDeleted && c.IsTopLevel); - } + LoadComponentsAsync(topLevelComments, loaded => + { + content.AddRange(loaded); - if (response.HasMore) - { - int loadedTopLevelComments = 0; - content.Children.OfType().ForEach(p => loadedTopLevelComments++); + deletedCommentsCounter.Count.Value += response.Comments.Count(c => c.IsDeleted && c.IsTopLevel); + commentCounter.Current.Value = response.Total; - moreButton.Current.Value = response.TopLevelCount - loadedTopLevelComments; - moreButton.IsLoading = false; - } - else - { - moreButton.Hide(); + if (response.HasMore) + { + int loadedTopLevelComments = 0; + content.Children.OfType().ForEach(p => loadedTopLevelComments++); + + moreButton.Current.Value = response.TopLevelCount - loadedTopLevelComments; + moreButton.IsLoading = false; + } + else + { + moreButton.Hide(); + } + }, (loadCancellation = new CancellationTokenSource()).Token); } } @@ -200,8 +211,9 @@ namespace osu.Game.Overlays.Comments /// Appends retrieved comments to the subtree rooted of comments in this page. /// /// The bundle of comments to add. - private void appendComments([NotNull] CommentBundle bundle) + private List appendComments([NotNull] CommentBundle bundle) { + var topLevelComments = new List(); var orphaned = new List(); foreach (var comment in bundle.Comments.Concat(bundle.IncludedComments)) @@ -217,6 +229,8 @@ namespace osu.Game.Overlays.Comments foreach (var o in orphaned) addNewComment(o); + return topLevelComments; + void addNewComment(Comment comment) { var drawableComment = getDrawableComment(comment); @@ -224,7 +238,7 @@ namespace osu.Game.Overlays.Comments if (comment.ParentId == null) { // Comments that have no parent are added as top-level comments to the flow. - content.Add(drawableComment); + topLevelComments.Add(drawableComment); } else if (commentDictionary.TryGetValue(comment.ParentId.Value, out var parentDrawable)) { @@ -266,6 +280,7 @@ namespace osu.Game.Overlays.Comments protected override void Dispose(bool isDisposing) { request?.Cancel(); + loadCancellation?.Cancel(); base.Dispose(isDisposing); } From de10e502782f337c1c37c3b88ac96a98f3eb0091 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 26 Feb 2020 18:31:01 +0300 Subject: [PATCH 0003/2442] Add test scene to test local comment bundles --- .../TestSceneOfflineCommentsContainer.cs | 177 ++++++++++++++++++ .../Overlays/Comments/CommentsContainer.cs | 16 +- 2 files changed, 185 insertions(+), 8 deletions(-) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneOfflineCommentsContainer.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneOfflineCommentsContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneOfflineCommentsContainer.cs new file mode 100644 index 0000000000..b69cc46bb8 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneOfflineCommentsContainer.cs @@ -0,0 +1,177 @@ +// 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 NUnit.Framework; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics; +using osu.Game.Overlays.Comments; +using osu.Game.Overlays; +using osu.Framework.Allocation; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneOfflineCommentsContainer : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(CommentsContainer), + typeof(CommentsHeader), + typeof(DrawableComment), + typeof(HeaderButton), + typeof(OverlaySortTabControl<>), + typeof(ShowChildrenButton), + typeof(DeletedCommentsCounter), + typeof(VotePill) + }; + + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); + + private TestCommentsContainer comments; + + [SetUp] + public void SetUp() => Schedule(() => + { + Clear(); + Add(new BasicScrollContainer + { + RelativeSizeAxes = Axes.Both, + Child = comments = new TestCommentsContainer() + }); + }); + + [Test] + public void TestLocalCommentBundle() + { + AddStep("Add comment bundle", () => comments.ShowComments(getCommentBundle())); + AddStep("Add empty comment bundle", () => comments.ShowComments(getEmptyCommentBundle())); + } + + private CommentBundle getEmptyCommentBundle() => new CommentBundle + { + Comments = new List(), + }; + + private CommentBundle getCommentBundle() => new CommentBundle + { + Comments = new List + { + new Comment + { + Id = 1, + Message = "Simple test comment", + LegacyName = "TestUser1", + CreatedAt = DateTimeOffset.Now, + VotesCount = 5 + }, + new Comment + { + Id = 100, + Message = "This comment has \"load replies\" button because it has unloaded replies", + LegacyName = "TestUser1100", + CreatedAt = DateTimeOffset.Now, + VotesCount = 5, + RepliesCount = 2, + }, + new Comment + { + Id = 111, + Message = "This comment has \"Show More\" button because it has unloaded replies, but some of them are loaded", + LegacyName = "TestUser1111", + CreatedAt = DateTimeOffset.Now, + VotesCount = 100, + RepliesCount = 2, + }, + new Comment + { + Id = 112, + ParentId = 111, + Message = "I'm here to make my parent work", + LegacyName = "someone", + CreatedAt = DateTimeOffset.Now, + VotesCount = 2, + }, + new Comment + { + Id = 2, + Message = "This comment has been deleted :( but visible for admins", + LegacyName = "TestUser2", + CreatedAt = DateTimeOffset.Now, + DeletedAt = DateTimeOffset.Now, + VotesCount = 5 + }, + new Comment + { + Id = 3, + Message = "This comment is a top level", + LegacyName = "TestUser3", + CreatedAt = DateTimeOffset.Now, + RepliesCount = 2, + }, + new Comment + { + Id = 4, + ParentId = 3, + Message = "And this is a reply", + RepliesCount = 1, + LegacyName = "TestUser1", + CreatedAt = DateTimeOffset.Now, + }, + new Comment + { + Id = 15, + ParentId = 4, + Message = "Reply to reply", + LegacyName = "TestUser1", + CreatedAt = DateTimeOffset.Now, + }, + new Comment + { + Id = 6, + ParentId = 3, + LegacyName = "TestUser11515", + CreatedAt = DateTimeOffset.Now, + DeletedAt = DateTimeOffset.Now, + }, + new Comment + { + Id = 5, + Message = "This comment is voted and edited", + LegacyName = "BigBrainUser", + CreatedAt = DateTimeOffset.Now, + EditedAt = DateTimeOffset.Now, + VotesCount = 1000, + EditedById = 1, + } + }, + IncludedComments = new List(), + UserVotes = new List + { + 5 + }, + Users = new List + { + new User + { + Id = 1, + Username = "Good_Admin" + } + }, + Total = 10 + }; + + private class TestCommentsContainer : CommentsContainer + { + public void ShowComments(CommentBundle bundle) + { + CommentCounter.Current.Value = 0; + ClearComments(); + OnSuccess(bundle); + } + } + } +} diff --git a/osu.Game/Overlays/Comments/CommentsContainer.cs b/osu.Game/Overlays/Comments/CommentsContainer.cs index 988b8cc4d4..00aa74f9ac 100644 --- a/osu.Game/Overlays/Comments/CommentsContainer.cs +++ b/osu.Game/Overlays/Comments/CommentsContainer.cs @@ -39,7 +39,7 @@ namespace osu.Game.Overlays.Comments private FillFlowContainer content; private DeletedCommentsCounter deletedCommentsCounter; private CommentsShowMoreButton moreButton; - private TotalCommentsCounter commentCounter; + protected TotalCommentsCounter CommentCounter; [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) @@ -60,7 +60,7 @@ namespace osu.Game.Overlays.Comments Direction = FillDirection.Vertical, Children = new Drawable[] { - commentCounter = new TotalCommentsCounter(), + CommentCounter = new TotalCommentsCounter(), new CommentsHeader { Sort = { BindTarget = Sort }, @@ -137,14 +137,14 @@ namespace osu.Game.Overlays.Comments return; // only reset when changing ID/type. other refetch ops are generally just changing sort order. - commentCounter.Current.Value = 0; + CommentCounter.Current.Value = 0; refetchComments(); } private void refetchComments() { - clearComments(); + ClearComments(); getComments(); } @@ -156,11 +156,11 @@ namespace osu.Game.Overlays.Comments request?.Cancel(); loadCancellation?.Cancel(); request = new GetCommentsRequest(id.Value, type.Value, Sort.Value, currentPage++, 0); - request.Success += response => Schedule(() => onSuccess(response)); + request.Success += response => Schedule(() => OnSuccess(response)); api.PerformAsync(request); } - private void clearComments() + protected void ClearComments() { currentPage = 1; deletedCommentsCounter.Count.Value = 0; @@ -172,7 +172,7 @@ namespace osu.Game.Overlays.Comments private readonly Dictionary commentDictionary = new Dictionary(); - private void onSuccess(CommentBundle response) + protected void OnSuccess(CommentBundle response) { if (!response.Comments.Any()) { @@ -189,7 +189,7 @@ namespace osu.Game.Overlays.Comments content.AddRange(loaded); deletedCommentsCounter.Count.Value += response.Comments.Count(c => c.IsDeleted && c.IsTopLevel); - commentCounter.Current.Value = response.Total; + CommentCounter.Current.Value = response.Total; if (response.HasMore) { From 1166d3d69624bad7e6ed645d5a5ac1c53201a92e Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 26 Feb 2020 18:52:58 +0300 Subject: [PATCH 0004/2442] CI fixes --- .../Overlays/Comments/CommentsContainer.cs | 46 +++++++++---------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/osu.Game/Overlays/Comments/CommentsContainer.cs b/osu.Game/Overlays/Comments/CommentsContainer.cs index 00aa74f9ac..da2cdbc335 100644 --- a/osu.Game/Overlays/Comments/CommentsContainer.cs +++ b/osu.Game/Overlays/Comments/CommentsContainer.cs @@ -180,31 +180,29 @@ namespace osu.Game.Overlays.Comments moreButton.Hide(); return; } - else + + var topLevelComments = appendComments(response); + + LoadComponentsAsync(topLevelComments, loaded => { - var topLevelComments = appendComments(response); + content.AddRange(loaded); - LoadComponentsAsync(topLevelComments, loaded => + deletedCommentsCounter.Count.Value += response.Comments.Count(c => c.IsDeleted && c.IsTopLevel); + CommentCounter.Current.Value = response.Total; + + if (response.HasMore) { - content.AddRange(loaded); + int loadedTopLevelComments = 0; + content.Children.OfType().ForEach(p => loadedTopLevelComments++); - deletedCommentsCounter.Count.Value += response.Comments.Count(c => c.IsDeleted && c.IsTopLevel); - CommentCounter.Current.Value = response.Total; - - if (response.HasMore) - { - int loadedTopLevelComments = 0; - content.Children.OfType().ForEach(p => loadedTopLevelComments++); - - moreButton.Current.Value = response.TopLevelCount - loadedTopLevelComments; - moreButton.IsLoading = false; - } - else - { - moreButton.Hide(); - } - }, (loadCancellation = new CancellationTokenSource()).Token); - } + moreButton.Current.Value = response.TopLevelCount - loadedTopLevelComments; + moreButton.IsLoading = false; + } + else + { + moreButton.Hide(); + } + }, (loadCancellation = new CancellationTokenSource()).Token); } /// @@ -270,11 +268,11 @@ namespace osu.Game.Overlays.Comments private void onCommentRepliesRequested(DrawableComment drawableComment, int page) { - var request = new GetCommentsRequest(id.Value, type.Value, Sort.Value, page, drawableComment.Comment.Id); + var req = new GetCommentsRequest(id.Value, type.Value, Sort.Value, page, drawableComment.Comment.Id); - request.Success += response => Schedule(() => appendComments(response)); + req.Success += response => Schedule(() => appendComments(response)); - api.PerformAsync(request); + api.PerformAsync(req); } protected override void Dispose(bool isDisposing) From 638d0601451c88872c727baf5c9cc653fcb5b7ec Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 26 Feb 2020 18:55:43 +0300 Subject: [PATCH 0005/2442] Fix incorrect character on RepliesButton --- osu.Game/Overlays/Comments/DrawableComment.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Comments/DrawableComment.cs b/osu.Game/Overlays/Comments/DrawableComment.cs index cb9e32f1ad..46f600615a 100644 --- a/osu.Game/Overlays/Comments/DrawableComment.cs +++ b/osu.Game/Overlays/Comments/DrawableComment.cs @@ -384,7 +384,7 @@ namespace osu.Game.Overlays.Comments protected override void OnExpandedChanged(ValueChangedEvent expanded) { - text.Text = $@"{(expanded.NewValue ? "[+]" : "[-]")} replies ({count})"; + text.Text = $@"{(expanded.NewValue ? "[-]" : "[+]")} replies ({count})"; } } From ddb494efb39d154d0dfae81fbd488395bbd5a16c Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 27 Feb 2020 01:38:21 +0300 Subject: [PATCH 0006/2442] Add back regression test --- .../TestSceneOfflineCommentsContainer.cs | 29 +++++++++++++++++++ .../Overlays/Comments/CommentsContainer.cs | 18 ++++++------ 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneOfflineCommentsContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneOfflineCommentsContainer.cs index b69cc46bb8..4040f36ef8 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneOfflineCommentsContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneOfflineCommentsContainer.cs @@ -51,6 +51,15 @@ namespace osu.Game.Tests.Visual.Online AddStep("Add empty comment bundle", () => comments.ShowComments(getEmptyCommentBundle())); } + [Test] + public void TestAppendDuplicatedComment() + { + AddStep("Add comment bundle", () => comments.ShowComments(getCommentBundle())); + AddUntilStep("Dictionary length is 10", () => comments.DictionaryLength == 10); + AddStep("Append existing comment", () => comments.AppendComments(getCommentSubBundle())); + AddAssert("Dictionary length is 10", () => comments.DictionaryLength == 10); + } + private CommentBundle getEmptyCommentBundle() => new CommentBundle { Comments = new List(), @@ -164,8 +173,28 @@ namespace osu.Game.Tests.Visual.Online Total = 10 }; + private CommentBundle getCommentSubBundle() => new CommentBundle + { + Comments = new List + { + new Comment + { + Id = 1, + Message = "Simple test comment", + LegacyName = "TestUser1", + CreatedAt = DateTimeOffset.Now, + VotesCount = 5 + }, + }, + IncludedComments = new List(), + }; + private class TestCommentsContainer : CommentsContainer { + public int DictionaryLength => CommentDictionary.Count; + + public new void AppendComments(CommentBundle bundle) => base.AppendComments(bundle); + public void ShowComments(CommentBundle bundle) { CommentCounter.Current.Value = 0; diff --git a/osu.Game/Overlays/Comments/CommentsContainer.cs b/osu.Game/Overlays/Comments/CommentsContainer.cs index da2cdbc335..0381d73583 100644 --- a/osu.Game/Overlays/Comments/CommentsContainer.cs +++ b/osu.Game/Overlays/Comments/CommentsContainer.cs @@ -167,10 +167,10 @@ namespace osu.Game.Overlays.Comments moreButton.Show(); moreButton.IsLoading = true; content.Clear(); - commentDictionary.Clear(); + CommentDictionary.Clear(); } - private readonly Dictionary commentDictionary = new Dictionary(); + protected readonly Dictionary CommentDictionary = new Dictionary(); protected void OnSuccess(CommentBundle response) { @@ -181,7 +181,7 @@ namespace osu.Game.Overlays.Comments return; } - var topLevelComments = appendComments(response); + var topLevelComments = AppendComments(response); LoadComponentsAsync(topLevelComments, loaded => { @@ -209,7 +209,7 @@ namespace osu.Game.Overlays.Comments /// Appends retrieved comments to the subtree rooted of comments in this page. /// /// The bundle of comments to add. - private List appendComments([NotNull] CommentBundle bundle) + protected List AppendComments([NotNull] CommentBundle bundle) { var topLevelComments = new List(); var orphaned = new List(); @@ -217,7 +217,7 @@ namespace osu.Game.Overlays.Comments foreach (var comment in bundle.Comments.Concat(bundle.IncludedComments)) { // Exclude possible duplicated comments. - if (commentDictionary.ContainsKey(comment.Id)) + if (CommentDictionary.ContainsKey(comment.Id)) continue; addNewComment(comment); @@ -238,7 +238,7 @@ namespace osu.Game.Overlays.Comments // Comments that have no parent are added as top-level comments to the flow. topLevelComments.Add(drawableComment); } - else if (commentDictionary.TryGetValue(comment.ParentId.Value, out var parentDrawable)) + else if (CommentDictionary.TryGetValue(comment.ParentId.Value, out var parentDrawable)) { // The comment's parent has already been seen, so the parent<-> child links can be added. comment.ParentComment = parentDrawable.Comment; @@ -255,10 +255,10 @@ namespace osu.Game.Overlays.Comments private DrawableComment getDrawableComment(Comment comment) { - if (commentDictionary.TryGetValue(comment.Id, out var existing)) + if (CommentDictionary.TryGetValue(comment.Id, out var existing)) return existing; - return commentDictionary[comment.Id] = new DrawableComment(comment) + return CommentDictionary[comment.Id] = new DrawableComment(comment) { ShowDeleted = { BindTarget = ShowDeleted }, Sort = { BindTarget = Sort }, @@ -270,7 +270,7 @@ namespace osu.Game.Overlays.Comments { var req = new GetCommentsRequest(id.Value, type.Value, Sort.Value, page, drawableComment.Comment.Id); - req.Success += response => Schedule(() => appendComments(response)); + req.Success += response => Schedule(() => AppendComments(response)); api.PerformAsync(req); } From 02fd85d485f232745df6296e30d56da410ec66cd Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 27 Feb 2020 01:42:55 +0300 Subject: [PATCH 0007/2442] CI fix --- .../TestSceneOfflineCommentsContainer.cs | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneOfflineCommentsContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneOfflineCommentsContainer.cs index 4040f36ef8..1ba8bbf7c4 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneOfflineCommentsContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneOfflineCommentsContainer.cs @@ -11,6 +11,7 @@ using osu.Game.Overlays; using osu.Framework.Allocation; using osu.Game.Online.API.Requests.Responses; using osu.Game.Users; +using JetBrains.Annotations; namespace osu.Game.Tests.Visual.Online { @@ -44,13 +45,6 @@ namespace osu.Game.Tests.Visual.Online }); }); - [Test] - public void TestLocalCommentBundle() - { - AddStep("Add comment bundle", () => comments.ShowComments(getCommentBundle())); - AddStep("Add empty comment bundle", () => comments.ShowComments(getEmptyCommentBundle())); - } - [Test] public void TestAppendDuplicatedComment() { @@ -60,6 +54,13 @@ namespace osu.Game.Tests.Visual.Online AddAssert("Dictionary length is 10", () => comments.DictionaryLength == 10); } + [Test] + public void TestLocalCommentBundle() + { + AddStep("Add comment bundle", () => comments.ShowComments(getCommentBundle())); + AddStep("Add empty comment bundle", () => comments.ShowComments(getEmptyCommentBundle())); + } + private CommentBundle getEmptyCommentBundle() => new CommentBundle { Comments = new List(), @@ -175,25 +176,25 @@ namespace osu.Game.Tests.Visual.Online private CommentBundle getCommentSubBundle() => new CommentBundle { - Comments = new List - { - new Comment - { - Id = 1, - Message = "Simple test comment", - LegacyName = "TestUser1", - CreatedAt = DateTimeOffset.Now, - VotesCount = 5 - }, - }, - IncludedComments = new List(), + Comments = new List + { + new Comment + { + Id = 1, + Message = "Simple test comment", + LegacyName = "TestUser1", + CreatedAt = DateTimeOffset.Now, + VotesCount = 5 + }, + }, + IncludedComments = new List(), }; private class TestCommentsContainer : CommentsContainer { - public int DictionaryLength => CommentDictionary.Count; + public new void AppendComments([NotNull] CommentBundle bundle) => base.AppendComments(bundle); - public new void AppendComments(CommentBundle bundle) => base.AppendComments(bundle); + public int DictionaryLength => CommentDictionary.Count; public void ShowComments(CommentBundle bundle) { From e2c495e8c28412d2d3fa2f530fc8409f516e54ce Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 27 Feb 2020 20:00:41 +0300 Subject: [PATCH 0008/2442] Move drawables loading to AppendComments() --- .../Overlays/Comments/CommentsContainer.cs | 50 ++++++++++--------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/osu.Game/Overlays/Comments/CommentsContainer.cs b/osu.Game/Overlays/Comments/CommentsContainer.cs index 0381d73583..f7bf1e2b07 100644 --- a/osu.Game/Overlays/Comments/CommentsContainer.cs +++ b/osu.Game/Overlays/Comments/CommentsContainer.cs @@ -174,6 +174,8 @@ namespace osu.Game.Overlays.Comments protected void OnSuccess(CommentBundle response) { + CommentCounter.Current.Value = response.Total; + if (!response.Comments.Any()) { content.Add(new NoCommentsPlaceholder()); @@ -181,35 +183,14 @@ namespace osu.Game.Overlays.Comments return; } - var topLevelComments = AppendComments(response); - - LoadComponentsAsync(topLevelComments, loaded => - { - content.AddRange(loaded); - - deletedCommentsCounter.Count.Value += response.Comments.Count(c => c.IsDeleted && c.IsTopLevel); - CommentCounter.Current.Value = response.Total; - - if (response.HasMore) - { - int loadedTopLevelComments = 0; - content.Children.OfType().ForEach(p => loadedTopLevelComments++); - - moreButton.Current.Value = response.TopLevelCount - loadedTopLevelComments; - moreButton.IsLoading = false; - } - else - { - moreButton.Hide(); - } - }, (loadCancellation = new CancellationTokenSource()).Token); + AppendComments(response); } /// /// Appends retrieved comments to the subtree rooted of comments in this page. /// /// The bundle of comments to add. - protected List AppendComments([NotNull] CommentBundle bundle) + protected void AppendComments([NotNull] CommentBundle bundle) { var topLevelComments = new List(); var orphaned = new List(); @@ -227,7 +208,28 @@ namespace osu.Game.Overlays.Comments foreach (var o in orphaned) addNewComment(o); - return topLevelComments; + if (topLevelComments.Any()) + { + LoadComponentsAsync(topLevelComments, loaded => + { + content.AddRange(loaded); + + deletedCommentsCounter.Count.Value += topLevelComments.Select(d => d.Comment).Count(c => c.IsDeleted && c.IsTopLevel); + + if (bundle.HasMore) + { + int loadedTopLevelComments = 0; + content.Children.OfType().ForEach(p => loadedTopLevelComments++); + + moreButton.Current.Value = bundle.TopLevelCount - loadedTopLevelComments; + moreButton.IsLoading = false; + } + else + { + moreButton.Hide(); + } + }, (loadCancellation = new CancellationTokenSource()).Token); + } void addNewComment(Comment comment) { From 62e676feb5e6ca8a4d352f96c51ffd4cc8c01446 Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Thu, 27 Feb 2020 22:18:02 +0100 Subject: [PATCH 0009/2442] Restyle SpotlightsDropdown to match osu-web --- .../Overlays/Rankings/SpotlightSelector.cs | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Rankings/SpotlightSelector.cs b/osu.Game/Overlays/Rankings/SpotlightSelector.cs index f019b50ae8..13640c7fe7 100644 --- a/osu.Game/Overlays/Rankings/SpotlightSelector.cs +++ b/osu.Game/Overlays/Rankings/SpotlightSelector.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; @@ -165,8 +165,28 @@ namespace osu.Game.Overlays.Rankings [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) { + // osu-web adds a 0.6 opacity container on top of the 0.5 base one when hovering, 0.8 on a single container here matches the resulting colour + AccentColour = colourProvider.Background6.Opacity(0.8f); menu.BackgroundColour = colourProvider.Background5; - AccentColour = colourProvider.Background6; + Padding = new MarginPadding { Vertical = 20 }; + } + + private class SpotlightsDropdownHeader : OsuDropdownHeader + { + public SpotlightsDropdownHeader() : base() + { + Height = 48; + Text.Font = OsuFont.GetFont(size: 15); + Foreground.Padding = new MarginPadding { Horizontal = 10, Vertical = 15 }; + Margin = Icon.Margin = new MarginPadding(0); + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + BackgroundColour = colourProvider.Background6.Opacity(0.5f); + BackgroundColourHover = colourProvider.Background5; + } } } } From 82cbd35e30940df06e6197bda5dbfba12eb47a62 Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Thu, 27 Feb 2020 22:22:22 +0100 Subject: [PATCH 0010/2442] Make CountryName use LinkFlowContainer for consistency --- .../Rankings/Tables/CountriesTable.cs | 28 +++++-------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs index 0b9a48ce0e..5306000d6a 100644 --- a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs @@ -5,11 +5,10 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using System; using osu.Game.Users; -using osu.Game.Graphics.Sprites; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using System.Collections.Generic; using osu.Framework.Allocation; -using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Rankings.Tables { @@ -62,35 +61,22 @@ namespace osu.Game.Overlays.Rankings.Tables } }; - private class CountryName : OsuHoverContainer + private class CountryName : LinkFlowContainer { - protected override IEnumerable EffectTargets => new[] { text }; - [Resolved(canBeNull: true)] private RankingsOverlay rankings { get; set; } - private readonly OsuSpriteText text; private readonly Country country; - public CountryName(Country country) + public CountryName(Country country) : base(t => t.Font = OsuFont.GetFont(size: 12)) { this.country = country; - AutoSizeAxes = Axes.Both; - Add(text = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 12), - Text = country.FullName ?? string.Empty, - }); - } + AutoSizeAxes = Axes.X; + RelativeSizeAxes = Axes.Y; + TextAnchor = Anchor.CentreLeft; - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) - { - IdleColour = colourProvider.Light2; - HoverColour = colourProvider.Content2; - - Action = () => rankings?.ShowCountry(country); + AddLink(country.FullName ?? string.Empty, () => rankings?.ShowCountry(country)); } } } From f03ada65ddec5c5b38f125bf49b776a0c80a5f0a Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Thu, 27 Feb 2020 22:23:50 +0100 Subject: [PATCH 0011/2442] Adjust grade columns spacing --- .../Overlays/Rankings/Tables/RankingsTable.cs | 10 +++++++--- .../Overlays/Rankings/Tables/UserBasedTable.cs | 18 ++++++++++-------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 943897581e..946bd82cf8 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; @@ -96,19 +96,23 @@ namespace osu.Game.Overlays.Rankings.Tables } }; + protected virtual IEnumerable GradeColumns() => new List(); + protected virtual string HighlightedColumn() => @"Performance"; private class HeaderText : OsuSpriteText { private readonly string highlighted; - public HeaderText(string text, string highlighted) + public HeaderText(string text, string highlighted, IEnumerable gradeColumns) { this.highlighted = highlighted; Text = text; Font = OsuFont.GetFont(size: 12); - Margin = new MarginPadding { Horizontal = 10 }; + + var isGrade = gradeColumns.Contains(text); + Margin = new MarginPadding { Vertical = 5, Horizontal = isGrade ? 20 : 10 }; } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs index cad7364103..8eecffd738 100644 --- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; @@ -19,16 +19,18 @@ namespace osu.Game.Overlays.Rankings.Tables { } - protected override TableColumn[] CreateAdditionalHeaders() => new[] + protected override IEnumerable GradeColumns() => new List() { "SS", "S", "A" }; + + protected override TableColumn[] CreateAdditionalHeaders() { + var gradeColumns = GradeColumns().Select(grade => new TableColumn(grade, Anchor.Centre, new Dimension(GridSizeMode.AutoSize))); + + return new[] + { new TableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - }.Concat(CreateUniqueHeaders()).Concat(new[] - { - new TableColumn("SS", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("S", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("A", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - }).ToArray(); + }.Concat(CreateUniqueHeaders()).Concat(gradeColumns).ToArray(); + } protected sealed override Country GetCountry(UserStatistics item) => item.User.Country; From 9e4a6a9cf3cf309f7c88e4791e438b605d3116f5 Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Thu, 27 Feb 2020 22:25:49 +0100 Subject: [PATCH 0012/2442] Add spacing between RankingsTable rows to match osu-web --- .../Overlays/Rankings/Tables/RankingsTable.cs | 23 +++++++++++-------- .../Rankings/Tables/TableRowBackground.cs | 7 +++--- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 946bd82cf8..e7da5c01fb 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; @@ -20,7 +20,8 @@ namespace osu.Game.Overlays.Rankings.Tables { protected const int TEXT_SIZE = 12; private const float horizontal_inset = 20; - private const float row_height = 25; + private const float row_height = 32; + private const float row_spacing = 3; private const int items_per_page = 50; private readonly int page; @@ -35,7 +36,7 @@ namespace osu.Game.Overlays.Rankings.Tables AutoSizeAxes = Axes.Y; Padding = new MarginPadding { Horizontal = horizontal_inset }; - RowSize = new Dimension(GridSizeMode.Absolute, row_height); + RowSize = new Dimension(GridSizeMode.Absolute, row_height + row_spacing); } [BackgroundDependencyLoader] @@ -47,10 +48,11 @@ namespace osu.Game.Overlays.Rankings.Tables { RelativeSizeAxes = Axes.Both, Depth = 1f, - Margin = new MarginPadding { Top = row_height } + Margin = new MarginPadding { Top = row_height + row_spacing }, + Spacing = new Vector2(0, row_spacing), }); - rankings.ForEach(_ => backgroundFlow.Add(new TableRowBackground())); + rankings.ForEach(_ => backgroundFlow.Add(new TableRowBackground(row_height))); Columns = mainHeaders.Concat(CreateAdditionalHeaders()).ToArray(); Content = rankings.Select((s, i) => createContent((page - 1) * items_per_page + i, s)).ToArray().ToRectangular(); @@ -68,13 +70,13 @@ namespace osu.Game.Overlays.Rankings.Tables protected abstract Drawable[] CreateAdditionalContent(TModel item); - protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? string.Empty, HighlightedColumn()); + protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? string.Empty, HighlightedColumn(), GradeColumns()); protected abstract Country GetCountry(TModel item); protected abstract Drawable CreateFlagContent(TModel item); - private OsuSpriteText createIndexDrawable(int index) => new OsuSpriteText + private OsuSpriteText createIndexDrawable(int index) => new RowText { Text = $"#{index + 1}", Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.SemiBold) @@ -84,12 +86,13 @@ namespace osu.Game.Overlays.Rankings.Tables { AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - Spacing = new Vector2(7, 0), + Spacing = new Vector2(10, 0), + Margin = new MarginPadding { Bottom = row_spacing }, Children = new[] { new UpdateableFlag(GetCountry(item)) { - Size = new Vector2(20, 13), + Size = new Vector2(30, 20), ShowPlaceholderOnNull = false, }, CreateFlagContent(item) @@ -128,7 +131,7 @@ namespace osu.Game.Overlays.Rankings.Tables public RowText() { Font = OsuFont.GetFont(size: TEXT_SIZE); - Margin = new MarginPadding { Horizontal = 10 }; + Margin = new MarginPadding { Horizontal = 10, Bottom = row_spacing }; } } diff --git a/osu.Game/Overlays/Rankings/Tables/TableRowBackground.cs b/osu.Game/Overlays/Rankings/Tables/TableRowBackground.cs index fe87a8b3d4..d5e2f12172 100644 --- a/osu.Game/Overlays/Rankings/Tables/TableRowBackground.cs +++ b/osu.Game/Overlays/Rankings/Tables/TableRowBackground.cs @@ -19,13 +19,14 @@ namespace osu.Game.Overlays.Rankings.Tables private Color4 idleColour; private Color4 hoverColour; - public TableRowBackground() + public TableRowBackground(float height) { RelativeSizeAxes = Axes.X; - Height = 25; + Height = height; - CornerRadius = 3; + CornerRadius = 4; Masking = true; + MaskingSmoothness = 0.5f; InternalChild = background = new Box { From 1d6746102f682469d9528a851d8d804e1f2b3e09 Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Thu, 27 Feb 2020 22:26:21 +0100 Subject: [PATCH 0013/2442] Adjust user links --- osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs index 8eecffd738..45a2c20000 100644 --- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; @@ -27,8 +27,8 @@ namespace osu.Game.Overlays.Rankings.Tables return new[] { - new TableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), }.Concat(CreateUniqueHeaders()).Concat(gradeColumns).ToArray(); } @@ -36,7 +36,12 @@ namespace osu.Game.Overlays.Rankings.Tables protected sealed override Drawable CreateFlagContent(UserStatistics item) { - var username = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: TEXT_SIZE, italics: true)) { AutoSizeAxes = Axes.Both }; + var username = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: TEXT_SIZE, italics: true)) + { + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + TextAnchor = Anchor.CentreLeft + }; username.AddUserLink(item.User); return username; } From b65f5031a2e1a9ddc9c73ee0acea921a65cb7461 Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Thu, 27 Feb 2020 22:26:45 +0100 Subject: [PATCH 0014/2442] Adjust spacing in SpotlightSelector --- osu.Game/Overlays/Rankings/SpotlightSelector.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Rankings/SpotlightSelector.cs b/osu.Game/Overlays/Rankings/SpotlightSelector.cs index 13640c7fe7..21e9881327 100644 --- a/osu.Game/Overlays/Rankings/SpotlightSelector.cs +++ b/osu.Game/Overlays/Rankings/SpotlightSelector.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; @@ -15,6 +15,7 @@ using System; using System.Collections.Generic; using osu.Framework.Graphics.UserInterface; using osu.Game.Online.API.Requests; +using osu.Framework.Extensions.Color4Extensions; namespace osu.Game.Overlays.Rankings { @@ -50,7 +51,7 @@ namespace osu.Game.Overlays.Rankings public SpotlightSelector() { RelativeSizeAxes = Axes.X; - Height = 100; + Height = 155; Add(content = new Container { RelativeSizeAxes = Axes.Both, @@ -63,7 +64,7 @@ namespace osu.Game.Overlays.Rankings new Container { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Vertical = 10 }, + Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN }, Children = new Drawable[] { dropdown = new SpotlightsDropdown @@ -128,6 +129,7 @@ namespace osu.Game.Overlays.Rankings { AutoSizeAxes = Axes.Both; Direction = FillDirection.Vertical; + Padding = new MarginPadding { Vertical = 15 }; Children = new Drawable[] { new OsuSpriteText @@ -138,12 +140,12 @@ namespace osu.Game.Overlays.Rankings new Container { AutoSizeAxes = Axes.X, - Height = 20, + Height = 25, Child = valueText = new OsuSpriteText { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Font = OsuFont.GetFont(size: 18, weight: FontWeight.Light), + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Font = OsuFont.GetFont(size: 20, weight: FontWeight.Light), } } }; From 92da7132cd616a6c3d7dbd5901f6fdfa351748be Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Thu, 27 Feb 2020 22:35:02 +0100 Subject: [PATCH 0015/2442] Fix code quality issues --- osu.Game/Overlays/Rankings/SpotlightSelector.cs | 2 +- osu.Game/Overlays/Rankings/Tables/CountriesTable.cs | 7 ++----- osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Rankings/SpotlightSelector.cs b/osu.Game/Overlays/Rankings/SpotlightSelector.cs index 21e9881327..37e2c1c38b 100644 --- a/osu.Game/Overlays/Rankings/SpotlightSelector.cs +++ b/osu.Game/Overlays/Rankings/SpotlightSelector.cs @@ -175,7 +175,7 @@ namespace osu.Game.Overlays.Rankings private class SpotlightsDropdownHeader : OsuDropdownHeader { - public SpotlightsDropdownHeader() : base() + public SpotlightsDropdownHeader() { Height = 48; Text.Font = OsuFont.GetFont(size: 15); diff --git a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs index 5306000d6a..c43ecf9be5 100644 --- a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs @@ -66,12 +66,9 @@ namespace osu.Game.Overlays.Rankings.Tables [Resolved(canBeNull: true)] private RankingsOverlay rankings { get; set; } - private readonly Country country; - - public CountryName(Country country) : base(t => t.Font = OsuFont.GetFont(size: 12)) + public CountryName(Country country) + : base(t => t.Font = OsuFont.GetFont(size: 12)) { - this.country = country; - AutoSizeAxes = Axes.X; RelativeSizeAxes = Axes.Y; TextAnchor = Anchor.CentreLeft; diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs index 45a2c20000..8ce31c8539 100644 --- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Rankings.Tables { } - protected override IEnumerable GradeColumns() => new List() { "SS", "S", "A" }; + protected override IEnumerable GradeColumns() => new List { "SS", "S", "A" }; protected override TableColumn[] CreateAdditionalHeaders() { From 6d09f1eea41d3cb3a01d19dcfc0e8f5daa2f2967 Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Fri, 28 Feb 2020 13:18:40 +0100 Subject: [PATCH 0016/2442] Hook up SpotlightsDropdownHeader --- osu.Game/Overlays/Rankings/SpotlightSelector.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Rankings/SpotlightSelector.cs b/osu.Game/Overlays/Rankings/SpotlightSelector.cs index 37e2c1c38b..408070f4c7 100644 --- a/osu.Game/Overlays/Rankings/SpotlightSelector.cs +++ b/osu.Game/Overlays/Rankings/SpotlightSelector.cs @@ -164,6 +164,8 @@ namespace osu.Game.Overlays.Rankings protected override DropdownMenu CreateMenu() => menu = base.CreateMenu().With(m => m.MaxHeight = 400); + protected override DropdownHeader CreateHeader() => new SpotlightsDropdownHeader(); + [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) { From 0760ccf0245afcdf60a87512dcec454a1dd85414 Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Sat, 29 Feb 2020 15:14:25 +0100 Subject: [PATCH 0017/2442] Adjust CountryFilter spacing to match web --- osu.Game/Overlays/Rankings/CountryFilter.cs | 4 ++-- osu.Game/Overlays/Rankings/CountryPill.cs | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Rankings/CountryFilter.cs b/osu.Game/Overlays/Rankings/CountryFilter.cs index 4bdefb06ef..9950f36141 100644 --- a/osu.Game/Overlays/Rankings/CountryFilter.cs +++ b/osu.Game/Overlays/Rankings/CountryFilter.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Rankings public class CountryFilter : CompositeDrawable, IHasCurrentValue { private const int duration = 200; - private const int height = 50; + private const int height = 70; private readonly BindableWithCurrent current = new BindableWithCurrent(); diff --git a/osu.Game/Overlays/Rankings/CountryPill.cs b/osu.Game/Overlays/Rankings/CountryPill.cs index 1b19bbd95e..edd7b596d2 100644 --- a/osu.Game/Overlays/Rankings/CountryPill.cs +++ b/osu.Game/Overlays/Rankings/CountryPill.cs @@ -42,7 +42,7 @@ namespace osu.Game.Overlays.Rankings InternalChild = content = new CircularContainer { - Height = 25, + Height = 30, AutoSizeDuration = duration, AutoSizeEasing = Easing.OutQuint, Masking = true, @@ -58,9 +58,9 @@ namespace osu.Game.Overlays.Rankings Origin = Anchor.Centre, RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, - Margin = new MarginPadding { Horizontal = 10 }, + Margin = new MarginPadding { Horizontal = 15 }, Direction = FillDirection.Horizontal, - Spacing = new Vector2(8, 0), + Spacing = new Vector2(15, 0), Children = new Drawable[] { new FillFlowContainer @@ -70,14 +70,14 @@ namespace osu.Game.Overlays.Rankings Anchor = Anchor.Centre, Origin = Anchor.Centre, Direction = FillDirection.Horizontal, - Spacing = new Vector2(3, 0), + Spacing = new Vector2(5, 0), Children = new Drawable[] { flag = new UpdateableFlag { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(22, 15) + Size = new Vector2(30, 20) }, countryName = new OsuSpriteText { @@ -148,7 +148,7 @@ namespace osu.Game.Overlays.Rankings AutoSizeAxes = Axes.Both; Add(icon = new SpriteIcon { - Size = new Vector2(8), + Size = new Vector2(10), Icon = FontAwesome.Solid.Times }); } From 520bbcf2e4d4fa56090b3da2b3680a34fab5a0d8 Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Sat, 29 Feb 2020 15:19:36 +0100 Subject: [PATCH 0018/2442] Use correct x-axis margins --- osu.Game/Overlays/Rankings/CountryFilter.cs | 4 ++-- osu.Game/Overlays/Rankings/SpotlightSelector.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Rankings/CountryFilter.cs b/osu.Game/Overlays/Rankings/CountryFilter.cs index 9950f36141..ab5df80d8a 100644 --- a/osu.Game/Overlays/Rankings/CountryFilter.cs +++ b/osu.Game/Overlays/Rankings/CountryFilter.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; @@ -52,7 +52,7 @@ namespace osu.Game.Overlays.Rankings Origin = Anchor.CentreLeft, Direction = FillDirection.Horizontal, Spacing = new Vector2(10, 0), - Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN }, + Margin = new MarginPadding { Left = OverlayHeader.CONTENT_X_MARGIN }, Children = new Drawable[] { new OsuSpriteText diff --git a/osu.Game/Overlays/Rankings/SpotlightSelector.cs b/osu.Game/Overlays/Rankings/SpotlightSelector.cs index 408070f4c7..4fb0d58e57 100644 --- a/osu.Game/Overlays/Rankings/SpotlightSelector.cs +++ b/osu.Game/Overlays/Rankings/SpotlightSelector.cs @@ -64,7 +64,7 @@ namespace osu.Game.Overlays.Rankings new Container { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN }, + Padding = new MarginPadding { Horizontal = OverlayHeader.CONTENT_X_MARGIN }, Children = new Drawable[] { dropdown = new SpotlightsDropdown From e09fbcb05fac1b881593810b1bed91d1ba69f101 Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Wed, 4 Mar 2020 17:47:48 +0100 Subject: [PATCH 0019/2442] Only add link if the country name isn't null --- osu.Game/Overlays/Rankings/Tables/CountriesTable.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs index c43ecf9be5..011a8207a0 100644 --- a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs @@ -73,7 +73,8 @@ namespace osu.Game.Overlays.Rankings.Tables RelativeSizeAxes = Axes.Y; TextAnchor = Anchor.CentreLeft; - AddLink(country.FullName ?? string.Empty, () => rankings?.ShowCountry(country)); + if (country.FullName != null) + AddLink(country.FullName, () => rankings?.ShowCountry(country)); } } } From 276a90fb8495f71b29e9886b7796746d754e4007 Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Wed, 4 Mar 2020 18:19:28 +0100 Subject: [PATCH 0020/2442] Use public property instead of ctor parameter --- osu.Game/Overlays/Rankings/Tables/RankingsTable.cs | 4 ++-- osu.Game/Overlays/Rankings/Tables/TableRowBackground.cs | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index e7da5c01fb..c03a279afa 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; @@ -52,7 +52,7 @@ namespace osu.Game.Overlays.Rankings.Tables Spacing = new Vector2(0, row_spacing), }); - rankings.ForEach(_ => backgroundFlow.Add(new TableRowBackground(row_height))); + rankings.ForEach(_ => backgroundFlow.Add(new TableRowBackground { Height = row_height })); Columns = mainHeaders.Concat(CreateAdditionalHeaders()).ToArray(); Content = rankings.Select((s, i) => createContent((page - 1) * items_per_page + i, s)).ToArray().ToRectangular(); diff --git a/osu.Game/Overlays/Rankings/Tables/TableRowBackground.cs b/osu.Game/Overlays/Rankings/Tables/TableRowBackground.cs index d5e2f12172..b49fec65db 100644 --- a/osu.Game/Overlays/Rankings/Tables/TableRowBackground.cs +++ b/osu.Game/Overlays/Rankings/Tables/TableRowBackground.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; @@ -19,10 +19,9 @@ namespace osu.Game.Overlays.Rankings.Tables private Color4 idleColour; private Color4 hoverColour; - public TableRowBackground(float height) + public TableRowBackground() { RelativeSizeAxes = Axes.X; - Height = height; CornerRadius = 4; Masking = true; From 06642dc7ff26a99386b8a2c8d7d8a861cdc03bfc Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Wed, 4 Mar 2020 18:20:55 +0100 Subject: [PATCH 0021/2442] Improve HeaderText highlight and spacing logic --- .../Overlays/Rankings/Tables/RankingsTable.cs | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index c03a279afa..eb2b676500 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -70,7 +70,14 @@ namespace osu.Game.Overlays.Rankings.Tables protected abstract Drawable[] CreateAdditionalContent(TModel item); - protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? string.Empty, HighlightedColumn(), GradeColumns()); + protected override Drawable CreateHeader(int index, TableColumn column) + { + var title = column?.Header ?? string.Empty; + var isHighlighted = HighlightedColumn() == title; + var isGrade = GradeColumns().Contains(title); + + return new HeaderText(title, isHighlighted) { Margin = new MarginPadding { Vertical = 5, Horizontal = isGrade ? 20 : 10 } }; + } protected abstract Country GetCountry(TModel item); @@ -105,23 +112,20 @@ namespace osu.Game.Overlays.Rankings.Tables private class HeaderText : OsuSpriteText { - private readonly string highlighted; + private readonly bool isHighlighted; - public HeaderText(string text, string highlighted, IEnumerable gradeColumns) + public HeaderText(string text, bool isHighlighted) { - this.highlighted = highlighted; + this.isHighlighted = isHighlighted; Text = text; Font = OsuFont.GetFont(size: 12); - - var isGrade = gradeColumns.Contains(text); - Margin = new MarginPadding { Vertical = 5, Horizontal = isGrade ? 20 : 10 }; } [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) { - if (Text != highlighted) + if (isHighlighted) Colour = colourProvider.Foreground1; } } From 56f76580fdaa02a04575f4a7e1be4b5b231194fb Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Wed, 4 Mar 2020 18:22:56 +0100 Subject: [PATCH 0022/2442] Use Y-axis autosizing in SpotlightsDropdownHeader --- osu.Game/Overlays/Rankings/SpotlightSelector.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Rankings/SpotlightSelector.cs b/osu.Game/Overlays/Rankings/SpotlightSelector.cs index 4fb0d58e57..d2cb94de9a 100644 --- a/osu.Game/Overlays/Rankings/SpotlightSelector.cs +++ b/osu.Game/Overlays/Rankings/SpotlightSelector.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; @@ -179,8 +179,9 @@ namespace osu.Game.Overlays.Rankings { public SpotlightsDropdownHeader() { - Height = 48; + AutoSizeAxes = Axes.Y; Text.Font = OsuFont.GetFont(size: 15); + Text.Padding = new MarginPadding { Vertical = 1.5f }; // osu-web line-height difference compensation Foreground.Padding = new MarginPadding { Horizontal = 10, Vertical = 15 }; Margin = Icon.Margin = new MarginPadding(0); } From df328dd9817c82c776b6211e710f177091637017 Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Thu, 5 Mar 2020 23:09:51 +0100 Subject: [PATCH 0023/2442] Revert margin changes --- osu.Game/Overlays/Rankings/CountryFilter.cs | 4 ++-- osu.Game/Overlays/Rankings/SpotlightSelector.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Rankings/CountryFilter.cs b/osu.Game/Overlays/Rankings/CountryFilter.cs index ab5df80d8a..9950f36141 100644 --- a/osu.Game/Overlays/Rankings/CountryFilter.cs +++ b/osu.Game/Overlays/Rankings/CountryFilter.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; @@ -52,7 +52,7 @@ namespace osu.Game.Overlays.Rankings Origin = Anchor.CentreLeft, Direction = FillDirection.Horizontal, Spacing = new Vector2(10, 0), - Margin = new MarginPadding { Left = OverlayHeader.CONTENT_X_MARGIN }, + Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN }, Children = new Drawable[] { new OsuSpriteText diff --git a/osu.Game/Overlays/Rankings/SpotlightSelector.cs b/osu.Game/Overlays/Rankings/SpotlightSelector.cs index d2cb94de9a..a9fcac42af 100644 --- a/osu.Game/Overlays/Rankings/SpotlightSelector.cs +++ b/osu.Game/Overlays/Rankings/SpotlightSelector.cs @@ -64,7 +64,7 @@ namespace osu.Game.Overlays.Rankings new Container { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = OverlayHeader.CONTENT_X_MARGIN }, + Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN }, Children = new Drawable[] { dropdown = new SpotlightsDropdown From 1c69e4eb5b56246e91e4c89a96c9639c848e6007 Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Sun, 15 Mar 2020 18:26:33 +0100 Subject: [PATCH 0024/2442] Update SpotlightSelector design --- osu.Game/Overlays/Rankings/SpotlightSelector.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Rankings/SpotlightSelector.cs b/osu.Game/Overlays/Rankings/SpotlightSelector.cs index a9fcac42af..7299a48483 100644 --- a/osu.Game/Overlays/Rankings/SpotlightSelector.cs +++ b/osu.Game/Overlays/Rankings/SpotlightSelector.cs @@ -77,11 +77,11 @@ namespace osu.Game.Overlays.Rankings }, new FillFlowContainer { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - Spacing = new Vector2(15, 0), + Spacing = new Vector2(20, 0), Children = new Drawable[] { startDateColumn = new InfoColumn(@"Start Date"), From 280a009784707a6a60586d89fe8bf7d53a998a8d Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Sun, 15 Mar 2020 18:31:44 +0100 Subject: [PATCH 0025/2442] Fix header colours being flipped --- osu.Game/Overlays/Rankings/Tables/RankingsTable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index eb2b676500..65ef7dc6b7 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -125,7 +125,7 @@ namespace osu.Game.Overlays.Rankings.Tables [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) { - if (isHighlighted) + if (!isHighlighted) Colour = colourProvider.Foreground1; } } From f1e54d2745c62e85ba06ec01a58b237ea673e2a0 Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Sun, 15 Mar 2020 18:32:12 +0100 Subject: [PATCH 0026/2442] Apply suggestions --- osu.Game/Overlays/Rankings/Tables/CountriesTable.cs | 4 ++-- osu.Game/Overlays/Rankings/Tables/RankingsTable.cs | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs index 011a8207a0..c5e413c7fa 100644 --- a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; @@ -73,7 +73,7 @@ namespace osu.Game.Overlays.Rankings.Tables RelativeSizeAxes = Axes.Y; TextAnchor = Anchor.CentreLeft; - if (country.FullName != null) + if (!string.IsNullOrEmpty(country.FullName)) AddLink(country.FullName, () => rankings?.ShowCountry(country)); } } diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 65ef7dc6b7..17d0c9cf24 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -76,7 +76,10 @@ namespace osu.Game.Overlays.Rankings.Tables var isHighlighted = HighlightedColumn() == title; var isGrade = GradeColumns().Contains(title); - return new HeaderText(title, isHighlighted) { Margin = new MarginPadding { Vertical = 5, Horizontal = isGrade ? 20 : 10 } }; + return new HeaderText(title, isHighlighted) + { + Margin = new MarginPadding { Vertical = 5, Horizontal = isGrade ? 20 : 10 } + }; } protected abstract Country GetCountry(TModel item); From db55b98ed338b4c9c20d4c3a0ee593c7cae0db6a Mon Sep 17 00:00:00 2001 From: TheWildTree Date: Sun, 15 Mar 2020 22:03:54 +0100 Subject: [PATCH 0027/2442] Move grade column spacing logic to UserBasedTable --- .../Overlays/Rankings/Tables/RankingsTable.cs | 17 +++------- .../Overlays/Rankings/Tables/ScoresTable.cs | 2 +- .../Rankings/Tables/UserBasedTable.cs | 31 ++++++++++++++----- 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 17d0c9cf24..3fb8602cbc 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -70,16 +70,12 @@ namespace osu.Game.Overlays.Rankings.Tables protected abstract Drawable[] CreateAdditionalContent(TModel item); + protected virtual string HighlightedColumn => @"Performance"; + protected override Drawable CreateHeader(int index, TableColumn column) { var title = column?.Header ?? string.Empty; - var isHighlighted = HighlightedColumn() == title; - var isGrade = GradeColumns().Contains(title); - - return new HeaderText(title, isHighlighted) - { - Margin = new MarginPadding { Vertical = 5, Horizontal = isGrade ? 20 : 10 } - }; + return new HeaderText(title, title == HighlightedColumn); } protected abstract Country GetCountry(TModel item); @@ -109,11 +105,7 @@ namespace osu.Game.Overlays.Rankings.Tables } }; - protected virtual IEnumerable GradeColumns() => new List(); - - protected virtual string HighlightedColumn() => @"Performance"; - - private class HeaderText : OsuSpriteText + protected class HeaderText : OsuSpriteText { private readonly bool isHighlighted; @@ -123,6 +115,7 @@ namespace osu.Game.Overlays.Rankings.Tables Text = text; Font = OsuFont.GetFont(size: 12); + Margin = new MarginPadding { Vertical = 5, Horizontal = 10 }; } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs index 370ee506c2..9fae8e1897 100644 --- a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs @@ -33,6 +33,6 @@ namespace osu.Game.Overlays.Rankings.Tables } }; - protected override string HighlightedColumn() => @"Ranked Score"; + protected override string HighlightedColumn => @"Ranked Score"; } } diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs index 8ce31c8539..a6969f483f 100644 --- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -19,17 +19,20 @@ namespace osu.Game.Overlays.Rankings.Tables { } - protected override IEnumerable GradeColumns() => new List { "SS", "S", "A" }; + protected virtual IEnumerable GradeColumns => new List { "SS", "S", "A" }; - protected override TableColumn[] CreateAdditionalHeaders() - { - var gradeColumns = GradeColumns().Select(grade => new TableColumn(grade, Anchor.Centre, new Dimension(GridSizeMode.AutoSize))); - - return new[] + protected override TableColumn[] CreateAdditionalHeaders() => new[] { new TableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - }.Concat(CreateUniqueHeaders()).Concat(gradeColumns).ToArray(); + }.Concat(CreateUniqueHeaders()) + .Concat(GradeColumns.Select(grade => new TableColumn(grade, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)))) + .ToArray(); + + protected override Drawable CreateHeader(int index, TableColumn column) + { + var title = column?.Header ?? string.Empty; + return new UserTableHeaderText(title, HighlightedColumn == title, GradeColumns.Contains(title)); } protected sealed override Country GetCountry(UserStatistics item) => item.User.Country; @@ -60,5 +63,19 @@ namespace osu.Game.Overlays.Rankings.Tables protected abstract TableColumn[] CreateUniqueHeaders(); protected abstract Drawable[] CreateUniqueContent(UserStatistics item); + + private class UserTableHeaderText : HeaderText + { + public UserTableHeaderText(string text, bool isHighlighted, bool isGrade) + : base(text, isHighlighted) + { + Margin = new MarginPadding + { + // Grade columns have extra horizontal padding for readibility + Horizontal = isGrade ? 20 : 10, + Vertical = 5 + }; + } + } } } From 560a0174dff62173c2288a390a0324b9ddedfaf2 Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Fri, 18 Dec 2020 17:49:17 +0800 Subject: [PATCH 0028/2442] Make auto restart toggleable --- osu.Game/Rulesets/Mods/ModSuddenDeath.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs index ae71041a64..466fdccfb7 100644 --- a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs +++ b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs @@ -2,7 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; +using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; @@ -20,9 +22,11 @@ namespace osu.Game.Rulesets.Mods public override bool Ranked => true; public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax), typeof(ModAutoplay) }; + [SettingSource("Restart on fail", "Automatically restarts when failed.")] + public BindableBool Restart { get; } = new BindableBool(); public bool PerformFail() => true; - public bool RestartOnFail => true; + public bool RestartOnFail => Restart.Value; public void ApplyToHealthProcessor(HealthProcessor healthProcessor) { From 2babb7ecb0b122555a339809ce158b748f0e3295 Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Fri, 18 Dec 2020 18:33:38 +0800 Subject: [PATCH 0029/2442] Fix CI --- osu.Game/Rulesets/Mods/ModSuddenDeath.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs index 466fdccfb7..925844275b 100644 --- a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs +++ b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs @@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Mods [SettingSource("Restart on fail", "Automatically restarts when failed.")] public BindableBool Restart { get; } = new BindableBool(); + public bool PerformFail() => true; public bool RestartOnFail => Restart.Value; From f95744170f008e5aecf912a4a380e6191296089b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 07:06:26 +0300 Subject: [PATCH 0030/2442] Add skin config lookup for combo colours --- osu.Game/Skinning/SkinComboColourLookup.cs | 26 +++++++++++++++++++++ osu.Game/Skinning/SkinProvidingContainer.cs | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Skinning/SkinComboColourLookup.cs diff --git a/osu.Game/Skinning/SkinComboColourLookup.cs b/osu.Game/Skinning/SkinComboColourLookup.cs new file mode 100644 index 0000000000..33e35a96fb --- /dev/null +++ b/osu.Game/Skinning/SkinComboColourLookup.cs @@ -0,0 +1,26 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Skinning +{ + public class SkinComboColourLookup + { + /// + /// The index to use for deciding the combo colour. + /// + public readonly int ColourIndex; + + /// + /// The combo information requesting the colour. + /// + public readonly IHasComboInformation Combo; + + public SkinComboColourLookup(int colourIndex, IHasComboInformation combo) + { + ColourIndex = colourIndex; + Combo = combo; + } + } +} diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index cf22b2e820..a7fc1306c9 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -72,7 +72,7 @@ namespace osu.Game.Skinning { if (skin != null) { - if (lookup is GlobalSkinColours || lookup is SkinCustomColourLookup) + if (lookup is GlobalSkinColours || lookup is SkinComboColourLookup || lookup is SkinCustomColourLookup) return lookupWithFallback(lookup, AllowColourLookup); return lookupWithFallback(lookup, AllowConfigurationLookup); From 9be8d3f0d21b77a63110ea0335de65a3ce0dfc06 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 07:12:17 +0300 Subject: [PATCH 0031/2442] Add overridable combo colour retrieval method and handle in legacy skin --- osu.Game/Skinning/LegacySkin.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index eae3b69233..e827dca232 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -16,6 +16,7 @@ using osu.Framework.IO.Stores; using osu.Game.Audio; using osu.Game.Beatmaps.Formats; using osu.Game.IO; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play.HUD; using osuTK.Graphics; @@ -119,6 +120,9 @@ namespace osu.Game.Skinning break; + case SkinComboColourLookup comboColour: + return SkinUtils.As(GetComboColour(Configuration, comboColour.ColourIndex, comboColour.Combo)); + case SkinCustomColourLookup customColour: return SkinUtils.As(getCustomColour(Configuration, customColour.Lookup.ToString())); @@ -276,6 +280,18 @@ namespace osu.Game.Skinning return null; } + /// + /// Retrieves the correct combo colour for a given colour index and information on the combo. + /// + /// The source to retrieve the combo colours from. + /// The preferred index for retrieving the combo colour with. + /// Information on the combo whose using the returned colour. + protected virtual IBindable GetComboColour(IHasComboColours source, int colourIndex, IHasComboInformation combo) + { + var colour = source.ComboColours?[colourIndex % source.ComboColours.Count]; + return colour.HasValue ? new Bindable(colour.Value) : null; + } + private IBindable getCustomColour(IHasCustomColours source, string lookup) => source.CustomColours.TryGetValue(lookup, out var col) ? new Bindable(col) : null; From 78794935b47f95648ac620378ddfe11108933e59 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 07:11:45 +0300 Subject: [PATCH 0032/2442] Handle combo colour lookups in other skins --- .../Gameplay/TestSceneHitObjectAccentColour.cs | 10 ++-------- osu.Game/Skinning/DefaultSkin.cs | 11 +++++++++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs index 883791c35c..d08d08390b 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs @@ -127,14 +127,8 @@ namespace osu.Game.Tests.Gameplay { switch (lookup) { - case GlobalSkinColours global: - switch (global) - { - case GlobalSkinColours.ComboColours: - return SkinUtils.As(new Bindable>(ComboColours)); - } - - break; + case SkinComboColourLookup comboColour: + return SkinUtils.As(new Bindable(ComboColours[comboColour.ColourIndex % ComboColours.Count])); } throw new NotImplementedException(); diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 0b3f5f3cde..a2d05a918c 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; using osu.Game.Audio; +using osu.Game.Beatmaps.Formats; using osuTK.Graphics; namespace osu.Game.Skinning @@ -28,10 +29,10 @@ namespace osu.Game.Skinning public override IBindable GetConfig(TLookup lookup) { + // todo: this code is pulled from LegacySkin and should not exist. + // will likely change based on how databased storage of skin configuration goes. switch (lookup) { - // todo: this code is pulled from LegacySkin and should not exist. - // will likely change based on how databased storage of skin configuration goes. case GlobalSkinColours global: switch (global) { @@ -40,9 +41,15 @@ namespace osu.Game.Skinning } break; + + case SkinComboColourLookup comboColour: + return SkinUtils.As(new Bindable(getComboColour(Configuration, comboColour.ColourIndex))); } return null; } + + private static Color4 getComboColour(IHasComboColours source, int colourIndex) + => source.ComboColours[colourIndex % source.ComboColours.Count]; } } From eeeb001d62279df9a5430b4345844dccc042acfd Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 07:17:27 +0300 Subject: [PATCH 0033/2442] Refactor combo colour retrieval logic to request skin lookups instead --- osu.Game.Rulesets.Catch/Objects/Banana.cs | 3 ++- .../Objects/PalpableCatchHitObject.cs | 4 ++-- .../Objects/Drawables/DrawableHitObject.cs | 3 +-- .../Objects/Types/IHasComboInformation.cs | 23 +++++++++++++------ .../Timeline/TimelineHitObjectBlueprint.cs | 4 +--- 5 files changed, 22 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Banana.cs b/osu.Game.Rulesets.Catch/Objects/Banana.cs index 178306b3bc..e5a36d08db 100644 --- a/osu.Game.Rulesets.Catch/Objects/Banana.cs +++ b/osu.Game.Rulesets.Catch/Objects/Banana.cs @@ -9,6 +9,7 @@ using osu.Game.Audio; using osu.Game.Rulesets.Catch.Judgements; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Skinning; using osu.Game.Utils; using osuTK.Graphics; @@ -31,7 +32,7 @@ namespace osu.Game.Rulesets.Catch.Objects } // override any external colour changes with banananana - Color4 IHasComboInformation.GetComboColour(IReadOnlyList comboColours) => getBananaColour(); + Color4 IHasComboInformation.GetComboColour(ISkin skin) => getBananaColour(); private Color4 getBananaColour() { diff --git a/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs index 0cd3af01df..24c12343e5 100644 --- a/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs @@ -1,9 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; using osu.Framework.Bindables; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Skinning; using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects @@ -43,6 +43,6 @@ namespace osu.Game.Rulesets.Catch.Objects } } - Color4 IHasComboInformation.GetComboColour(IReadOnlyList comboColours) => comboColours[(IndexInBeatmap + 1) % comboColours.Count]; + Color4 IHasComboInformation.GetComboColour(ISkin skin) => IHasComboInformation.GetSkinComboColour(this, skin, IndexInBeatmap + 1); } } diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 86c733c392..63ae2e286f 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -509,8 +509,7 @@ namespace osu.Game.Rulesets.Objects.Drawables { if (!(HitObject is IHasComboInformation combo)) return; - var comboColours = CurrentSkin.GetConfig>(GlobalSkinColours.ComboColours)?.Value ?? Array.Empty(); - AccentColour.Value = combo.GetComboColour(comboColours); + AccentColour.Value = combo.GetComboColour(CurrentSkin); } /// diff --git a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs index 4f66802079..03e6f76cca 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs @@ -1,9 +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.Collections.Generic; -using JetBrains.Annotations; using osu.Framework.Bindables; +using osu.Game.Skinning; using osuTK.Graphics; namespace osu.Game.Rulesets.Objects.Types @@ -40,11 +39,21 @@ namespace osu.Game.Rulesets.Objects.Types bool LastInCombo { get; set; } /// - /// Retrieves the colour of the combo described by this object from a set of possible combo colours. - /// Defaults to using to decide the colour. + /// Retrieves the colour of the combo described by this object. /// - /// A list of possible combo colours provided by the beatmap or skin. - /// The colour of the combo described by this object. - Color4 GetComboColour([NotNull] IReadOnlyList comboColours) => comboColours.Count > 0 ? comboColours[ComboIndex % comboColours.Count] : Color4.White; + /// The skin to retrieve the combo colour from, if wanted. + Color4 GetComboColour(ISkin skin) => GetSkinComboColour(this, skin, ComboIndex); + + /// + /// Retrieves the colour of the combo described by a given object from a given skin. + /// + /// The combo information, should be this. + /// The skin to retrieve the combo colour from. + /// The index to retrieve the combo colour with. + /// + protected static Color4 GetSkinComboColour(IHasComboInformation combo, ISkin skin, int comboIndex) + { + return skin.GetConfig(new SkinComboColourLookup(comboIndex, combo))?.Value ?? Color4.White; + } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index dbe689be2f..67a28c4fa0 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -139,8 +138,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline if (!(Item is IHasComboInformation combo)) return; - var comboColours = skin.GetConfig>(GlobalSkinColours.ComboColours)?.Value ?? Array.Empty(); - var comboColour = combo.GetComboColour(comboColours); + var comboColour = combo.GetComboColour(skin); if (IsSelected) { From cd6d070b4aacc33a880562c928001fdc90dee920 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 07:27:57 +0300 Subject: [PATCH 0034/2442] Consider "combo offsets" as legacy logic and separate from combo information --- .../Beatmaps/CatchBeatmapConverter.cs | 3 --- .../Objects/CatchHitObject.cs | 2 -- .../Beatmaps/OsuBeatmapConverter.cs | 6 ++--- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 14 ++++------- .../TestSceneHitObjectAccentColour.cs | 1 - osu.Game/Beatmaps/BeatmapProcessor.cs | 12 +++++++++- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 5 ++-- .../Objects/Legacy/Catch/ConvertHit.cs | 2 -- .../Legacy/Catch/ConvertHitObjectParser.cs | 14 ----------- .../Objects/Legacy/Catch/ConvertSlider.cs | 2 -- .../Objects/Legacy/Catch/ConvertSpinner.cs | 2 -- .../Rulesets/Objects/Legacy/Osu/ConvertHit.cs | 6 +++-- .../Legacy/Osu/ConvertHitObjectParser.cs | 4 ++-- .../Objects/Legacy/Osu/ConvertSlider.cs | 6 +++-- .../Objects/Legacy/Osu/ConvertSpinner.cs | 2 -- osu.Game/Rulesets/Objects/Types/IHasCombo.cs | 5 ---- .../Types/IHasLegacyBeatmapComboOffset.cs | 24 +++++++++++++++++++ 17 files changed, 56 insertions(+), 54 deletions(-) create mode 100644 osu.Game/Rulesets/Objects/Types/IHasLegacyBeatmapComboOffset.cs diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs index 34964fc4ae..8b0213bfeb 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs @@ -38,7 +38,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps RepeatCount = curveData.RepeatCount, X = positionData?.X ?? 0, NewCombo = comboData?.NewCombo ?? false, - ComboOffset = comboData?.ComboOffset ?? 0, LegacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0 }.Yield(); @@ -49,7 +48,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps Samples = obj.Samples, Duration = endTime.Duration, NewCombo = comboData?.NewCombo ?? false, - ComboOffset = comboData?.ComboOffset ?? 0, }.Yield(); default: @@ -58,7 +56,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps StartTime = obj.StartTime, Samples = obj.Samples, NewCombo = comboData?.NewCombo ?? false, - ComboOffset = comboData?.ComboOffset ?? 0, X = positionData?.X ?? 0 }.Yield(); } diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index ae45182960..ce2da314ca 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -67,8 +67,6 @@ namespace osu.Game.Rulesets.Catch.Objects public virtual bool NewCombo { get; set; } - public int ComboOffset { get; set; } - public Bindable IndexInCurrentComboBindable { get; } = new Bindable(); public int IndexInCurrentCombo diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs index a2fc4848af..d812f86938 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps RepeatCount = curveData.RepeatCount, Position = positionData?.Position ?? Vector2.Zero, NewCombo = comboData?.NewCombo ?? false, - ComboOffset = comboData?.ComboOffset ?? 0, + LegacyBeatmapComboOffset = (original as IHasLegacyBeatmapComboOffset)?.LegacyBeatmapComboOffset ?? 0, LegacyLastTickOffset = (original as IHasLegacyLastTickOffset)?.LegacyLastTickOffset, // prior to v8, speed multipliers don't adjust for how many ticks are generated over the same distance. // this results in more (or less) ticks being generated in /// The radius of hit objects (ie. the radius of a ). @@ -73,14 +73,6 @@ namespace osu.Game.Rulesets.Osu.Objects public virtual bool NewCombo { get; set; } - public readonly Bindable ComboOffsetBindable = new Bindable(); - - public int ComboOffset - { - get => ComboOffsetBindable.Value; - set => ComboOffsetBindable.Value = value; - } - public Bindable IndexInCurrentComboBindable { get; } = new Bindable(); public virtual int IndexInCurrentCombo @@ -105,6 +97,10 @@ namespace osu.Game.Rulesets.Osu.Objects set => LastInComboBindable.Value = value; } + public int LegacyBeatmapComboOffset { get; set; } + + public int LegacyBeatmapComboIndex { get; set; } + protected OsuHitObject() { StackHeightBindable.BindValueChanged(height => diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs index d08d08390b..99a681acf8 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs @@ -82,7 +82,6 @@ namespace osu.Game.Tests.Gameplay private class TestHitObjectWithCombo : ConvertHitObject, IHasComboInformation { public bool NewCombo { get; set; } - public int ComboOffset => 0; public Bindable IndexInCurrentComboBindable { get; } = new Bindable(); diff --git a/osu.Game/Beatmaps/BeatmapProcessor.cs b/osu.Game/Beatmaps/BeatmapProcessor.cs index b7b5adc52e..ac2d7d0fc1 100644 --- a/osu.Game/Beatmaps/BeatmapProcessor.cs +++ b/osu.Game/Beatmaps/BeatmapProcessor.cs @@ -37,7 +37,7 @@ namespace osu.Game.Beatmaps if (obj.NewCombo) { obj.IndexInCurrentCombo = 0; - obj.ComboIndex = (lastObj?.ComboIndex ?? 0) + obj.ComboOffset + 1; + obj.ComboIndex = (lastObj?.ComboIndex ?? 0) + 1; if (lastObj != null) lastObj.LastInCombo = true; @@ -48,6 +48,16 @@ namespace osu.Game.Beatmaps obj.ComboIndex = lastObj.ComboIndex; } + if (obj is IHasLegacyBeatmapComboOffset legacyObj) + { + var lastLegacyObj = (IHasLegacyBeatmapComboOffset)lastObj; + + if (obj.NewCombo) + legacyObj.LegacyBeatmapComboIndex = (lastLegacyObj?.LegacyBeatmapComboIndex ?? 0) + legacyObj.LegacyBeatmapComboOffset + 1; + else if (lastLegacyObj != null) + legacyObj.LegacyBeatmapComboIndex = lastLegacyObj.LegacyBeatmapComboIndex; + } + lastObj = obj; } } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index acbf57d25f..e494f6c95c 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -292,10 +292,11 @@ namespace osu.Game.Beatmaps.Formats if (hitObject is IHasCombo combo) { - type = (LegacyHitObjectType)(combo.ComboOffset << 4); - if (combo.NewCombo) type |= LegacyHitObjectType.NewCombo; + + if (hitObject is IHasLegacyBeatmapComboOffset comboOffset) + type |= (LegacyHitObjectType)(comboOffset.LegacyBeatmapComboOffset << 4); } switch (hitObject) diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs index 19722fb796..f1a03a03e2 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs @@ -13,7 +13,5 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch public float X { get; set; } public bool NewCombo { get; set; } - - public int ComboOffset { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs index c10c8dc30f..d71ddfd05e 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs @@ -18,21 +18,16 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch } private bool forceNewCombo; - private int extraComboOffset; protected override HitObject CreateHit(Vector2 position, bool newCombo, int comboOffset) { newCombo |= forceNewCombo; - comboOffset += extraComboOffset; - forceNewCombo = false; - extraComboOffset = 0; return new ConvertHit { X = position.X, NewCombo = newCombo, - ComboOffset = comboOffset }; } @@ -40,16 +35,12 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch List> nodeSamples) { newCombo |= forceNewCombo; - comboOffset += extraComboOffset; - forceNewCombo = false; - extraComboOffset = 0; return new ConvertSlider { X = position.X, NewCombo = FirstObject || newCombo, - ComboOffset = comboOffset, Path = new SliderPath(controlPoints, length), NodeSamples = nodeSamples, RepeatCount = repeatCount @@ -58,11 +49,6 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double duration) { - // Convert spinners don't create the new combo themselves, but force the next non-spinner hitobject to create a new combo - // Their combo offset is still added to that next hitobject's combo index - forceNewCombo |= FormatVersion <= 8 || newCombo; - extraComboOffset += comboOffset; - return new ConvertSpinner { Duration = duration diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs index 56790629b4..8b8bc2f4e1 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs @@ -13,7 +13,5 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch public float X { get; set; } public bool NewCombo { get; set; } - - public int ComboOffset { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs index 014494ec54..3355bb3ee5 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs @@ -17,7 +17,5 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch public float X => 256; // Required for CatchBeatmapConverter public bool NewCombo { get; set; } - - public int ComboOffset { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs index 069366bad3..03b103b8fe 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu /// /// Legacy osu! Hit-type, used for parsing Beatmaps. /// - internal sealed class ConvertHit : ConvertHitObject, IHasPosition, IHasCombo + internal sealed class ConvertHit : ConvertHitObject, IHasPosition, IHasCombo, IHasLegacyBeatmapComboOffset { public Vector2 Position { get; set; } @@ -19,6 +19,8 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu public bool NewCombo { get; set; } - public int ComboOffset { get; set; } + public int LegacyBeatmapComboOffset { get; set; } + + int IHasLegacyBeatmapComboOffset.LegacyBeatmapComboIndex { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs index 75ecab0b8f..451b714266 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu { Position = position, NewCombo = FirstObject || newCombo, - ComboOffset = comboOffset + LegacyBeatmapComboOffset = comboOffset }; } @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu { Position = position, NewCombo = FirstObject || newCombo, - ComboOffset = comboOffset, + LegacyBeatmapComboOffset = comboOffset, Path = new SliderPath(controlPoints, length), NodeSamples = nodeSamples, RepeatCount = repeatCount diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs index e947690668..c76c5a3f67 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu /// /// Legacy osu! Slider-type, used for parsing Beatmaps. /// - internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasPosition, IHasCombo + internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasPosition, IHasCombo, IHasLegacyBeatmapComboOffset { public Vector2 Position { get; set; } @@ -19,6 +19,8 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu public bool NewCombo { get; set; } - public int ComboOffset { get; set; } + public int LegacyBeatmapComboOffset { get; set; } + + int IHasLegacyBeatmapComboOffset.LegacyBeatmapComboIndex { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs index e9e5ca8c94..328a380a96 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs @@ -22,7 +22,5 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu public float Y => Position.Y; public bool NewCombo { get; set; } - - public int ComboOffset { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasCombo.cs b/osu.Game/Rulesets/Objects/Types/IHasCombo.cs index d1a4683a1d..7288684d27 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasCombo.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasCombo.cs @@ -12,10 +12,5 @@ namespace osu.Game.Rulesets.Objects.Types /// Whether the HitObject starts a new combo. /// bool NewCombo { get; } - - /// - /// When starting a new combo, the offset of the new combo relative to the current one. - /// - int ComboOffset { get; } } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasLegacyBeatmapComboOffset.cs b/osu.Game/Rulesets/Objects/Types/IHasLegacyBeatmapComboOffset.cs new file mode 100644 index 0000000000..1a39192438 --- /dev/null +++ b/osu.Game/Rulesets/Objects/Types/IHasLegacyBeatmapComboOffset.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Objects.Types +{ + /// + /// A type of that has a combo index with arbitrary offsets applied to use when retrieving legacy beatmap combo colours. + /// This is done in stable for hitobjects to skip combo colours from the beatmap skin (known as "colour hax"). + /// See https://osu.ppy.sh/wiki/en/osu%21_File_Formats/Osu_%28file_format%29#type for more information. + /// + public interface IHasLegacyBeatmapComboOffset + { + /// + /// The legacy offset of the new combo relative to the current one, when starting a new combo. + /// + int LegacyBeatmapComboOffset { get; } + + /// + /// The combo index with the applied, + /// to use for legacy beatmap skins to decide on the combo colour. + /// + int LegacyBeatmapComboIndex { get; set; } + } +} From 51ff59242d73cd6f7b83d1fa6059aeac068b168d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 07:36:30 +0300 Subject: [PATCH 0035/2442] Use legacy beatmap combo indices for legacy beatmap skins --- osu.Game/Skinning/LegacyBeatmapSkin.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 3ec205e897..7262169742 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -6,8 +6,11 @@ using osu.Framework.Bindables; using osu.Framework.IO.Stores; using osu.Game.Audio; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Formats; using osu.Game.IO; using osu.Game.Rulesets.Objects.Legacy; +using osu.Game.Rulesets.Objects.Types; +using osuTK.Graphics; namespace osu.Game.Skinning { @@ -39,6 +42,14 @@ namespace osu.Game.Skinning return base.GetConfig(lookup); } + protected override IBindable GetComboColour(IHasComboColours source, int comboIndex, IHasComboInformation combo) + { + if (combo is IHasLegacyBeatmapComboOffset legacyBeatmapCombo) + return base.GetComboColour(source, legacyBeatmapCombo.LegacyBeatmapComboIndex, combo); + + return base.GetComboColour(source, comboIndex, combo); + } + public override ISample GetSample(ISampleInfo sampleInfo) { if (sampleInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacy && legacy.CustomSampleBank == 0) From fda6d8685caa8facab80bc66e129040055b432c1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 09:21:09 +0300 Subject: [PATCH 0036/2442] Let `LegacyBeatmapSkinColourTest` inherit from `TestPlayer` instead --- osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs index 051ede30b7..a38806aa71 100644 --- a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs +++ b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs @@ -10,7 +10,6 @@ using osu.Framework.Bindables; using osu.Framework.IO.Stores; using osu.Framework.Testing; using osu.Game.Beatmaps; -using osu.Game.Screens.Play; using osu.Game.Skinning; using osu.Game.Tests.Visual; using osuTK.Graphics; @@ -66,16 +65,12 @@ namespace osu.Game.Tests.Beatmaps protected virtual ExposedPlayer CreateTestPlayer(bool userHasCustomColours) => new ExposedPlayer(userHasCustomColours); - protected class ExposedPlayer : Player + protected class ExposedPlayer : TestPlayer { protected readonly bool UserHasCustomColours; public ExposedPlayer(bool userHasCustomColours) - : base(new PlayerConfiguration - { - AllowPause = false, - ShowResults = false, - }) + : base(false, false) { UserHasCustomColours = userHasCustomColours; } From 4cdfdcaddf00a3ce24ecdd40ae486cd8b39fce50 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 09:21:38 +0300 Subject: [PATCH 0037/2442] Expand the combo colours used for testing To avoid potential false positive. --- osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs index a38806aa71..2315bbf709 100644 --- a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs +++ b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs @@ -107,6 +107,8 @@ namespace osu.Game.Tests.Beatmaps { new Color4(50, 100, 150, 255), new Color4(40, 80, 120, 255), + new Color4(25, 50, 75, 255), + new Color4(10, 20, 30, 255), }; public static readonly Color4 HYPER_DASH_COLOUR = Color4.DarkBlue; @@ -134,6 +136,8 @@ namespace osu.Game.Tests.Beatmaps { new Color4(150, 100, 50, 255), new Color4(20, 20, 20, 255), + new Color4(75, 50, 25, 255), + new Color4(80, 80, 80, 255), }; public static readonly Color4 HYPER_DASH_COLOUR = Color4.LightBlue; From 71d3b44c78cd3256aed8bd79b3b8593ef147f281 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 09:22:19 +0300 Subject: [PATCH 0038/2442] Add test coverage for legacy beatmap combo offsets --- .../TestSceneLegacyBeatmapSkin.cs | 86 +++++++++++++++++-- 1 file changed, 81 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs index c26419b0e8..c24e54b4e8 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.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; +using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; @@ -11,6 +13,7 @@ using osu.Game.Rulesets.Osu.Objects; using osu.Game.Skinning; using osu.Game.Tests.Beatmaps; using osuTK; +using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Tests { @@ -77,23 +80,96 @@ namespace osu.Game.Rulesets.Osu.Tests AddAssert("is custom user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours)); } + [TestCase(true, true)] + [TestCase(true, false)] + [TestCase(false, true)] + [TestCase(false, false)] + public void TestLegacyOffsetWithBeatmapColours(bool userHasCustomColours, bool useBeatmapSkin) + { + TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true, getHitCirclesWithLegacyOffsets()); + base.TestBeatmapComboColours(userHasCustomColours, useBeatmapSkin); + + assertCorrectObjectComboColours("is beatmap skin colours with legacy offsets applied", + TestBeatmapSkin.Colours, + (i, obj) => i + 1 + obj.LegacyBeatmapComboOffset); + } + + [TestCase(true)] + [TestCase(false)] + public void TestLegacyOffsetWithIgnoredBeatmapColours(bool useBeatmapSkin) + { + TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true, getHitCirclesWithLegacyOffsets()); + base.TestBeatmapComboColoursOverride(useBeatmapSkin); + + assertCorrectObjectComboColours("is user skin colours without legacy offsets applied", + TestSkin.Colours, + (i, _) => i + 1); + } + + private void assertCorrectObjectComboColours(string description, Color4[] expectedColours, Func nextExpectedComboIndex) + { + AddUntilStep("wait for objects to become alive", () => + TestPlayer.DrawableRuleset.Playfield.AllHitObjects.Count() == TestPlayer.DrawableRuleset.Objects.Count()); + + AddAssert(description, () => + { + int index = 0; + + foreach (var drawable in TestPlayer.DrawableRuleset.Playfield.AllHitObjects) + { + index = nextExpectedComboIndex(index, (OsuHitObject)drawable.HitObject); + + if (drawable.AccentColour.Value != expectedColours[index % expectedColours.Length]) + return false; + } + + return true; + }); + } + + private static IEnumerable getHitCirclesWithLegacyOffsets() + { + var hitObjects = new List(); + + for (int i = 0; i < 5; i++) + { + hitObjects.Add(new HitCircle + { + StartTime = i, + Position = new Vector2(256, 192), + NewCombo = true, + LegacyBeatmapComboOffset = i, + }); + } + + return hitObjects; + } + private class OsuCustomSkinWorkingBeatmap : CustomSkinWorkingBeatmap { - public OsuCustomSkinWorkingBeatmap(AudioManager audio, bool hasColours) - : base(createBeatmap(), audio, hasColours) + public OsuCustomSkinWorkingBeatmap(AudioManager audio, bool hasColours, IEnumerable hitObjects = null) + : base(createBeatmap(hitObjects), audio, hasColours) { } - private static IBeatmap createBeatmap() => - new Beatmap + private static IBeatmap createBeatmap(IEnumerable hitObjects) + { + var beatmap = new Beatmap { BeatmapInfo = { BeatmapSet = new BeatmapSetInfo(), Ruleset = new OsuRuleset().RulesetInfo, }, - HitObjects = { new HitCircle { Position = new Vector2(256, 192) } } }; + + beatmap.HitObjects.AddRange(hitObjects ?? new[] + { + new HitCircle { Position = new Vector2(256, 192) } + }); + + return beatmap; + } } } } From bf6e98345c53fbaf0eb61e8d6542516567aa2b12 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 14:18:51 +0300 Subject: [PATCH 0039/2442] Remove and update pre-existing test cases --- .../Formats/LegacyBeatmapDecoderTest.cs | 39 ++++--------------- 1 file changed, 7 insertions(+), 32 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 0f82492e51..be88bb9b43 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -14,8 +14,6 @@ using osu.Game.Rulesets.Objects.Types; using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Timing; using osu.Game.IO; -using osu.Game.Rulesets.Catch; -using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Legacy; @@ -310,7 +308,7 @@ namespace osu.Game.Tests.Beatmaps.Formats } [Test] - public void TestDecodeBeatmapComboOffsetsOsu() + public void TestDecodeLegacyBeatmapComboOffsets() { var decoder = new LegacyBeatmapDecoder(); @@ -323,35 +321,12 @@ namespace osu.Game.Tests.Beatmaps.Formats new OsuBeatmapProcessor(converted).PreProcess(); new OsuBeatmapProcessor(converted).PostProcess(); - Assert.AreEqual(4, ((IHasComboInformation)converted.HitObjects.ElementAt(0)).ComboIndex); - Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(2)).ComboIndex); - Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(4)).ComboIndex); - Assert.AreEqual(6, ((IHasComboInformation)converted.HitObjects.ElementAt(6)).ComboIndex); - Assert.AreEqual(11, ((IHasComboInformation)converted.HitObjects.ElementAt(8)).ComboIndex); - Assert.AreEqual(14, ((IHasComboInformation)converted.HitObjects.ElementAt(11)).ComboIndex); - } - } - - [Test] - public void TestDecodeBeatmapComboOffsetsCatch() - { - var decoder = new LegacyBeatmapDecoder(); - - using (var resStream = TestResources.OpenResource("hitobject-combo-offset.osu")) - using (var stream = new LineBufferedReader(resStream)) - { - var beatmap = decoder.Decode(stream); - - var converted = new CatchBeatmapConverter(beatmap, new CatchRuleset()).Convert(); - new CatchBeatmapProcessor(converted).PreProcess(); - new CatchBeatmapProcessor(converted).PostProcess(); - - Assert.AreEqual(4, ((IHasComboInformation)converted.HitObjects.ElementAt(0)).ComboIndex); - Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(2)).ComboIndex); - Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(4)).ComboIndex); - Assert.AreEqual(6, ((IHasComboInformation)converted.HitObjects.ElementAt(6)).ComboIndex); - Assert.AreEqual(11, ((IHasComboInformation)converted.HitObjects.ElementAt(8)).ComboIndex); - Assert.AreEqual(14, ((IHasComboInformation)converted.HitObjects.ElementAt(11)).ComboIndex); + Assert.AreEqual(4, ((IHasLegacyBeatmapComboOffset)converted.HitObjects.ElementAt(0)).LegacyBeatmapComboIndex); + Assert.AreEqual(5, ((IHasLegacyBeatmapComboOffset)converted.HitObjects.ElementAt(2)).LegacyBeatmapComboIndex); + Assert.AreEqual(5, ((IHasLegacyBeatmapComboOffset)converted.HitObjects.ElementAt(4)).LegacyBeatmapComboIndex); + Assert.AreEqual(6, ((IHasLegacyBeatmapComboOffset)converted.HitObjects.ElementAt(6)).LegacyBeatmapComboIndex); + Assert.AreEqual(11, ((IHasLegacyBeatmapComboOffset)converted.HitObjects.ElementAt(8)).LegacyBeatmapComboIndex); + Assert.AreEqual(14, ((IHasLegacyBeatmapComboOffset)converted.HitObjects.ElementAt(11)).LegacyBeatmapComboIndex); } } From 6fb9eb8b33227284c889760d6ba45a22408f04e4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 14:24:13 +0300 Subject: [PATCH 0040/2442] Move legacy beatmap combo offset to osu! processor Better suited there, I intiailly wanted the whole legacy interface to reside in `osu.Game.Rulesets.Osu` but it's required in `ConvertHitObjectParser` and that's in the main game project, so had to leave the interface as-is for now. --- .../Beatmaps/OsuBeatmapProcessor.cs | 19 +++++++++++++++++++ osu.Game/Beatmaps/BeatmapProcessor.cs | 10 ---------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs index f51f04bf87..5bff3c7bcc 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs @@ -2,9 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; using osuTK; @@ -19,6 +21,23 @@ namespace osu.Game.Rulesets.Osu.Beatmaps { } + public override void PreProcess() + { + base.PreProcess(); + + OsuHitObject lastObj = null; + + foreach (var obj in Beatmap.HitObjects.OfType()) + { + if (obj.NewCombo) + obj.LegacyBeatmapComboIndex = (lastObj?.LegacyBeatmapComboIndex ?? 0) + obj.LegacyBeatmapComboOffset + 1; + else if (lastObj != null) + obj.LegacyBeatmapComboIndex = lastObj.LegacyBeatmapComboIndex; + + lastObj = obj; + } + } + public override void PostProcess() { base.PostProcess(); diff --git a/osu.Game/Beatmaps/BeatmapProcessor.cs b/osu.Game/Beatmaps/BeatmapProcessor.cs index ac2d7d0fc1..f75f04b26f 100644 --- a/osu.Game/Beatmaps/BeatmapProcessor.cs +++ b/osu.Game/Beatmaps/BeatmapProcessor.cs @@ -48,16 +48,6 @@ namespace osu.Game.Beatmaps obj.ComboIndex = lastObj.ComboIndex; } - if (obj is IHasLegacyBeatmapComboOffset legacyObj) - { - var lastLegacyObj = (IHasLegacyBeatmapComboOffset)lastObj; - - if (obj.NewCombo) - legacyObj.LegacyBeatmapComboIndex = (lastLegacyObj?.LegacyBeatmapComboIndex ?? 0) + legacyObj.LegacyBeatmapComboOffset + 1; - else if (lastLegacyObj != null) - legacyObj.LegacyBeatmapComboIndex = lastLegacyObj.LegacyBeatmapComboIndex; - } - lastObj = obj; } } From cc24d8a6bd9627645de6afec29d514ad9e11e884 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 14:27:37 +0300 Subject: [PATCH 0041/2442] Remove unused using directive I literally noticed it after I pushed, god damn it. --- osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs index 5bff3c7bcc..b7ec8795ca 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs @@ -6,7 +6,6 @@ using System.Linq; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; using osuTK; From fe099be443b7ce7f2552cfa9958752197a43ebea Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Wed, 19 May 2021 22:25:56 +0800 Subject: [PATCH 0042/2442] Implemented osu target mod --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 367 ++++++++++++++++++++- 1 file changed, 366 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 2464308347..667fc91142 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -1,13 +1,34 @@ // 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 osu.Framework.Allocation; +using osu.Framework.Audio.Track; +using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Framework.Utils; +using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Beatmaps; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Skinning; +using osuTK; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModTarget : Mod + public class OsuModTarget : ModWithVisibilityAdjustment, IApplicableToDrawableRuleset, + IApplicableToHealthProcessor, IApplicableToDifficulty { public override string Name => "Target"; public override string Acronym => "TP"; @@ -15,5 +36,349 @@ namespace osu.Game.Rulesets.Osu.Mods public override IconUsage? Icon => OsuIcon.ModTarget; public override string Description => @"Practice keeping up with the beat of the song."; public override double ScoreMultiplier => 1; + + public void ApplyToHealthProcessor(HealthProcessor healthProcessor) + { + // Sudden death + healthProcessor.FailConditions += (_, result) + => result.Type.AffectsCombo() + && !result.IsHit; + } + + // Maximum distance to jump + public const float MAX_DISTANCE = 250f; + + public override void ApplyToBeatmap(IBeatmap beatmap) + { + base.ApplyToBeatmap(beatmap); + + var osuBeatmap = (OsuBeatmap)beatmap; + var origHitObjects = osuBeatmap.HitObjects.OrderBy(x => x.StartTime).ToList(); + + var hitObjects = new List(); + + // Only place circles between startTime and endTime + var startTime = origHitObjects.First().StartTime; + double endTime; + var endObj = origHitObjects.Last(); + switch (endObj) + { + case Slider slider: + endTime = slider.EndTime; + break; + case Spinner spinner: + endTime = spinner.EndTime; + break; + default: + endTime = endObj.StartTime; + break; + } + + var comboStarts = origHitObjects.Where(x => x.NewCombo).Select(x => x.StartTime).ToList(); + + TimingControlPoint currentTimingPoint = osuBeatmap.ControlPointInfo.TimingPointAt(startTime); + var currentTime = currentTimingPoint.Time; + int currentCombo = -1; + IList lastSamples = null; + + float direction = MathHelper.TwoPi * RNG.NextSingle(); + float distance = 40f; + + while (!Precision.AlmostBigger(currentTime, endTime)) + { + // Place a circle + + // Don't place any circles before the start of the first hit object of the map + // Don't place any circles from the start of break time to the start of the first hit object after the break + if (!Precision.AlmostBigger(startTime, currentTime) + && !osuBeatmap.Breaks.Any(x => Precision.AlmostBigger(currentTime, x.StartTime) + && Precision.AlmostBigger(origHitObjects.First(y => Precision.AlmostBigger(y.StartTime, x.EndTime)).StartTime, currentTime))) + { + var newCircle = new HitCircle(); + newCircle.ApplyDefaults(osuBeatmap.ControlPointInfo, osuBeatmap.BeatmapInfo.BaseDifficulty); + newCircle.StartTime = currentTime; + + // Determine circle position + if (hitObjects.Count == 0) + { + newCircle.Position = new Vector2(RNG.NextSingle(OsuPlayfield.BASE_SIZE.X), RNG.NextSingle(OsuPlayfield.BASE_SIZE.Y)); + } + else + { + var relativePos = new Vector2( + distance * (float)Math.Cos(direction), + distance * (float)Math.Sin(direction) + ); + relativePos = getRotatedVector(hitObjects.Last().Position, relativePos); + direction = (float)Math.Atan2(relativePos.Y, relativePos.X); + + var newPosition = Vector2.Add(hitObjects.Last().Position, relativePos); + + if (newPosition.Y < 0) + newPosition.Y = 0; + else if (newPosition.Y > OsuPlayfield.BASE_SIZE.Y) + newPosition.Y = OsuPlayfield.BASE_SIZE.Y; + if (newPosition.X < 0) + newPosition.X = 0; + else if (newPosition.X > OsuPlayfield.BASE_SIZE.X) + newPosition.X = OsuPlayfield.BASE_SIZE.X; + + newCircle.Position = newPosition; + // Add a random nudge to the direction + direction += distance / MAX_DISTANCE * (RNG.NextSingle() * MathHelper.TwoPi - MathHelper.Pi); + } + + // Determine samples to use + var samples = getSamplesAtTime(origHitObjects, currentTime); + if (samples == null) + { + if (lastSamples != null) + newCircle.Samples = lastSamples; + } + else + { + newCircle.Samples = samples; + lastSamples = samples; + } + + // Determine combo + if (comboStarts.Count > currentCombo + 1 && !Precision.AlmostBigger(comboStarts[currentCombo + 1], currentTime)) + { + currentCombo++; + newCircle.NewCombo = true; + newCircle.IndexInCurrentCombo = 0; + if (hitObjects.Count > 0) + hitObjects.Last().LastInCombo = true; + + // Increase distance for every combo + distance = Math.Min(distance * 1.05f, MAX_DISTANCE); + // Randomize direction as well + direction = MathHelper.TwoPi * RNG.NextSingle(); + } + else + { + if (hitObjects.Count > 0) + newCircle.IndexInCurrentCombo = hitObjects.Last().IndexInCurrentCombo + 1; + } + newCircle.ComboIndex = currentCombo; + + // Remove the last circle if it is too close in time to this one + if (hitObjects.Count > 0 + && Precision.AlmostBigger(currentTimingPoint.BeatLength / 2, newCircle.StartTime - hitObjects.Last().StartTime)) + { + hitObjects.RemoveAt(hitObjects.Count - 1); + } + + hitObjects.Add(newCircle); + } + + // Advance to the next beat and check for timing point changes + + currentTime += currentTimingPoint.BeatLength; + + var newTimingPoint = osuBeatmap.ControlPointInfo.TimingPointAt(currentTime); + if (newTimingPoint != currentTimingPoint) + { + currentTimingPoint = newTimingPoint; + currentTime = currentTimingPoint.Time; + } + } + + osuBeatmap.HitObjects = hitObjects; + } + + /// + /// Get samples (if any) for a specific point in time. + /// + /// + /// Samples will be returned if a hit circle or a slider node exists at that point of time. + /// + /// The list of hit objects in a beatmap, ordered by StartTime + /// The point in time to get samples for + /// Hit samples + private IList getSamplesAtTime(List hitObjects, double time) + { + var sampleObj = hitObjects.FirstOrDefault(x => + { + if (Precision.AlmostEquals(time, x.StartTime)) return true; + if (x is Slider slider + && Precision.AlmostBigger(time, slider.StartTime) + && Precision.AlmostBigger(slider.EndTime, time)) + { + if (Precision.AlmostEquals((time - slider.StartTime) % slider.SpanDuration, 0)) + { + return true; + } + } + return false; + }); + if (sampleObj == null) return null; + + IList samples = null; + if (sampleObj is Slider slider) + { + samples = slider.NodeSamples[(int)Math.Round((time - slider.StartTime) % slider.SpanDuration)]; + } + else + { + samples = sampleObj.Samples; + } + return samples; + } + + protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) + { + } + + protected override void ApplyNormalVisibilityState(DrawableHitObject drawable, ArmedState state) + { + if (drawable is DrawableSpinner) + return; + + var h = (OsuHitObject)drawable.HitObject; + + // apply grow and fade effect + if (drawable is DrawableHitCircle circle) + { + using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) + { + drawable.ScaleTo(0.4f) + .Then().ScaleTo(1.6f, h.TimePreempt * 2); + drawable.FadeTo(0.5f) + .Then().Delay(h.TimeFadeIn).FadeTo(1f); + + // remove approach circles + circle.ApproachCircle.Hide(); + } + } + } + + public void ReadFromDifficulty(BeatmapDifficulty difficulty) + { + } + + public void ApplyToDifficulty(BeatmapDifficulty difficulty) + { + // Decrease AR to increase preempt time + difficulty.ApproachRate *= 0.5f; + } + + // The distances from the hit objects to the borders of the playfield they start to "turn around" and curve towards the middle. + // The closer the hit objects draw to the border, the sharper the turn + private const byte border_distance_x = 192; + private const byte border_distance_y = 144; + + /// + /// Determines the position of the current hit object relative to the previous one. + /// + /// The position of the current hit object relative to the previous one + private Vector2 getRotatedVector(Vector2 prevPosChanged, Vector2 posRelativeToPrev) + { + var relativeRotationDistance = 0f; + var playfieldMiddle = Vector2.Divide(OsuPlayfield.BASE_SIZE, 2); + + if (prevPosChanged.X < playfieldMiddle.X) + { + relativeRotationDistance = Math.Max( + (border_distance_x - prevPosChanged.X) / border_distance_x, + relativeRotationDistance + ); + } + else + { + relativeRotationDistance = Math.Max( + (prevPosChanged.X - (OsuPlayfield.BASE_SIZE.X - border_distance_x)) / border_distance_x, + relativeRotationDistance + ); + } + + if (prevPosChanged.Y < playfieldMiddle.Y) + { + relativeRotationDistance = Math.Max( + (border_distance_y - prevPosChanged.Y) / border_distance_y, + relativeRotationDistance + ); + } + else + { + relativeRotationDistance = Math.Max( + (prevPosChanged.Y - (OsuPlayfield.BASE_SIZE.Y - border_distance_y)) / border_distance_y, + relativeRotationDistance + ); + } + + return rotateVectorTowardsVector( + posRelativeToPrev, + Vector2.Subtract(playfieldMiddle, prevPosChanged), + relativeRotationDistance / 2 + ); + } + + /// + /// Rotates vector "initial" towards vector "destinantion" + /// + /// Vector to rotate to "destination" + /// Vector "initial" should be rotated to + /// The angle the vector should be rotated relative to the difference between the angles of the the two vectors. + /// Resulting vector + private Vector2 rotateVectorTowardsVector(Vector2 initial, Vector2 destination, float relativeDistance) + { + var initialAngleRad = Math.Atan2(initial.Y, initial.X); + var destAngleRad = Math.Atan2(destination.Y, destination.X); + + var diff = destAngleRad - initialAngleRad; + + while (diff < -Math.PI) + { + diff += 2 * Math.PI; + } + + while (diff > Math.PI) + { + diff -= 2 * Math.PI; + } + + var finalAngleRad = initialAngleRad + relativeDistance * diff; + + return new Vector2( + initial.Length * (float)Math.Cos(finalAngleRad), + initial.Length * (float)Math.Sin(finalAngleRad) + ); + } + + // Background metronome + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + drawableRuleset.Overlays.Add(new TargetBeatContainer()); + } + + public class TargetBeatContainer : BeatSyncedContainer + { + private PausableSkinnableSound sample; + + public TargetBeatContainer() + { + Divisor = 1; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChildren = new Drawable[] + { + sample = new PausableSkinnableSound(new SampleInfo("spinnerbonus")), // TODO: use another sample? + }; + } + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) + { + base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + + if (!IsBeatSyncedWithTrack) return; + + sample?.Play(); + } + } } } From d20b5c2d5aa9865bb302fe17b4a541f237b436a1 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 20 May 2021 11:57:13 +0800 Subject: [PATCH 0043/2442] Refactored ApplyToBeatmap --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 202 +++++++++++---------- 1 file changed, 105 insertions(+), 97 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 667fc91142..412b7049ea 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -55,8 +55,6 @@ namespace osu.Game.Rulesets.Osu.Mods var osuBeatmap = (OsuBeatmap)beatmap; var origHitObjects = osuBeatmap.HitObjects.OrderBy(x => x.StartTime).ToList(); - var hitObjects = new List(); - // Only place circles between startTime and endTime var startTime = origHitObjects.First().StartTime; double endTime; @@ -74,113 +72,122 @@ namespace osu.Game.Rulesets.Osu.Mods break; } - var comboStarts = origHitObjects.Where(x => x.NewCombo).Select(x => x.StartTime).ToList(); - - TimingControlPoint currentTimingPoint = osuBeatmap.ControlPointInfo.TimingPointAt(startTime); - var currentTime = currentTimingPoint.Time; - int currentCombo = -1; - IList lastSamples = null; - - float direction = MathHelper.TwoPi * RNG.NextSingle(); - float distance = 40f; - - while (!Precision.AlmostBigger(currentTime, endTime)) - { - // Place a circle - - // Don't place any circles before the start of the first hit object of the map - // Don't place any circles from the start of break time to the start of the first hit object after the break - if (!Precision.AlmostBigger(startTime, currentTime) - && !osuBeatmap.Breaks.Any(x => Precision.AlmostBigger(currentTime, x.StartTime) - && Precision.AlmostBigger(origHitObjects.First(y => Precision.AlmostBigger(y.StartTime, x.EndTime)).StartTime, currentTime))) + // Generate the beats + var beats = osuBeatmap.ControlPointInfo.TimingPoints + .Where(x => Precision.AlmostBigger(endTime, x.Time)) + .SelectMany(tp => + { + var tpBeats = new List(); + var currentTime = tp.Time; + while (Precision.AlmostBigger(endTime, currentTime) && osuBeatmap.ControlPointInfo.TimingPointAt(currentTime) == tp) + { + tpBeats.Add(currentTime); + currentTime += tp.BeatLength; + } + return tpBeats; + }) + // Remove beats that are before startTime + .Where(x => Precision.AlmostBigger(x, startTime)) + // Remove beats during breaks + .Where(x => !osuBeatmap.Breaks.Any(b => + Precision.AlmostBigger(x, b.StartTime) + && Precision.AlmostBigger(origHitObjects.First(y => Precision.AlmostBigger(y.StartTime, b.EndTime)).StartTime, x) + )) + .ToList(); + // Generate a hit circle for each beat + var hitObjects = beats + // Remove beats that are too close to the next one (e.g. due to timing point changes) + .Where((x, idx) => + { + if (idx == beats.Count - 1) return true; + if (Precision.AlmostBigger(osuBeatmap.ControlPointInfo.TimingPointAt(x).BeatLength / 2, beats[idx + 1] - x)) + return false; + return true; + }) + .Select(x => { var newCircle = new HitCircle(); newCircle.ApplyDefaults(osuBeatmap.ControlPointInfo, osuBeatmap.BeatmapInfo.BaseDifficulty); - newCircle.StartTime = currentTime; + newCircle.StartTime = x; + return (OsuHitObject)newCircle; + }).ToList(); - // Determine circle position - if (hitObjects.Count == 0) - { - newCircle.Position = new Vector2(RNG.NextSingle(OsuPlayfield.BASE_SIZE.X), RNG.NextSingle(OsuPlayfield.BASE_SIZE.Y)); - } - else - { - var relativePos = new Vector2( + // Add hit samples to the circles + for (int i = 0; i < hitObjects.Count; i++) + { + var x = hitObjects[i]; + var samples = getSamplesAtTime(origHitObjects, x.StartTime); + if (samples == null) + { + if (i > 0) + x.Samples = hitObjects[i - 1].Samples; + } + else + { + x.Samples = samples; + } + } + + // Process combo numbers + // First follow the combo indices in the original beatmap + hitObjects.ForEach(x => + { + var origObj = origHitObjects.FindLast(y => Precision.AlmostBigger(x.StartTime, y.StartTime)); + if (origObj == null) x.ComboIndex = 0; + else x.ComboIndex = origObj.ComboIndex; + }); + // Then reprocess them to ensure continuity in the combo indices and add indices in current combo + var combos = hitObjects.GroupBy(x => x.ComboIndex).ToList(); + for (int i = 0; i < combos.Count; i++) + { + var group = combos[i].ToList(); + group.First().NewCombo = true; + group.Last().LastInCombo = true; + + for (int j = 0; j < group.Count; j++) + { + var x = group[j]; + x.ComboIndex = i; + x.IndexInCurrentCombo = j; + } + } + + // Position all hit circles + var direction = MathHelper.TwoPi * RNG.NextSingle(); + for (int i = 0; i < hitObjects.Count; i++) + { + var x = hitObjects[i]; + if (i == 0) + { + x.Position = new Vector2(RNG.NextSingle(OsuPlayfield.BASE_SIZE.X), RNG.NextSingle(OsuPlayfield.BASE_SIZE.Y)); + } + else + { + var distance = Math.Min(MAX_DISTANCE, 40f * (float)Math.Pow(1.05, x.ComboIndex)); + var relativePos = new Vector2( distance * (float)Math.Cos(direction), distance * (float)Math.Sin(direction) ); - relativePos = getRotatedVector(hitObjects.Last().Position, relativePos); - direction = (float)Math.Atan2(relativePos.Y, relativePos.X); + relativePos = getRotatedVector(hitObjects[i - 1].Position, relativePos); + direction = (float)Math.Atan2(relativePos.Y, relativePos.X); - var newPosition = Vector2.Add(hitObjects.Last().Position, relativePos); + var newPosition = Vector2.Add(hitObjects[i - 1].Position, relativePos); - if (newPosition.Y < 0) - newPosition.Y = 0; - else if (newPosition.Y > OsuPlayfield.BASE_SIZE.Y) - newPosition.Y = OsuPlayfield.BASE_SIZE.Y; - if (newPosition.X < 0) - newPosition.X = 0; - else if (newPosition.X > OsuPlayfield.BASE_SIZE.X) - newPosition.X = OsuPlayfield.BASE_SIZE.X; + if (newPosition.Y < 0) + newPosition.Y = 0; + else if (newPosition.Y > OsuPlayfield.BASE_SIZE.Y) + newPosition.Y = OsuPlayfield.BASE_SIZE.Y; + if (newPosition.X < 0) + newPosition.X = 0; + else if (newPosition.X > OsuPlayfield.BASE_SIZE.X) + newPosition.X = OsuPlayfield.BASE_SIZE.X; - newCircle.Position = newPosition; - // Add a random nudge to the direction - direction += distance / MAX_DISTANCE * (RNG.NextSingle() * MathHelper.TwoPi - MathHelper.Pi); - } + x.Position = newPosition; - // Determine samples to use - var samples = getSamplesAtTime(origHitObjects, currentTime); - if (samples == null) - { - if (lastSamples != null) - newCircle.Samples = lastSamples; - } - else - { - newCircle.Samples = samples; - lastSamples = samples; - } - - // Determine combo - if (comboStarts.Count > currentCombo + 1 && !Precision.AlmostBigger(comboStarts[currentCombo + 1], currentTime)) - { - currentCombo++; - newCircle.NewCombo = true; - newCircle.IndexInCurrentCombo = 0; - if (hitObjects.Count > 0) - hitObjects.Last().LastInCombo = true; - - // Increase distance for every combo - distance = Math.Min(distance * 1.05f, MAX_DISTANCE); - // Randomize direction as well + if (x.LastInCombo) direction = MathHelper.TwoPi * RNG.NextSingle(); - } else - { - if (hitObjects.Count > 0) - newCircle.IndexInCurrentCombo = hitObjects.Last().IndexInCurrentCombo + 1; - } - newCircle.ComboIndex = currentCombo; - - // Remove the last circle if it is too close in time to this one - if (hitObjects.Count > 0 - && Precision.AlmostBigger(currentTimingPoint.BeatLength / 2, newCircle.StartTime - hitObjects.Last().StartTime)) - { - hitObjects.RemoveAt(hitObjects.Count - 1); - } - - hitObjects.Add(newCircle); - } - - // Advance to the next beat and check for timing point changes - - currentTime += currentTimingPoint.BeatLength; - - var newTimingPoint = osuBeatmap.ControlPointInfo.TimingPointAt(currentTime); - if (newTimingPoint != currentTimingPoint) - { - currentTimingPoint = newTimingPoint; - currentTime = currentTimingPoint.Time; + direction += distance / MAX_DISTANCE * (RNG.NextSingle() * MathHelper.TwoPi - MathHelper.Pi); } } @@ -242,6 +249,7 @@ namespace osu.Game.Rulesets.Osu.Mods { using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) { + // todo: this doesn't feel quite right yet drawable.ScaleTo(0.4f) .Then().ScaleTo(1.6f, h.TimePreempt * 2); drawable.FadeTo(0.5f) @@ -367,7 +375,7 @@ namespace osu.Game.Rulesets.Osu.Mods { InternalChildren = new Drawable[] { - sample = new PausableSkinnableSound(new SampleInfo("spinnerbonus")), // TODO: use another sample? + sample = new PausableSkinnableSound(new SampleInfo("spinnerbonus")), // todo: use another sample? }; } From 15e7cce264ed3c31cf03a29a7cadd241abbf030f Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sat, 22 May 2021 14:14:41 +0800 Subject: [PATCH 0044/2442] Added seed setting, fixed mod ignoring IncreaseFirstObjectVisibility --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 116 +++++++++++++++++++-- 1 file changed, 110 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 412b7049ea..3fe0161742 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -6,15 +6,23 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Audio.Track; +using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Utils; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.Multiplayer; +using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Objects; @@ -37,6 +45,13 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => @"Practice keeping up with the beat of the song."; public override double ScoreMultiplier => 1; + [SettingSource("Seed", "Seed for random circle placement", SettingControlType = typeof(OsuModTargetSettingsControl))] + public Bindable Seed { get; } = new Bindable + { + Default = null, + Value = null + }; + public void ApplyToHealthProcessor(HealthProcessor healthProcessor) { // Sudden death @@ -50,7 +65,14 @@ namespace osu.Game.Rulesets.Osu.Mods public override void ApplyToBeatmap(IBeatmap beatmap) { - base.ApplyToBeatmap(beatmap); + Seed.Value ??= RNG.Next(); + + var rng = new Random(Seed.Value.GetValueOrDefault()); + + float nextSingle(float max = 1f) + { + return (float)(rng.NextDouble() * max); + } var osuBeatmap = (OsuBeatmap)beatmap; var origHitObjects = osuBeatmap.HitObjects.OrderBy(x => x.StartTime).ToList(); @@ -153,13 +175,13 @@ namespace osu.Game.Rulesets.Osu.Mods } // Position all hit circles - var direction = MathHelper.TwoPi * RNG.NextSingle(); + var direction = MathHelper.TwoPi * nextSingle(); for (int i = 0; i < hitObjects.Count; i++) { var x = hitObjects[i]; if (i == 0) { - x.Position = new Vector2(RNG.NextSingle(OsuPlayfield.BASE_SIZE.X), RNG.NextSingle(OsuPlayfield.BASE_SIZE.Y)); + x.Position = new Vector2(nextSingle(OsuPlayfield.BASE_SIZE.X), nextSingle(OsuPlayfield.BASE_SIZE.Y)); } else { @@ -185,13 +207,15 @@ namespace osu.Game.Rulesets.Osu.Mods x.Position = newPosition; if (x.LastInCombo) - direction = MathHelper.TwoPi * RNG.NextSingle(); + direction = MathHelper.TwoPi * nextSingle(); else - direction += distance / MAX_DISTANCE * (RNG.NextSingle() * MathHelper.TwoPi - MathHelper.Pi); + direction += distance / MAX_DISTANCE * (nextSingle() * MathHelper.TwoPi - MathHelper.Pi); } } osuBeatmap.HitObjects = hitObjects; + + base.ApplyToBeatmap(beatmap); } /// @@ -233,7 +257,7 @@ namespace osu.Game.Rulesets.Osu.Mods return samples; } - protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) + protected override void ApplyIncreasedVisibilityState(DrawableHitObject drawable, ArmedState state) { } @@ -389,4 +413,84 @@ namespace osu.Game.Rulesets.Osu.Mods } } } + + public class OsuModTargetSettingsControl : SettingsItem + { + protected override Drawable CreateControl() => new SeedControl + { + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding { Top = 5 } + }; + + private sealed class SeedControl : CompositeDrawable, IHasCurrentValue + { + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + public Bindable Current + { + get => current.Current; + set + { + current.Current = value; + seedNumberBox.Text = value.Value.ToString(); + } + } + + private readonly OsuNumberBox seedNumberBox; + + public SeedControl() + { + AutoSizeAxes = Axes.Y; + + InternalChildren = new[] + { + new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, 120) + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize) + }, + Content = new[] + { + new Drawable[] + { + seedNumberBox = new OsuNumberBox + { + RelativeSizeAxes = Axes.X, + CommitOnFocusLost = true, + }, + new TriangleButton + { + Width = 120, + Text = "Randomise", + Action = () => current.Value = RNG.Next(), + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + }, + } + } + } + }; + + seedNumberBox.Current.BindValueChanged(e => + { + if (int.TryParse(e.NewValue, out var intVal)) + current.Value = intVal; + else + current.Value = null; + }); + current.BindValueChanged(e => + { + seedNumberBox.Text = e.NewValue.ToString(); + }); + } + } + } } From 7815b3c72bed5856e52803d646097fdffc56e668 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 10 Jun 2021 10:58:42 +0800 Subject: [PATCH 0045/2442] Display results after fail --- osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs | 2 ++ osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 8 +++++++- osu.Game/Rulesets/Mods/IApplicableFailOverride.cs | 5 +++++ osu.Game/Rulesets/Mods/ModAutoplay.cs | 2 ++ osu.Game/Rulesets/Mods/ModBlockFail.cs | 1 + osu.Game/Rulesets/Mods/ModEasyWithExtraLives.cs | 1 + osu.Game/Rulesets/Mods/ModFailCondition.cs | 1 + osu.Game/Screens/Play/Player.cs | 10 ++++++++-- 8 files changed, 27 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs index aac830801b..2b92174b29 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs @@ -28,6 +28,8 @@ namespace osu.Game.Rulesets.Osu.Mods public bool RestartOnFail => false; + public bool DisplayResultsOnFail => false; + private OsuInputManager inputManager; private IFrameStableClock gameplayClock; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 3fe0161742..c3e9e60420 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -36,7 +36,7 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Mods { public class OsuModTarget : ModWithVisibilityAdjustment, IApplicableToDrawableRuleset, - IApplicableToHealthProcessor, IApplicableToDifficulty + IApplicableToHealthProcessor, IApplicableToDifficulty, IApplicableFailOverride { public override string Name => "Target"; public override string Acronym => "TP"; @@ -52,6 +52,12 @@ namespace osu.Game.Rulesets.Osu.Mods Value = null }; + public bool PerformFail() => true; + + public bool RestartOnFail => false; + + public bool DisplayResultsOnFail => true; + public void ApplyToHealthProcessor(HealthProcessor healthProcessor) { // Sudden death diff --git a/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs b/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs index 8c99d739cb..b7d306e57b 100644 --- a/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs +++ b/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs @@ -18,5 +18,10 @@ namespace osu.Game.Rulesets.Mods /// Whether we want to restart on fail. Only used if returns true. /// bool RestartOnFail { get; } + + /// + /// Whether to proceed to results screen on fail. Only used if returns true and is false. + /// + bool DisplayResultsOnFail { get; } } } diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs index d6e1d46b06..3fb92527f2 100644 --- a/osu.Game/Rulesets/Mods/ModAutoplay.cs +++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs @@ -35,6 +35,8 @@ namespace osu.Game.Rulesets.Mods public bool RestartOnFail => false; + public bool DisplayResultsOnFail => false; + public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModFailCondition), typeof(ModNoFail) }; public override bool HasImplementation => GetType().GenericTypeArguments.Length == 0; diff --git a/osu.Game/Rulesets/Mods/ModBlockFail.cs b/osu.Game/Rulesets/Mods/ModBlockFail.cs index 1fde5abad4..973a192378 100644 --- a/osu.Game/Rulesets/Mods/ModBlockFail.cs +++ b/osu.Game/Rulesets/Mods/ModBlockFail.cs @@ -17,6 +17,7 @@ namespace osu.Game.Rulesets.Mods public bool PerformFail() => false; public bool RestartOnFail => false; + public bool DisplayResultsOnFail => false; public void ReadFromConfig(OsuConfigManager config) { diff --git a/osu.Game/Rulesets/Mods/ModEasyWithExtraLives.cs b/osu.Game/Rulesets/Mods/ModEasyWithExtraLives.cs index 2ac0f59d84..91508b7e70 100644 --- a/osu.Game/Rulesets/Mods/ModEasyWithExtraLives.cs +++ b/osu.Game/Rulesets/Mods/ModEasyWithExtraLives.cs @@ -41,6 +41,7 @@ namespace osu.Game.Rulesets.Mods } public bool RestartOnFail => false; + public bool DisplayResultsOnFail => false; public void ApplyToHealthProcessor(HealthProcessor healthProcessor) { diff --git a/osu.Game/Rulesets/Mods/ModFailCondition.cs b/osu.Game/Rulesets/Mods/ModFailCondition.cs index c0d7bae2b2..012a0f73ff 100644 --- a/osu.Game/Rulesets/Mods/ModFailCondition.cs +++ b/osu.Game/Rulesets/Mods/ModFailCondition.cs @@ -14,6 +14,7 @@ namespace osu.Game.Rulesets.Mods public virtual bool PerformFail() => true; public virtual bool RestartOnFail => true; + public virtual bool DisplayResultsOnFail => false; public void ApplyToHealthProcessor(HealthProcessor healthProcessor) { diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 890883f0d2..b72524ad99 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -604,7 +604,7 @@ namespace osu.Game.Screens.Play if (!this.IsCurrentScreen()) return; - if (!ScoreProcessor.HasCompleted.Value) + if (!ScoreProcessor.HasCompleted.Value && !HealthProcessor.HasFailed) { completionProgressDelegate?.Cancel(); completionProgressDelegate = null; @@ -617,7 +617,7 @@ namespace osu.Game.Screens.Play throw new InvalidOperationException($"{nameof(updateCompletionState)} was fired more than once"); // Only show the completion screen if the player hasn't failed - if (HealthProcessor.HasFailed) + if (HealthProcessor.HasFailed && !Mods.Value.OfType().Any(m => m.DisplayResultsOnFail)) return; ValidForResume = false; @@ -712,6 +712,12 @@ namespace osu.Game.Screens.Play // Called back when the transform finishes private void onFailComplete() { + if (Mods.Value.OfType().Any(m => m.DisplayResultsOnFail)) + { + updateCompletionState(true); + return; + } + GameplayClockContainer.Stop(); FailOverlay.Retries = RestartCount; From 12a17d0983c6f3557ef7c8231fca247f8311181f Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 10 Jun 2021 15:31:20 +0800 Subject: [PATCH 0046/2442] Extract seed setting contorl to IHasSeed --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 90 +------------------- osu.Game/Rulesets/Mods/IHasSeed.cs | 95 ++++++++++++++++++++++ osu.Game/Rulesets/Mods/ModRandom.cs | 81 +----------------- 3 files changed, 99 insertions(+), 167 deletions(-) create mode 100644 osu.Game/Rulesets/Mods/IHasSeed.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index c3e9e60420..04a7365148 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -8,9 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.UserInterface; using osu.Framework.Utils; using osu.Game.Audio; using osu.Game.Beatmaps; @@ -18,11 +16,7 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online.Multiplayer; -using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Objects; @@ -36,7 +30,7 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Mods { public class OsuModTarget : ModWithVisibilityAdjustment, IApplicableToDrawableRuleset, - IApplicableToHealthProcessor, IApplicableToDifficulty, IApplicableFailOverride + IApplicableToHealthProcessor, IApplicableToDifficulty, IApplicableFailOverride, IHasSeed { public override string Name => "Target"; public override string Acronym => "TP"; @@ -45,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => @"Practice keeping up with the beat of the song."; public override double ScoreMultiplier => 1; - [SettingSource("Seed", "Seed for random circle placement", SettingControlType = typeof(OsuModTargetSettingsControl))] + [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SeedSettingsControl))] public Bindable Seed { get; } = new Bindable { Default = null, @@ -419,84 +413,4 @@ namespace osu.Game.Rulesets.Osu.Mods } } } - - public class OsuModTargetSettingsControl : SettingsItem - { - protected override Drawable CreateControl() => new SeedControl - { - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding { Top = 5 } - }; - - private sealed class SeedControl : CompositeDrawable, IHasCurrentValue - { - private readonly BindableWithCurrent current = new BindableWithCurrent(); - - public Bindable Current - { - get => current.Current; - set - { - current.Current = value; - seedNumberBox.Text = value.Value.ToString(); - } - } - - private readonly OsuNumberBox seedNumberBox; - - public SeedControl() - { - AutoSizeAxes = Axes.Y; - - InternalChildren = new[] - { - new GridContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - ColumnDimensions = new[] - { - new Dimension(), - new Dimension(GridSizeMode.Absolute, 120) - }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize) - }, - Content = new[] - { - new Drawable[] - { - seedNumberBox = new OsuNumberBox - { - RelativeSizeAxes = Axes.X, - CommitOnFocusLost = true, - }, - new TriangleButton - { - Width = 120, - Text = "Randomise", - Action = () => current.Value = RNG.Next(), - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - }, - } - } - } - }; - - seedNumberBox.Current.BindValueChanged(e => - { - if (int.TryParse(e.NewValue, out var intVal)) - current.Value = intVal; - else - current.Value = null; - }); - current.BindValueChanged(e => - { - seedNumberBox.Text = e.NewValue.ToString(); - }); - } - } - } } diff --git a/osu.Game/Rulesets/Mods/IHasSeed.cs b/osu.Game/Rulesets/Mods/IHasSeed.cs new file mode 100644 index 0000000000..45a806e80c --- /dev/null +++ b/osu.Game/Rulesets/Mods/IHasSeed.cs @@ -0,0 +1,95 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Configuration; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Settings; + +namespace osu.Game.Rulesets.Mods +{ + public interface IHasSeed + { + public Bindable Seed { get; } + } + + public class SeedSettingsControl : SettingsItem + { + protected override Drawable CreateControl() => new SeedControl + { + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding { Top = 5 } + }; + + private sealed class SeedControl : CompositeDrawable, IHasCurrentValue + { + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + public Bindable Current + { + get => current; + set + { + current.Current = value; + seedNumberBox.Text = value.Value.ToString(); + } + } + + private readonly OsuNumberBox seedNumberBox; + + public SeedControl() + { + AutoSizeAxes = Axes.Y; + + InternalChildren = new[] + { + new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, 2), + new Dimension(GridSizeMode.Relative, 0.25f) + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize) + }, + Content = new[] + { + new Drawable[] + { + seedNumberBox = new OsuNumberBox + { + RelativeSizeAxes = Axes.X, + CommitOnFocusLost = true + } + } + } + } + }; + + seedNumberBox.Current.BindValueChanged(e => + { + int? value = null; + + if (int.TryParse(e.NewValue, out var intVal)) + value = intVal; + + current.Value = value; + }); + } + + protected override void Update() + { + if (current.Value == null) + seedNumberBox.Text = current.Current.Value.ToString(); + } + } + } +} diff --git a/osu.Game/Rulesets/Mods/ModRandom.cs b/osu.Game/Rulesets/Mods/ModRandom.cs index 3f14263420..eec66161ff 100644 --- a/osu.Game/Rulesets/Mods/ModRandom.cs +++ b/osu.Game/Rulesets/Mods/ModRandom.cs @@ -13,7 +13,7 @@ using osu.Game.Overlays.Settings; namespace osu.Game.Rulesets.Mods { - public abstract class ModRandom : Mod + public abstract class ModRandom : Mod, IHasSeed { public override string Name => "Random"; public override string Acronym => "RD"; @@ -21,88 +21,11 @@ namespace osu.Game.Rulesets.Mods public override IconUsage? Icon => OsuIcon.Dice; public override double ScoreMultiplier => 1; - [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(ModRandomSettingsControl))] + [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SeedSettingsControl))] public Bindable Seed { get; } = new Bindable { Default = null, Value = null }; - - private class ModRandomSettingsControl : SettingsItem - { - protected override Drawable CreateControl() => new SeedControl - { - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding { Top = 5 } - }; - - private sealed class SeedControl : CompositeDrawable, IHasCurrentValue - { - private readonly BindableWithCurrent current = new BindableWithCurrent(); - - public Bindable Current - { - get => current; - set - { - current.Current = value; - seedNumberBox.Text = value.Value.ToString(); - } - } - - private readonly OsuNumberBox seedNumberBox; - - public SeedControl() - { - AutoSizeAxes = Axes.Y; - - InternalChildren = new[] - { - new GridContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - ColumnDimensions = new[] - { - new Dimension(), - new Dimension(GridSizeMode.Absolute, 2), - new Dimension(GridSizeMode.Relative, 0.25f) - }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize) - }, - Content = new[] - { - new Drawable[] - { - seedNumberBox = new OsuNumberBox - { - RelativeSizeAxes = Axes.X, - CommitOnFocusLost = true - } - } - } - } - }; - - seedNumberBox.Current.BindValueChanged(e => - { - int? value = null; - - if (int.TryParse(e.NewValue, out var intVal)) - value = intVal; - - current.Value = value; - }); - } - - protected override void Update() - { - if (current.Value == null) - seedNumberBox.Text = current.Current.Value.ToString(); - } - } - } } } From 3309ab2be32441415d3432aa6d0476a8b1604bb0 Mon Sep 17 00:00:00 2001 From: emu1337 Date: Sun, 13 Jun 2021 15:18:35 +0200 Subject: [PATCH 0047/2442] balance changes --- .../Difficulty/OsuDifficultyCalculator.cs | 4 +- .../Difficulty/OsuPerformanceCalculator.cs | 7 ++- .../Difficulty/Skills/Aim.cs | 4 +- .../Difficulty/Skills/OsuSkill.cs | 44 +++++++++++++++++++ .../Difficulty/Skills/Speed.cs | 2 +- 5 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Difficulty/Skills/OsuSkill.cs diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 75d6786d95..b83e504a7a 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -32,8 +32,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (beatmap.HitObjects.Count == 0) return new OsuDifficultyAttributes { Mods = mods, Skills = skills }; - double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; - double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; + double aimRating = Math.Sqrt((skills[0] as OsuSkill).OsuDifficultyValue()) * difficulty_multiplier; + double speedRating = Math.Sqrt((skills[1] as OsuSkill).OsuDifficultyValue()) * difficulty_multiplier; double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2; HitWindows hitWindows = new OsuHitWindows(); diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 44a9dd2f1f..90ded2b837 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -106,22 +106,25 @@ namespace osu.Game.Rulesets.Osu.Difficulty else if (Attributes.ApproachRate < 8.0) approachRateFactor += 0.01 * (8.0 - Attributes.ApproachRate); - aimValue *= 1.0 + Math.Min(approachRateFactor, approachRateFactor * (totalHits / 1000.0)); + approachRateFactor = 1.0 + Math.Min(approachRateFactor, approachRateFactor * (totalHits / 1000.0)); // We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR. if (mods.Any(h => h is OsuModHidden)) aimValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); + double flashlightBonus = 1.0; if (mods.Any(h => h is OsuModFlashlight)) { // Apply object-based bonus for flashlight. - aimValue *= 1.0 + 0.35 * Math.Min(1.0, totalHits / 200.0) + + flashlightBonus = 1.0 + 0.35 * Math.Min(1.0, totalHits / 200.0) + (totalHits > 200 ? 0.3 * Math.Min(1.0, (totalHits - 200) / 300.0) + (totalHits > 500 ? (totalHits - 500) / 1200.0 : 0.0) : 0.0); } + aimValue *= Math.Max(flashlightBonus, approachRateFactor); + // Scale the aim value with accuracy _slightly_ aimValue *= 0.5 + accuracy / 2.0; // It is important to also consider accuracy difficulty when doing that diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index cb819ec090..b7bb10a87e 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// /// Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances. /// - public class Aim : StrainSkill + public class Aim : OsuSkill { private const double angle_bonus_begin = Math.PI / 3; private const double timing_threshold = 107; @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills Math.Max(osuPrevious.JumpDistance - scale, 0) * Math.Pow(Math.Sin(osuCurrent.Angle.Value - angle_bonus_begin), 2) * Math.Max(osuCurrent.JumpDistance - scale, 0)); - result = 1.5 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, osuPrevious.StrainTime); + result = 1.35 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, osuPrevious.StrainTime); } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuSkill.cs new file mode 100644 index 0000000000..d113f7135f --- /dev/null +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuSkill.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Text; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Difficulty.Skills; +using osu.Game.Rulesets.Mods; +using System.Linq; + +namespace osu.Game.Rulesets.Osu.Difficulty.Skills +{ + public abstract class OsuSkill : StrainSkill + { + public OsuSkill(Mod[] mods) : base(mods) + { + } + + public double OsuDifficultyValue() + { + double difficulty = 0; + double weight = 1; + + double strainMultiplier; + List strains = GetCurrentStrainPeaks().OrderByDescending(d => d).ToList(); + + double baseLine = 0.6; + + for (int i = 0; i <= 9; i++) + { + strainMultiplier = baseLine + Math.Log10(i+1) * (1.0 - baseLine); + strains[i] = strains[i] * strainMultiplier; + } + + // Difficulty is the weighted sum of the highest strains from every section. + // We're sorting from highest to lowest strain. + foreach (double strain in strains.OrderByDescending(d => d)) + { + difficulty += strain * weight; + weight *= DecayWeight; + } + + return difficulty * 1.06; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index fbac080fc6..975a633bad 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// /// Represents the skill required to press keys with regards to keeping up with the speed at which objects need to be hit. /// - public class Speed : StrainSkill + public class Speed : OsuSkill { private const double single_spacing_threshold = 125; From 4c949d9829ae58eddf0e7df9227d66b0fc189bcd Mon Sep 17 00:00:00 2001 From: emu1337 Date: Sun, 13 Jun 2021 21:20:08 +0200 Subject: [PATCH 0048/2442] reduced diffspike nerf --- osu.Game.Rulesets.Osu/Difficulty/Skills/OsuSkill.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuSkill.cs index d113f7135f..d52b95d3ec 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuSkill.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double strainMultiplier; List strains = GetCurrentStrainPeaks().OrderByDescending(d => d).ToList(); - double baseLine = 0.6; + double baseLine = 0.68; for (int i = 0; i <= 9; i++) { From 04c0db6dceb94b147c5dd8dad095ed0d97e39adc Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Mon, 14 Jun 2021 21:34:34 +0800 Subject: [PATCH 0049/2442] Code cleanup --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 161 ++++++++++----------- osu.Game/Rulesets/Mods/IHasSeed.cs | 54 +++---- osu.Game/Rulesets/Mods/ModRandom.cs | 5 - 3 files changed, 105 insertions(+), 115 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 04a7365148..0d123a4ce3 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -30,7 +30,7 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Mods { public class OsuModTarget : ModWithVisibilityAdjustment, IApplicableToDrawableRuleset, - IApplicableToHealthProcessor, IApplicableToDifficulty, IApplicableFailOverride, IHasSeed + IApplicableToHealthProcessor, IApplicableToDifficulty, IApplicableFailOverride, IHasSeed { public override string Name => "Target"; public override string Acronym => "TP"; @@ -57,11 +57,11 @@ namespace osu.Game.Rulesets.Osu.Mods // Sudden death healthProcessor.FailConditions += (_, result) => result.Type.AffectsCombo() - && !result.IsHit; + && !result.IsHit; } // Maximum distance to jump - public const float MAX_DISTANCE = 250f; + private const float max_distance = 250f; public override void ApplyToBeatmap(IBeatmap beatmap) { @@ -69,76 +69,68 @@ namespace osu.Game.Rulesets.Osu.Mods var rng = new Random(Seed.Value.GetValueOrDefault()); - float nextSingle(float max = 1f) - { - return (float)(rng.NextDouble() * max); - } + float nextSingle(float max = 1f) => (float)(rng.NextDouble() * max); var osuBeatmap = (OsuBeatmap)beatmap; var origHitObjects = osuBeatmap.HitObjects.OrderBy(x => x.StartTime).ToList(); // Only place circles between startTime and endTime var startTime = origHitObjects.First().StartTime; - double endTime; var endObj = origHitObjects.Last(); - switch (endObj) + var endTime = endObj switch { - case Slider slider: - endTime = slider.EndTime; - break; - case Spinner spinner: - endTime = spinner.EndTime; - break; - default: - endTime = endObj.StartTime; - break; - } + Slider slider => slider.EndTime, + Spinner spinner => spinner.EndTime, + _ => endObj.StartTime + }; // Generate the beats var beats = osuBeatmap.ControlPointInfo.TimingPoints - .Where(x => Precision.AlmostBigger(endTime, x.Time)) - .SelectMany(tp => - { - var tpBeats = new List(); - var currentTime = tp.Time; - while (Precision.AlmostBigger(endTime, currentTime) && osuBeatmap.ControlPointInfo.TimingPointAt(currentTime) == tp) - { - tpBeats.Add(currentTime); - currentTime += tp.BeatLength; - } - return tpBeats; - }) - // Remove beats that are before startTime - .Where(x => Precision.AlmostBigger(x, startTime)) - // Remove beats during breaks - .Where(x => !osuBeatmap.Breaks.Any(b => - Precision.AlmostBigger(x, b.StartTime) - && Precision.AlmostBigger(origHitObjects.First(y => Precision.AlmostBigger(y.StartTime, b.EndTime)).StartTime, x) - )) - .ToList(); + .Where(x => Precision.AlmostBigger(endTime, x.Time)) + .SelectMany(tp => + { + var tpBeats = new List(); + var currentTime = tp.Time; + + while (Precision.AlmostBigger(endTime, currentTime) && osuBeatmap.ControlPointInfo.TimingPointAt(currentTime) == tp) + { + tpBeats.Add(currentTime); + currentTime += tp.BeatLength; + } + + return tpBeats; + }) + // Remove beats that are before startTime + .Where(x => Precision.AlmostBigger(x, startTime)) + // Remove beats during breaks + .Where(x => !osuBeatmap.Breaks.Any(b => + Precision.AlmostBigger(x, b.StartTime) + && Precision.AlmostBigger(origHitObjects.First(y => Precision.AlmostBigger(y.StartTime, b.EndTime)).StartTime, x) + )) + .ToList(); // Generate a hit circle for each beat var hitObjects = beats - // Remove beats that are too close to the next one (e.g. due to timing point changes) - .Where((x, idx) => - { - if (idx == beats.Count - 1) return true; - if (Precision.AlmostBigger(osuBeatmap.ControlPointInfo.TimingPointAt(x).BeatLength / 2, beats[idx + 1] - x)) - return false; - return true; - }) - .Select(x => - { - var newCircle = new HitCircle(); - newCircle.ApplyDefaults(osuBeatmap.ControlPointInfo, osuBeatmap.BeatmapInfo.BaseDifficulty); - newCircle.StartTime = x; - return (OsuHitObject)newCircle; - }).ToList(); + // Remove beats that are too close to the next one (e.g. due to timing point changes) + .Where((x, idx) => + { + if (idx == beats.Count - 1) return true; + + return !Precision.AlmostBigger(osuBeatmap.ControlPointInfo.TimingPointAt(x).BeatLength / 2, beats[idx + 1] - x); + }) + .Select(x => + { + var newCircle = new HitCircle(); + newCircle.ApplyDefaults(osuBeatmap.ControlPointInfo, osuBeatmap.BeatmapInfo.BaseDifficulty); + newCircle.StartTime = x; + return (OsuHitObject)newCircle; + }).ToList(); // Add hit samples to the circles for (int i = 0; i < hitObjects.Count; i++) { var x = hitObjects[i]; var samples = getSamplesAtTime(origHitObjects, x.StartTime); + if (samples == null) { if (i > 0) @@ -155,11 +147,11 @@ namespace osu.Game.Rulesets.Osu.Mods hitObjects.ForEach(x => { var origObj = origHitObjects.FindLast(y => Precision.AlmostBigger(x.StartTime, y.StartTime)); - if (origObj == null) x.ComboIndex = 0; - else x.ComboIndex = origObj.ComboIndex; + x.ComboIndex = origObj?.ComboIndex ?? 0; }); // Then reprocess them to ensure continuity in the combo indices and add indices in current combo var combos = hitObjects.GroupBy(x => x.ComboIndex).ToList(); + for (int i = 0; i < combos.Count; i++) { var group = combos[i].ToList(); @@ -176,20 +168,22 @@ namespace osu.Game.Rulesets.Osu.Mods // Position all hit circles var direction = MathHelper.TwoPi * nextSingle(); + for (int i = 0; i < hitObjects.Count; i++) { var x = hitObjects[i]; + if (i == 0) { x.Position = new Vector2(nextSingle(OsuPlayfield.BASE_SIZE.X), nextSingle(OsuPlayfield.BASE_SIZE.Y)); } else { - var distance = Math.Min(MAX_DISTANCE, 40f * (float)Math.Pow(1.05, x.ComboIndex)); + var distance = Math.Min(max_distance, 40f * (float)Math.Pow(1.05, x.ComboIndex)); var relativePos = new Vector2( - distance * (float)Math.Cos(direction), - distance * (float)Math.Sin(direction) - ); + distance * (float)Math.Cos(direction), + distance * (float)Math.Sin(direction) + ); relativePos = getRotatedVector(hitObjects[i - 1].Position, relativePos); direction = (float)Math.Atan2(relativePos.Y, relativePos.X); @@ -209,7 +203,7 @@ namespace osu.Game.Rulesets.Osu.Mods if (x.LastInCombo) direction = MathHelper.TwoPi * nextSingle(); else - direction += distance / MAX_DISTANCE * (nextSingle() * MathHelper.TwoPi - MathHelper.Pi); + direction += distance / max_distance * (nextSingle() * MathHelper.TwoPi - MathHelper.Pi); } } @@ -227,25 +221,24 @@ namespace osu.Game.Rulesets.Osu.Mods /// The list of hit objects in a beatmap, ordered by StartTime /// The point in time to get samples for /// Hit samples - private IList getSamplesAtTime(List hitObjects, double time) + private IList getSamplesAtTime(IEnumerable hitObjects, double time) { var sampleObj = hitObjects.FirstOrDefault(x => { if (Precision.AlmostEquals(time, x.StartTime)) return true; - if (x is Slider slider - && Precision.AlmostBigger(time, slider.StartTime) - && Precision.AlmostBigger(slider.EndTime, time)) - { - if (Precision.AlmostEquals((time - slider.StartTime) % slider.SpanDuration, 0)) - { - return true; - } - } - return false; + + if (!(x is Slider s)) + return false; + if (!Precision.AlmostBigger(time, s.StartTime) + || !Precision.AlmostBigger(s.EndTime, time)) + return false; + + return Precision.AlmostEquals((time - s.StartTime) % s.SpanDuration, 0); }); if (sampleObj == null) return null; - IList samples = null; + IList samples; + if (sampleObj is Slider slider) { samples = slider.NodeSamples[(int)Math.Round((time - slider.StartTime) % slider.SpanDuration)]; @@ -254,6 +247,7 @@ namespace osu.Game.Rulesets.Osu.Mods { samples = sampleObj.Samples; } + return samples; } @@ -269,19 +263,18 @@ namespace osu.Game.Rulesets.Osu.Mods var h = (OsuHitObject)drawable.HitObject; // apply grow and fade effect - if (drawable is DrawableHitCircle circle) + if (!(drawable is DrawableHitCircle circle)) return; + + using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) { - using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) - { - // todo: this doesn't feel quite right yet - drawable.ScaleTo(0.4f) + // todo: this doesn't feel quite right yet + drawable.ScaleTo(0.4f) .Then().ScaleTo(1.6f, h.TimePreempt * 2); - drawable.FadeTo(0.5f) + drawable.FadeTo(0.5f) .Then().Delay(h.TimeFadeIn).FadeTo(1f); - // remove approach circles - circle.ApproachCircle.Hide(); - } + // remove approach circles + circle.ApproachCircle.Hide(); } } @@ -347,7 +340,7 @@ namespace osu.Game.Rulesets.Osu.Mods } /// - /// Rotates vector "initial" towards vector "destinantion" + /// Rotates vector "initial" towards vector "destination" /// /// Vector to rotate to "destination" /// Vector "initial" should be rotated to @@ -399,7 +392,7 @@ namespace osu.Game.Rulesets.Osu.Mods { InternalChildren = new Drawable[] { - sample = new PausableSkinnableSound(new SampleInfo("spinnerbonus")), // todo: use another sample? + sample = new PausableSkinnableSound(new SampleInfo("spinnerbonus")) // todo: use another sample? }; } diff --git a/osu.Game/Rulesets/Mods/IHasSeed.cs b/osu.Game/Rulesets/Mods/IHasSeed.cs index 45a806e80c..13d59a9855 100644 --- a/osu.Game/Rulesets/Mods/IHasSeed.cs +++ b/osu.Game/Rulesets/Mods/IHasSeed.cs @@ -5,7 +5,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; -using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; @@ -18,11 +17,14 @@ namespace osu.Game.Rulesets.Mods public class SeedSettingsControl : SettingsItem { - protected override Drawable CreateControl() => new SeedControl + protected override Drawable CreateControl() { - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding { Top = 5 } - }; + return new SeedControl + { + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding { Top = 5 } + }; + } private sealed class SeedControl : CompositeDrawable, IHasCurrentValue { @@ -46,33 +48,33 @@ namespace osu.Game.Rulesets.Mods InternalChildren = new[] { - new GridContainer + new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ColumnDimensions = new[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - ColumnDimensions = new[] + new Dimension(), + new Dimension(GridSizeMode.Absolute, 2), + new Dimension(GridSizeMode.Relative, 0.25f) + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize) + }, + Content = new[] + { + new Drawable[] { - new Dimension(), - new Dimension(GridSizeMode.Absolute, 2), - new Dimension(GridSizeMode.Relative, 0.25f) - }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize) - }, - Content = new[] - { - new Drawable[] + seedNumberBox = new OsuNumberBox { - seedNumberBox = new OsuNumberBox - { - RelativeSizeAxes = Axes.X, - CommitOnFocusLost = true - } + RelativeSizeAxes = Axes.X, + CommitOnFocusLost = true } } } - }; + } + }; seedNumberBox.Current.BindValueChanged(e => { diff --git a/osu.Game/Rulesets/Mods/ModRandom.cs b/osu.Game/Rulesets/Mods/ModRandom.cs index eec66161ff..61297c162d 100644 --- a/osu.Game/Rulesets/Mods/ModRandom.cs +++ b/osu.Game/Rulesets/Mods/ModRandom.cs @@ -2,14 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.UserInterface; using osu.Game.Configuration; using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays.Settings; namespace osu.Game.Rulesets.Mods { From fea7b029aa31da7037d9aa91d330412ab800dd40 Mon Sep 17 00:00:00 2001 From: emu1337 Date: Mon, 14 Jun 2021 19:18:49 +0200 Subject: [PATCH 0050/2442] refactored diffspike nerf --- .../Difficulty/OsuDifficultyCalculator.cs | 4 ++-- osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs | 2 +- .../Skills/{OsuSkill.cs => OsuStrainSkill.cs} | 16 +++++++++------- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 2 +- 4 files changed, 13 insertions(+), 11 deletions(-) rename osu.Game.Rulesets.Osu/Difficulty/Skills/{OsuSkill.cs => OsuStrainSkill.cs} (64%) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index b83e504a7a..fdcc6f44a6 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -32,8 +32,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (beatmap.HitObjects.Count == 0) return new OsuDifficultyAttributes { Mods = mods, Skills = skills }; - double aimRating = Math.Sqrt((skills[0] as OsuSkill).OsuDifficultyValue()) * difficulty_multiplier; - double speedRating = Math.Sqrt((skills[1] as OsuSkill).OsuDifficultyValue()) * difficulty_multiplier; + double aimRating = Math.Sqrt((skills[0] as OsuStrainSkill).OsuDifficultyValue()) * difficulty_multiplier; + double speedRating = Math.Sqrt((skills[1] as OsuStrainSkill).OsuDifficultyValue()) * difficulty_multiplier; double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2; HitWindows hitWindows = new OsuHitWindows(); diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index b7bb10a87e..a95bf4fa94 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// /// Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances. /// - public class Aim : OsuSkill + public class Aim : OsuStrainSkill { private const double angle_bonus_begin = Math.PI / 3; private const double timing_threshold = 107; diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs similarity index 64% rename from osu.Game.Rulesets.Osu/Difficulty/Skills/OsuSkill.cs rename to osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index d52b95d3ec..f1c7ae3403 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -8,9 +8,13 @@ using System.Linq; namespace osu.Game.Rulesets.Osu.Difficulty.Skills { - public abstract class OsuSkill : StrainSkill + public abstract class OsuStrainSkill : StrainSkill { - public OsuSkill(Mod[] mods) : base(mods) + protected virtual int ReducedSectionCount => 9; + protected virtual double ReducedStrainBaseline => 0.68; + protected virtual double DifficultyMultiplier => 1.06; + + public OsuStrainSkill(Mod[] mods) : base(mods) { } @@ -22,11 +26,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double strainMultiplier; List strains = GetCurrentStrainPeaks().OrderByDescending(d => d).ToList(); - double baseLine = 0.68; - - for (int i = 0; i <= 9; i++) + for (int i = 0; i < ReducedSectionCount; i++) { - strainMultiplier = baseLine + Math.Log10(i+1) * (1.0 - baseLine); + strainMultiplier = ReducedStrainBaseline + Math.Log10(i * 9.0 / ReducedSectionCount + 1) * (1.0 - ReducedStrainBaseline); strains[i] = strains[i] * strainMultiplier; } @@ -38,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills weight *= DecayWeight; } - return difficulty * 1.06; + return difficulty * DifficultyMultiplier; } } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 975a633bad..0d8400badc 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// /// Represents the skill required to press keys with regards to keeping up with the speed at which objects need to be hit. /// - public class Speed : OsuSkill + public class Speed : OsuStrainSkill { private const double single_spacing_threshold = 125; From e987a511bad01745370702f7104cc955e165ce1d Mon Sep 17 00:00:00 2001 From: emu1337 Date: Mon, 14 Jun 2021 19:22:35 +0200 Subject: [PATCH 0051/2442] diffspike & wide angle balance --- osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs | 2 +- osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index a95bf4fa94..e063276ec5 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills Math.Max(osuPrevious.JumpDistance - scale, 0) * Math.Pow(Math.Sin(osuCurrent.Angle.Value - angle_bonus_begin), 2) * Math.Max(osuCurrent.JumpDistance - scale, 0)); - result = 1.35 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, osuPrevious.StrainTime); + result = 1.4 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, osuPrevious.StrainTime); } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index f1c7ae3403..136c4ae309 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -10,9 +10,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { public abstract class OsuStrainSkill : StrainSkill { - protected virtual int ReducedSectionCount => 9; - protected virtual double ReducedStrainBaseline => 0.68; - protected virtual double DifficultyMultiplier => 1.06; + protected virtual int ReducedSectionCount => 10; + protected virtual double ReducedStrainBaseline => 0.7; + protected virtual double DifficultyMultiplier => 1.08; public OsuStrainSkill(Mod[] mods) : base(mods) { From 4ffff06dcbc0ac52d49d70643c5bd00d881d8c1c Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Tue, 15 Jun 2021 11:06:56 +0800 Subject: [PATCH 0052/2442] Break ApplyToBeatmap into subroutines --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 116 ++++++++++++--------- 1 file changed, 67 insertions(+), 49 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 0d123a4ce3..3bb4b0419f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -67,14 +67,31 @@ namespace osu.Game.Rulesets.Osu.Mods { Seed.Value ??= RNG.Next(); - var rng = new Random(Seed.Value.GetValueOrDefault()); - - float nextSingle(float max = 1f) => (float)(rng.NextDouble() * max); - var osuBeatmap = (OsuBeatmap)beatmap; var origHitObjects = osuBeatmap.HitObjects.OrderBy(x => x.StartTime).ToList(); - // Only place circles between startTime and endTime + var hitObjects = generateBeats(osuBeatmap, origHitObjects) + .Select(x => + { + var newCircle = new HitCircle(); + newCircle.ApplyDefaults(osuBeatmap.ControlPointInfo, osuBeatmap.BeatmapInfo.BaseDifficulty); + newCircle.StartTime = x; + return (OsuHitObject)newCircle; + }).ToList(); + + addHitSamples(hitObjects, origHitObjects); + + fixComboInfo(hitObjects, origHitObjects); + + randomizeCirclePos(hitObjects); + + osuBeatmap.HitObjects = hitObjects; + + base.ApplyToBeatmap(beatmap); + } + + private IEnumerable generateBeats(IBeatmap beatmap, IReadOnlyCollection origHitObjects) + { var startTime = origHitObjects.First().StartTime; var endObj = origHitObjects.Last(); var endTime = endObj switch @@ -84,48 +101,45 @@ namespace osu.Game.Rulesets.Osu.Mods _ => endObj.StartTime }; - // Generate the beats - var beats = osuBeatmap.ControlPointInfo.TimingPoints - .Where(x => Precision.AlmostBigger(endTime, x.Time)) - .SelectMany(tp => - { - var tpBeats = new List(); - var currentTime = tp.Time; + var beats = beatmap.ControlPointInfo.TimingPoints + .Where(x => Precision.AlmostBigger(endTime, x.Time)) + .SelectMany(tp => + { + var tpBeats = new List(); + var currentTime = tp.Time; - while (Precision.AlmostBigger(endTime, currentTime) && osuBeatmap.ControlPointInfo.TimingPointAt(currentTime) == tp) - { - tpBeats.Add(currentTime); - currentTime += tp.BeatLength; - } + while (Precision.AlmostBigger(endTime, currentTime) && beatmap.ControlPointInfo.TimingPointAt(currentTime) == tp) + { + tpBeats.Add(currentTime); + currentTime += tp.BeatLength; + } - return tpBeats; - }) - // Remove beats that are before startTime - .Where(x => Precision.AlmostBigger(x, startTime)) - // Remove beats during breaks - .Where(x => !osuBeatmap.Breaks.Any(b => - Precision.AlmostBigger(x, b.StartTime) - && Precision.AlmostBigger(origHitObjects.First(y => Precision.AlmostBigger(y.StartTime, b.EndTime)).StartTime, x) - )) - .ToList(); - // Generate a hit circle for each beat - var hitObjects = beats - // Remove beats that are too close to the next one (e.g. due to timing point changes) - .Where((x, idx) => - { - if (idx == beats.Count - 1) return true; + return tpBeats; + }) + .Where(x => Precision.AlmostBigger(x, startTime)) + // Remove beats during breaks + .Where(x => !beatmap.Breaks.Any(b => + Precision.AlmostBigger(x, b.StartTime) + && Precision.AlmostBigger(origHitObjects.First(y => Precision.AlmostBigger(y.StartTime, b.EndTime)).StartTime, x) + )) + .ToList(); - return !Precision.AlmostBigger(osuBeatmap.ControlPointInfo.TimingPointAt(x).BeatLength / 2, beats[idx + 1] - x); - }) - .Select(x => - { - var newCircle = new HitCircle(); - newCircle.ApplyDefaults(osuBeatmap.ControlPointInfo, osuBeatmap.BeatmapInfo.BaseDifficulty); - newCircle.StartTime = x; - return (OsuHitObject)newCircle; - }).ToList(); + // Remove beats that are too close to the next one (e.g. due to timing point changes) + for (int i = beats.Count - 2; i >= 0; i--) + { + var beat = beats[i]; - // Add hit samples to the circles + if (Precision.AlmostBigger(beatmap.ControlPointInfo.TimingPointAt(beat).BeatLength / 2, beats[i + 1] - beat)) + { + beats.RemoveAt(i); + } + } + + return beats; + } + + private void addHitSamples(IReadOnlyList hitObjects, IReadOnlyCollection origHitObjects) + { for (int i = 0; i < hitObjects.Count; i++) { var x = hitObjects[i]; @@ -141,8 +155,10 @@ namespace osu.Game.Rulesets.Osu.Mods x.Samples = samples; } } + } - // Process combo numbers + private void fixComboInfo(List hitObjects, List origHitObjects) + { // First follow the combo indices in the original beatmap hitObjects.ForEach(x => { @@ -165,8 +181,14 @@ namespace osu.Game.Rulesets.Osu.Mods x.IndexInCurrentCombo = j; } } + } + + private void randomizeCirclePos(IReadOnlyList hitObjects) + { + var rng = new Random(Seed.Value.GetValueOrDefault()); + + float nextSingle(float max = 1f) => (float)(rng.NextDouble() * max); - // Position all hit circles var direction = MathHelper.TwoPi * nextSingle(); for (int i = 0; i < hitObjects.Count; i++) @@ -206,10 +228,6 @@ namespace osu.Game.Rulesets.Osu.Mods direction += distance / max_distance * (nextSingle() * MathHelper.TwoPi - MathHelper.Pi); } } - - osuBeatmap.HitObjects = hitObjects; - - base.ApplyToBeatmap(beatmap); } /// From be68950c307f1ffa355b1e6fef021b27f73a36c4 Mon Sep 17 00:00:00 2001 From: emu1337 Date: Wed, 16 Jun 2021 03:34:46 +0200 Subject: [PATCH 0053/2442] refactoring --- .../Difficulty/OsuDifficultyCalculator.cs | 4 ++-- .../Difficulty/OsuPerformanceCalculator.cs | 9 +++++---- osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs | 1 - .../Difficulty/Skills/OsuStrainSkill.cs | 17 +++++++++-------- .../Difficulty/Skills/Speed.cs | 1 - .../Rulesets/Difficulty/Skills/StrainSkill.cs | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index fdcc6f44a6..75d6786d95 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -32,8 +32,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (beatmap.HitObjects.Count == 0) return new OsuDifficultyAttributes { Mods = mods, Skills = skills }; - double aimRating = Math.Sqrt((skills[0] as OsuStrainSkill).OsuDifficultyValue()) * difficulty_multiplier; - double speedRating = Math.Sqrt((skills[1] as OsuStrainSkill).OsuDifficultyValue()) * difficulty_multiplier; + double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; + double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2; HitWindows hitWindows = new OsuHitWindows(); diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 90ded2b837..74840853c0 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -113,14 +113,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty aimValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); double flashlightBonus = 1.0; + if (mods.Any(h => h is OsuModFlashlight)) { // Apply object-based bonus for flashlight. flashlightBonus = 1.0 + 0.35 * Math.Min(1.0, totalHits / 200.0) + - (totalHits > 200 - ? 0.3 * Math.Min(1.0, (totalHits - 200) / 300.0) + - (totalHits > 500 ? (totalHits - 500) / 1200.0 : 0.0) - : 0.0); + (totalHits > 200 + ? 0.3 * Math.Min(1.0, (totalHits - 200) / 300.0) + + (totalHits > 500 ? (totalHits - 500) / 1200.0 : 0.0) + : 0.0); } aimValue *= Math.Max(flashlightBonus, approachRateFactor); diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index e063276ec5..16a18cbcb9 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -3,7 +3,6 @@ using System; using osu.Game.Rulesets.Difficulty.Preprocessing; -using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Objects; diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index 136c4ae309..93101bf3c3 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -1,7 +1,8 @@ -using System; +// 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.Text; -using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; using System.Linq; @@ -14,22 +15,22 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected virtual double ReducedStrainBaseline => 0.7; protected virtual double DifficultyMultiplier => 1.08; - public OsuStrainSkill(Mod[] mods) : base(mods) + protected OsuStrainSkill(Mod[] mods) + : base(mods) { } - public double OsuDifficultyValue() + public override double DifficultyValue() { double difficulty = 0; double weight = 1; - double strainMultiplier; List strains = GetCurrentStrainPeaks().OrderByDescending(d => d).ToList(); + // We are reducing the highest strains first to account for extreme difficulty spikes for (int i = 0; i < ReducedSectionCount; i++) { - strainMultiplier = ReducedStrainBaseline + Math.Log10(i * 9.0 / ReducedSectionCount + 1) * (1.0 - ReducedStrainBaseline); - strains[i] = strains[i] * strainMultiplier; + strains[i] *= ReducedStrainBaseline + Math.Log10(i * 9.0 / ReducedSectionCount + 1) * (1.0 - ReducedStrainBaseline); } // Difficulty is the weighted sum of the highest strains from every section. diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 0d8400badc..872bb0601d 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -3,7 +3,6 @@ using System; using osu.Game.Rulesets.Difficulty.Preprocessing; -using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Objects; diff --git a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs index 71cee36812..d4fcefab9b 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs @@ -109,7 +109,7 @@ namespace osu.Game.Rulesets.Difficulty.Skills /// /// Returns the calculated difficulty value representing all s that have been processed up to this point. /// - public sealed override double DifficultyValue() + public override double DifficultyValue() { double difficulty = 0; double weight = 1; From 18fe05b7b5856e210fa2f91f368d2ec317bd777f Mon Sep 17 00:00:00 2001 From: emu1337 Date: Wed, 16 Jun 2021 15:13:46 +0200 Subject: [PATCH 0054/2442] diffspikes balance --- osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs | 4 ++-- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index 93101bf3c3..565cc5805f 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -12,8 +12,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills public abstract class OsuStrainSkill : StrainSkill { protected virtual int ReducedSectionCount => 10; - protected virtual double ReducedStrainBaseline => 0.7; - protected virtual double DifficultyMultiplier => 1.08; + protected virtual double ReducedStrainBaseline => 0.75; + protected virtual double DifficultyMultiplier => 1.06; protected OsuStrainSkill(Mod[] mods) : base(mods) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 872bb0601d..f0eb199e5f 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override double SkillMultiplier => 1400; protected override double StrainDecayBase => 0.3; + protected override int ReducedSectionCount => 5; + protected override double DifficultyMultiplier => 1.04; private const double min_speed_bonus = 75; // ~200BPM private const double max_speed_bonus = 45; // ~330BPM From 41662a16431eba5ddee0c75f7dbc870f1537fda1 Mon Sep 17 00:00:00 2001 From: emu1337 Date: Wed, 16 Jun 2021 19:54:22 +0200 Subject: [PATCH 0055/2442] refactored for clarity --- osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index 565cc5805f..56c00d263d 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; using System.Linq; +using osu.Framework.Utils; namespace osu.Game.Rulesets.Osu.Difficulty.Skills { @@ -30,7 +31,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills // We are reducing the highest strains first to account for extreme difficulty spikes for (int i = 0; i < ReducedSectionCount; i++) { - strains[i] *= ReducedStrainBaseline + Math.Log10(i * 9.0 / ReducedSectionCount + 1) * (1.0 - ReducedStrainBaseline); + strains[i] *= ReducedStrainBaseline + + Math.Log10(Interpolation.Lerp(1, 10, Math.Clamp((float)i / ReducedSectionCount, 0, 1))) + * (1.0 - ReducedStrainBaseline); } // Difficulty is the weighted sum of the highest strains from every section. From 2665a873f87e7bf2528f26507a0eb4f289b6a845 Mon Sep 17 00:00:00 2001 From: emu1337 Date: Wed, 16 Jun 2021 19:55:19 +0200 Subject: [PATCH 0056/2442] fixed an error with extremely short maps --- osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index 56c00d263d..5a40524e8a 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills List strains = GetCurrentStrainPeaks().OrderByDescending(d => d).ToList(); // We are reducing the highest strains first to account for extreme difficulty spikes - for (int i = 0; i < ReducedSectionCount; i++) + for (int i = 0; i < Math.Min(strains.Count, ReducedSectionCount); i++) { strains[i] *= ReducedStrainBaseline + Math.Log10(Interpolation.Lerp(1, 10, Math.Clamp((float)i / ReducedSectionCount, 0, 1))) From 14622f473442cdf43a9539f669903fd4f9ba3f57 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 17 Jun 2021 10:20:50 +0800 Subject: [PATCH 0057/2442] Improved guesstimations; fixed hit samples --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 3bb4b0419f..b748cdf4a2 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Osu.Mods while (Precision.AlmostBigger(endTime, currentTime) && beatmap.ControlPointInfo.TimingPointAt(currentTime) == tp) { - tpBeats.Add(currentTime); + tpBeats.Add(Math.Floor(currentTime)); currentTime += tp.BeatLength; } @@ -148,7 +148,7 @@ namespace osu.Game.Rulesets.Osu.Mods if (samples == null) { if (i > 0) - x.Samples = hitObjects[i - 1].Samples; + x.Samples = hitObjects[i - 1].Samples.Where(s => !HitSampleInfo.AllAdditions.Contains(s.Name)).ToList(); } else { @@ -289,7 +289,7 @@ namespace osu.Game.Rulesets.Osu.Mods drawable.ScaleTo(0.4f) .Then().ScaleTo(1.6f, h.TimePreempt * 2); drawable.FadeTo(0.5f) - .Then().Delay(h.TimeFadeIn).FadeTo(1f); + .Then().Delay(h.TimePreempt * 2 / 3).FadeTo(1f); // remove approach circles circle.ApproachCircle.Hide(); @@ -304,6 +304,7 @@ namespace osu.Game.Rulesets.Osu.Mods { // Decrease AR to increase preempt time difficulty.ApproachRate *= 0.5f; + difficulty.CircleSize *= 0.75f; } // The distances from the hit objects to the borders of the playfield they start to "turn around" and curve towards the middle. From f22beaeb5b4e63decc9fa0e0837920e6d496fbc2 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 17 Jun 2021 14:30:59 +0800 Subject: [PATCH 0058/2442] Increase distance between combos; pull circles closer to center --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index b748cdf4a2..e91b2cd9d7 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -193,15 +193,17 @@ namespace osu.Game.Rulesets.Osu.Mods for (int i = 0; i < hitObjects.Count; i++) { - var x = hitObjects[i]; + var obj = hitObjects[i]; if (i == 0) { - x.Position = new Vector2(nextSingle(OsuPlayfield.BASE_SIZE.X), nextSingle(OsuPlayfield.BASE_SIZE.Y)); + obj.Position = new Vector2(nextSingle(OsuPlayfield.BASE_SIZE.X), nextSingle(OsuPlayfield.BASE_SIZE.Y)); } else { - var distance = Math.Min(max_distance, 40f * (float)Math.Pow(1.05, x.ComboIndex)); + var distance = 40f * (float)Math.Pow(1.05, obj.ComboIndex); + if (obj.NewCombo) distance *= 1.5f; + distance = Math.Min(max_distance, distance); var relativePos = new Vector2( distance * (float)Math.Cos(direction), distance * (float)Math.Sin(direction) @@ -220,9 +222,9 @@ namespace osu.Game.Rulesets.Osu.Mods else if (newPosition.X > OsuPlayfield.BASE_SIZE.X) newPosition.X = OsuPlayfield.BASE_SIZE.X; - x.Position = newPosition; + obj.Position = newPosition; - if (x.LastInCombo) + if (obj.LastInCombo) direction = MathHelper.TwoPi * nextSingle(); else direction += distance / max_distance * (nextSingle() * MathHelper.TwoPi - MathHelper.Pi); @@ -354,7 +356,7 @@ namespace osu.Game.Rulesets.Osu.Mods return rotateVectorTowardsVector( posRelativeToPrev, Vector2.Subtract(playfieldMiddle, prevPosChanged), - relativeRotationDistance / 2 + relativeRotationDistance * 0.75f ); } From b7f43405fc3d9175db86cf165ced58a1a97ec057 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 17 Jun 2021 22:01:58 +0800 Subject: [PATCH 0059/2442] Dim circles instead of fade; improved hit samples; changed jump distance to be closer to cuttingedge --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 268 +++++++++++---------- 1 file changed, 138 insertions(+), 130 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index e91b2cd9d7..c9e43704e1 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -26,6 +26,7 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Skinning; using osuTK; +using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Mods { @@ -46,12 +47,23 @@ namespace osu.Game.Rulesets.Osu.Mods Value = null }; - public bool PerformFail() => true; - public bool RestartOnFail => false; public bool DisplayResultsOnFail => true; + // Maximum distance to jump + private const float max_distance = 250f; + + // The distances from the hit objects to the borders of the playfield they start to "turn around" and curve towards the middle. + // The closer the hit objects draw to the border, the sharper the turn + private const byte border_distance_x = 192; + private const byte border_distance_y = 144; + + public bool PerformFail() + { + return true; + } + public void ApplyToHealthProcessor(HealthProcessor healthProcessor) { // Sudden death @@ -60,9 +72,6 @@ namespace osu.Game.Rulesets.Osu.Mods && !result.IsHit; } - // Maximum distance to jump - private const float max_distance = 250f; - public override void ApplyToBeatmap(IBeatmap beatmap) { Seed.Value ??= RNG.Next(); @@ -90,6 +99,55 @@ namespace osu.Game.Rulesets.Osu.Mods base.ApplyToBeatmap(beatmap); } + public void ReadFromDifficulty(BeatmapDifficulty difficulty) + { + } + + public void ApplyToDifficulty(BeatmapDifficulty difficulty) + { + // Decrease AR to increase preempt time + difficulty.ApproachRate *= 0.5f; + difficulty.CircleSize *= 0.75f; + } + + // Background metronome + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + drawableRuleset.Overlays.Add(new TargetBeatContainer()); + } + + protected override void ApplyIncreasedVisibilityState(DrawableHitObject drawable, ArmedState state) + { + } + + protected override void ApplyNormalVisibilityState(DrawableHitObject drawable, ArmedState state) + { + if (!(drawable is DrawableHitCircle circle)) return; + + var h = (OsuHitObject)drawable.HitObject; + + using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) + { + drawable.ScaleTo(0.5f) + .Then().ScaleTo(1f, h.TimePreempt); + + var colour = drawable.Colour; + + var avgColour = colour.AverageColour.Linear; + drawable.FadeColour(new Color4(avgColour.R * 0.45f, avgColour.G * 0.45f, avgColour.B * 0.45f, avgColour.A)) + .Then().Delay(h.TimeFadeIn).FadeColour(colour); + + // remove approach circles + circle.ApproachCircle.Hide(); + } + } + + private static float map(float value, float fromLow, float fromHigh, float toLow, float toHigh) + { + return (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow; + } + private IEnumerable generateBeats(IBeatmap beatmap, IReadOnlyCollection origHitObjects) { var startTime = origHitObjects.First().StartTime; @@ -125,35 +183,43 @@ namespace osu.Game.Rulesets.Osu.Mods .ToList(); // Remove beats that are too close to the next one (e.g. due to timing point changes) - for (int i = beats.Count - 2; i >= 0; i--) + for (var i = beats.Count - 2; i >= 0; i--) { var beat = beats[i]; - if (Precision.AlmostBigger(beatmap.ControlPointInfo.TimingPointAt(beat).BeatLength / 2, beats[i + 1] - beat)) - { - beats.RemoveAt(i); - } + if (Precision.AlmostBigger(beatmap.ControlPointInfo.TimingPointAt(beat).BeatLength / 2, beats[i + 1] - beat)) beats.RemoveAt(i); } return beats; } - private void addHitSamples(IReadOnlyList hitObjects, IReadOnlyCollection origHitObjects) + private void addHitSamples(IEnumerable hitObjects, IReadOnlyList origHitObjects) { - for (int i = 0; i < hitObjects.Count; i++) + var lastSampleIdx = 0; + + foreach (var x in hitObjects) { - var x = hitObjects[i]; var samples = getSamplesAtTime(origHitObjects, x.StartTime); if (samples == null) { - if (i > 0) - x.Samples = hitObjects[i - 1].Samples.Where(s => !HitSampleInfo.AllAdditions.Contains(s.Name)).ToList(); + while (lastSampleIdx < origHitObjects.Count && origHitObjects[lastSampleIdx].StartTime <= x.StartTime) + lastSampleIdx++; + lastSampleIdx--; + + if (lastSampleIdx < 0 && lastSampleIdx >= origHitObjects.Count) continue; + + if (lastSampleIdx < origHitObjects.Count - 1) + { + // get samples from the next hit object if it is closer in time + if (origHitObjects[lastSampleIdx + 1].StartTime - x.StartTime < x.StartTime - origHitObjects[lastSampleIdx].StartTime) + lastSampleIdx++; + } + + x.Samples = origHitObjects[lastSampleIdx].Samples.Where(s => !HitSampleInfo.AllAdditions.Contains(s.Name)).ToList(); } else - { x.Samples = samples; - } } } @@ -168,13 +234,13 @@ namespace osu.Game.Rulesets.Osu.Mods // Then reprocess them to ensure continuity in the combo indices and add indices in current combo var combos = hitObjects.GroupBy(x => x.ComboIndex).ToList(); - for (int i = 0; i < combos.Count; i++) + for (var i = 0; i < combos.Count; i++) { var group = combos[i].ToList(); group.First().NewCombo = true; group.Last().LastInCombo = true; - for (int j = 0; j < group.Count; j++) + for (var j = 0; j < group.Count; j++) { var x = group[j]; x.ComboIndex = i; @@ -190,53 +256,52 @@ namespace osu.Game.Rulesets.Osu.Mods float nextSingle(float max = 1f) => (float)(rng.NextDouble() * max); var direction = MathHelper.TwoPi * nextSingle(); + var maxComboIndex = hitObjects.Last().ComboIndex; - for (int i = 0; i < hitObjects.Count; i++) + for (var i = 0; i < hitObjects.Count; i++) { var obj = hitObjects[i]; + var lastPos = i == 0 + ? Vector2.Divide(OsuPlayfield.BASE_SIZE, 2) + : hitObjects[i - 1].Position; - if (i == 0) - { - obj.Position = new Vector2(nextSingle(OsuPlayfield.BASE_SIZE.X), nextSingle(OsuPlayfield.BASE_SIZE.Y)); - } + var distance = map(obj.ComboIndex, 0, maxComboIndex, (float)obj.Radius, 333f); + if (obj.NewCombo) distance *= 1.5f; + if (obj.Kiai) distance *= 1.2f; + distance = Math.Min(max_distance, distance); + + var relativePos = new Vector2( + distance * (float)Math.Cos(direction), + distance * (float)Math.Sin(direction) + ); + relativePos = getRotatedVector(lastPos, relativePos); + direction = (float)Math.Atan2(relativePos.Y, relativePos.X); + + var newPosition = Vector2.Add(lastPos, relativePos); + + if (newPosition.Y < 0) + newPosition.Y = 0; + else if (newPosition.Y > OsuPlayfield.BASE_SIZE.Y) + newPosition.Y = OsuPlayfield.BASE_SIZE.Y; + if (newPosition.X < 0) + newPosition.X = 0; + else if (newPosition.X > OsuPlayfield.BASE_SIZE.X) + newPosition.X = OsuPlayfield.BASE_SIZE.X; + + obj.Position = newPosition; + + if (obj.LastInCombo) + direction = MathHelper.TwoPi * nextSingle(); else - { - var distance = 40f * (float)Math.Pow(1.05, obj.ComboIndex); - if (obj.NewCombo) distance *= 1.5f; - distance = Math.Min(max_distance, distance); - var relativePos = new Vector2( - distance * (float)Math.Cos(direction), - distance * (float)Math.Sin(direction) - ); - relativePos = getRotatedVector(hitObjects[i - 1].Position, relativePos); - direction = (float)Math.Atan2(relativePos.Y, relativePos.X); - - var newPosition = Vector2.Add(hitObjects[i - 1].Position, relativePos); - - if (newPosition.Y < 0) - newPosition.Y = 0; - else if (newPosition.Y > OsuPlayfield.BASE_SIZE.Y) - newPosition.Y = OsuPlayfield.BASE_SIZE.Y; - if (newPosition.X < 0) - newPosition.X = 0; - else if (newPosition.X > OsuPlayfield.BASE_SIZE.X) - newPosition.X = OsuPlayfield.BASE_SIZE.X; - - obj.Position = newPosition; - - if (obj.LastInCombo) - direction = MathHelper.TwoPi * nextSingle(); - else - direction += distance / max_distance * (nextSingle() * MathHelper.TwoPi - MathHelper.Pi); - } + direction += distance / max_distance * (nextSingle() * MathHelper.TwoPi - MathHelper.Pi); } } /// - /// Get samples (if any) for a specific point in time. + /// Get samples (if any) for a specific point in time. /// /// - /// Samples will be returned if a hit circle or a slider node exists at that point of time. + /// Samples will be returned if a hit circle or a slider node exists at that point of time. /// /// The list of hit objects in a beatmap, ordered by StartTime /// The point in time to get samples for @@ -260,62 +325,15 @@ namespace osu.Game.Rulesets.Osu.Mods IList samples; if (sampleObj is Slider slider) - { samples = slider.NodeSamples[(int)Math.Round((time - slider.StartTime) % slider.SpanDuration)]; - } else - { samples = sampleObj.Samples; - } return samples; } - protected override void ApplyIncreasedVisibilityState(DrawableHitObject drawable, ArmedState state) - { - } - - protected override void ApplyNormalVisibilityState(DrawableHitObject drawable, ArmedState state) - { - if (drawable is DrawableSpinner) - return; - - var h = (OsuHitObject)drawable.HitObject; - - // apply grow and fade effect - if (!(drawable is DrawableHitCircle circle)) return; - - using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) - { - // todo: this doesn't feel quite right yet - drawable.ScaleTo(0.4f) - .Then().ScaleTo(1.6f, h.TimePreempt * 2); - drawable.FadeTo(0.5f) - .Then().Delay(h.TimePreempt * 2 / 3).FadeTo(1f); - - // remove approach circles - circle.ApproachCircle.Hide(); - } - } - - public void ReadFromDifficulty(BeatmapDifficulty difficulty) - { - } - - public void ApplyToDifficulty(BeatmapDifficulty difficulty) - { - // Decrease AR to increase preempt time - difficulty.ApproachRate *= 0.5f; - difficulty.CircleSize *= 0.75f; - } - - // The distances from the hit objects to the borders of the playfield they start to "turn around" and curve towards the middle. - // The closer the hit objects draw to the border, the sharper the turn - private const byte border_distance_x = 192; - private const byte border_distance_y = 144; - /// - /// Determines the position of the current hit object relative to the previous one. + /// Determines the position of the current hit object relative to the previous one. /// /// The position of the current hit object relative to the previous one private Vector2 getRotatedVector(Vector2 prevPosChanged, Vector2 posRelativeToPrev) @@ -356,16 +374,19 @@ namespace osu.Game.Rulesets.Osu.Mods return rotateVectorTowardsVector( posRelativeToPrev, Vector2.Subtract(playfieldMiddle, prevPosChanged), - relativeRotationDistance * 0.75f + Math.Min(1, relativeRotationDistance * 0.75f) ); } /// - /// Rotates vector "initial" towards vector "destination" + /// Rotates vector "initial" towards vector "destination" /// /// Vector to rotate to "destination" /// Vector "initial" should be rotated to - /// The angle the vector should be rotated relative to the difference between the angles of the the two vectors. + /// + /// The angle the vector should be rotated relative to the difference between the angles of + /// the the two vectors. + /// /// Resulting vector private Vector2 rotateVectorTowardsVector(Vector2 initial, Vector2 destination, float relativeDistance) { @@ -374,15 +395,9 @@ namespace osu.Game.Rulesets.Osu.Mods var diff = destAngleRad - initialAngleRad; - while (diff < -Math.PI) - { - diff += 2 * Math.PI; - } + while (diff < -Math.PI) diff += 2 * Math.PI; - while (diff > Math.PI) - { - diff -= 2 * Math.PI; - } + while (diff > Math.PI) diff -= 2 * Math.PI; var finalAngleRad = initialAngleRad + relativeDistance * diff; @@ -392,13 +407,6 @@ namespace osu.Game.Rulesets.Osu.Mods ); } - // Background metronome - - public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) - { - drawableRuleset.Overlays.Add(new TargetBeatContainer()); - } - public class TargetBeatContainer : BeatSyncedContainer { private PausableSkinnableSound sample; @@ -408,15 +416,6 @@ namespace osu.Game.Rulesets.Osu.Mods Divisor = 1; } - [BackgroundDependencyLoader] - private void load() - { - InternalChildren = new Drawable[] - { - sample = new PausableSkinnableSound(new SampleInfo("spinnerbonus")) // todo: use another sample? - }; - } - protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) { base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); @@ -425,6 +424,15 @@ namespace osu.Game.Rulesets.Osu.Mods sample?.Play(); } + + [BackgroundDependencyLoader] + private void load() + { + InternalChildren = new Drawable[] + { + sample = new PausableSkinnableSound(new SampleInfo("spinnerbonus")) // todo: use another sample + }; + } } } } From 8c4e60e5ccca84571b0aa5494e665519e7d652b7 Mon Sep 17 00:00:00 2001 From: emu1337 Date: Thu, 17 Jun 2021 21:41:06 +0200 Subject: [PATCH 0060/2442] xmldoc and refactoring --- .../Difficulty/Skills/OsuStrainSkill.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index 5a40524e8a..e47edc37cc 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -12,8 +12,20 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { public abstract class OsuStrainSkill : StrainSkill { + /// + /// The number of sections with the highest strains, which the peak strain reductions will apply to. + /// This is done in order to decrease their impact on the overall difficulty of the map for this skill. + /// protected virtual int ReducedSectionCount => 10; + + /// + /// The baseline multiplier applied to the section with the biggest strain. + /// protected virtual double ReducedStrainBaseline => 0.75; + + /// + /// The final multiplier to be applied to after all other calculations. + /// protected virtual double DifficultyMultiplier => 1.06; protected OsuStrainSkill(Mod[] mods) @@ -31,9 +43,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills // We are reducing the highest strains first to account for extreme difficulty spikes for (int i = 0; i < Math.Min(strains.Count, ReducedSectionCount); i++) { - strains[i] *= ReducedStrainBaseline - + Math.Log10(Interpolation.Lerp(1, 10, Math.Clamp((float)i / ReducedSectionCount, 0, 1))) - * (1.0 - ReducedStrainBaseline); + double scale = Math.Log10(Interpolation.Lerp(1, 10, Math.Clamp((float)i / ReducedSectionCount, 0, 1))); + strains[i] *= Interpolation.Lerp(ReducedStrainBaseline, 1.0, scale); } // Difficulty is the weighted sum of the highest strains from every section. From 639e8b62b94c37cadb3d9bf84da159efa20a9e36 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Fri, 18 Jun 2021 11:20:04 +0800 Subject: [PATCH 0061/2442] Make circles light up 1 beat length before start time --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index c9e43704e1..db6d483dee 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -59,6 +59,8 @@ namespace osu.Game.Rulesets.Osu.Mods private const byte border_distance_x = 192; private const byte border_distance_y = 144; + private ControlPointInfo controlPointInfo; + public bool PerformFail() { return true; @@ -77,13 +79,14 @@ namespace osu.Game.Rulesets.Osu.Mods Seed.Value ??= RNG.Next(); var osuBeatmap = (OsuBeatmap)beatmap; + controlPointInfo = osuBeatmap.ControlPointInfo; var origHitObjects = osuBeatmap.HitObjects.OrderBy(x => x.StartTime).ToList(); var hitObjects = generateBeats(osuBeatmap, origHitObjects) .Select(x => { var newCircle = new HitCircle(); - newCircle.ApplyDefaults(osuBeatmap.ControlPointInfo, osuBeatmap.BeatmapInfo.BaseDifficulty); + newCircle.ApplyDefaults(controlPointInfo, osuBeatmap.BeatmapInfo.BaseDifficulty); newCircle.StartTime = x; return (OsuHitObject)newCircle; }).ToList(); @@ -136,7 +139,7 @@ namespace osu.Game.Rulesets.Osu.Mods var avgColour = colour.AverageColour.Linear; drawable.FadeColour(new Color4(avgColour.R * 0.45f, avgColour.G * 0.45f, avgColour.B * 0.45f, avgColour.A)) - .Then().Delay(h.TimeFadeIn).FadeColour(colour); + .Then().Delay(h.TimePreempt - controlPointInfo.TimingPointAt(h.StartTime).BeatLength).FadeColour(colour); // remove approach circles circle.ApproachCircle.Hide(); From dca2d8af4f7c2027f4f874237cc6aadf809cc16c Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Fri, 18 Jun 2021 13:18:44 +0800 Subject: [PATCH 0062/2442] Animate circles undimming --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index db6d483dee..b7463103df 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -139,7 +139,7 @@ namespace osu.Game.Rulesets.Osu.Mods var avgColour = colour.AverageColour.Linear; drawable.FadeColour(new Color4(avgColour.R * 0.45f, avgColour.G * 0.45f, avgColour.B * 0.45f, avgColour.A)) - .Then().Delay(h.TimePreempt - controlPointInfo.TimingPointAt(h.StartTime).BeatLength).FadeColour(colour); + .Then().Delay(h.TimePreempt - controlPointInfo.TimingPointAt(h.StartTime).BeatLength - 96).FadeColour(colour, 96); // remove approach circles circle.ApproachCircle.Hide(); From f5134c7fc279d804067ecd8d30b291f654994a02 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Fri, 18 Jun 2021 14:39:46 +0800 Subject: [PATCH 0063/2442] Extract constants and add xmldoc --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 25 ++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index b7463103df..63fda42393 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -51,14 +51,26 @@ namespace osu.Game.Rulesets.Osu.Mods public bool DisplayResultsOnFail => true; - // Maximum distance to jump - private const float max_distance = 250f; + /// + /// Jump distance for circles in the last combo + /// + private const float max_base_distance = 250f; + + /// + /// The maximum allowed jump distance after multipliers are applied + /// + private const float distance_cap = 350f; // The distances from the hit objects to the borders of the playfield they start to "turn around" and curve towards the middle. // The closer the hit objects draw to the border, the sharper the turn private const byte border_distance_x = 192; private const byte border_distance_y = 144; + /// + /// Duration of the undimming animation + /// + private const double undim_duration = 96; + private ControlPointInfo controlPointInfo; public bool PerformFail() @@ -139,7 +151,8 @@ namespace osu.Game.Rulesets.Osu.Mods var avgColour = colour.AverageColour.Linear; drawable.FadeColour(new Color4(avgColour.R * 0.45f, avgColour.G * 0.45f, avgColour.B * 0.45f, avgColour.A)) - .Then().Delay(h.TimePreempt - controlPointInfo.TimingPointAt(h.StartTime).BeatLength - 96).FadeColour(colour, 96); + .Then().Delay(h.TimePreempt - controlPointInfo.TimingPointAt(h.StartTime).BeatLength - undim_duration) + .FadeColour(colour, undim_duration); // remove approach circles circle.ApproachCircle.Hide(); @@ -268,10 +281,10 @@ namespace osu.Game.Rulesets.Osu.Mods ? Vector2.Divide(OsuPlayfield.BASE_SIZE, 2) : hitObjects[i - 1].Position; - var distance = map(obj.ComboIndex, 0, maxComboIndex, (float)obj.Radius, 333f); + var distance = map(obj.ComboIndex, 0, maxComboIndex, (float)obj.Radius, max_base_distance); if (obj.NewCombo) distance *= 1.5f; if (obj.Kiai) distance *= 1.2f; - distance = Math.Min(max_distance, distance); + distance = Math.Min(distance_cap, distance); var relativePos = new Vector2( distance * (float)Math.Cos(direction), @@ -296,7 +309,7 @@ namespace osu.Game.Rulesets.Osu.Mods if (obj.LastInCombo) direction = MathHelper.TwoPi * nextSingle(); else - direction += distance / max_distance * (nextSingle() * MathHelper.TwoPi - MathHelper.Pi); + direction += distance / distance_cap * (nextSingle() * MathHelper.TwoPi - MathHelper.Pi); } } From cca26d465110153254eeac9e2fbd078a4f771e5e Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Fri, 18 Jun 2021 16:05:09 +0800 Subject: [PATCH 0064/2442] Take circle radius into account when clamping hit objects to playfield --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 43 +++++++++++++--------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 63fda42393..de7309c27f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -52,12 +52,12 @@ namespace osu.Game.Rulesets.Osu.Mods public bool DisplayResultsOnFail => true; /// - /// Jump distance for circles in the last combo + /// Jump distance for circles in the last combo /// private const float max_base_distance = 250f; /// - /// The maximum allowed jump distance after multipliers are applied + /// The maximum allowed jump distance after multipliers are applied /// private const float distance_cap = 350f; @@ -67,7 +67,12 @@ namespace osu.Game.Rulesets.Osu.Mods private const byte border_distance_y = 144; /// - /// Duration of the undimming animation + /// The extent of rotation towards playfield centre when a circle is near the edge + /// + private const float edge_rotation_multiplier = 0.75f; + + /// + /// Duration of the undimming animation /// private const double undim_duration = 96; @@ -295,14 +300,16 @@ namespace osu.Game.Rulesets.Osu.Mods var newPosition = Vector2.Add(lastPos, relativePos); - if (newPosition.Y < 0) - newPosition.Y = 0; - else if (newPosition.Y > OsuPlayfield.BASE_SIZE.Y) - newPosition.Y = OsuPlayfield.BASE_SIZE.Y; - if (newPosition.X < 0) - newPosition.X = 0; - else if (newPosition.X > OsuPlayfield.BASE_SIZE.X) - newPosition.X = OsuPlayfield.BASE_SIZE.X; + var radius = (float)obj.Radius; + + if (newPosition.Y < radius) + newPosition.Y = radius; + else if (newPosition.Y > OsuPlayfield.BASE_SIZE.Y - radius) + newPosition.Y = OsuPlayfield.BASE_SIZE.Y - radius; + if (newPosition.X < radius) + newPosition.X = radius; + else if (newPosition.X > OsuPlayfield.BASE_SIZE.X - radius) + newPosition.X = OsuPlayfield.BASE_SIZE.X - radius; obj.Position = newPosition; @@ -314,10 +321,10 @@ namespace osu.Game.Rulesets.Osu.Mods } /// - /// Get samples (if any) for a specific point in time. + /// Get samples (if any) for a specific point in time. /// /// - /// Samples will be returned if a hit circle or a slider node exists at that point of time. + /// Samples will be returned if a hit circle or a slider node exists at that point of time. /// /// The list of hit objects in a beatmap, ordered by StartTime /// The point in time to get samples for @@ -349,7 +356,7 @@ namespace osu.Game.Rulesets.Osu.Mods } /// - /// Determines the position of the current hit object relative to the previous one. + /// Determines the position of the current hit object relative to the previous one. /// /// The position of the current hit object relative to the previous one private Vector2 getRotatedVector(Vector2 prevPosChanged, Vector2 posRelativeToPrev) @@ -390,18 +397,18 @@ namespace osu.Game.Rulesets.Osu.Mods return rotateVectorTowardsVector( posRelativeToPrev, Vector2.Subtract(playfieldMiddle, prevPosChanged), - Math.Min(1, relativeRotationDistance * 0.75f) + Math.Min(1, relativeRotationDistance * edge_rotation_multiplier) ); } /// - /// Rotates vector "initial" towards vector "destination" + /// Rotates vector "initial" towards vector "destination" /// /// Vector to rotate to "destination" /// Vector "initial" should be rotated to /// - /// The angle the vector should be rotated relative to the difference between the angles of - /// the the two vectors. + /// The angle the vector should be rotated relative to the difference between the angles of + /// the the two vectors. /// /// Resulting vector private Vector2 rotateVectorTowardsVector(Vector2 initial, Vector2 destination, float relativeDistance) From 403909b598e4c735a1a1e47c429593eb54164b7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 18 Jun 2021 12:49:49 +0200 Subject: [PATCH 0065/2442] Update SR test values in line with diffspike changes --- .../OsuDifficultyCalculatorTest.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index afd94f4570..8d8387378e 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -15,13 +15,13 @@ namespace osu.Game.Rulesets.Osu.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; - [TestCase(6.9311451172574934d, "diffcalc-test")] - [TestCase(1.0736586907780401d, "zero-length-sliders")] + [TestCase(6.7568168283591499d, "diffcalc-test")] + [TestCase(1.0348244046058293d, "zero-length-sliders")] public void Test(double expected, string name) => base.Test(expected, name); - [TestCase(8.7212283220412345d, "diffcalc-test")] - [TestCase(1.3212137158641493d, "zero-length-sliders")] + [TestCase(8.4783236764532557d, "diffcalc-test")] + [TestCase(1.2708532136987165d, "zero-length-sliders")] public void TestClockRateAdjusted(double expected, string name) => Test(expected, name, new OsuModDoubleTime()); From ca8f08ca84894258478ff86a283aa8729986c433 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sat, 19 Jun 2021 10:04:48 +0800 Subject: [PATCH 0066/2442] Avoid overlapping with recent circles --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 62 +++++++++++++++------- 1 file changed, 43 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index de7309c27f..4e4e798f47 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -54,12 +54,12 @@ namespace osu.Game.Rulesets.Osu.Mods /// /// Jump distance for circles in the last combo /// - private const float max_base_distance = 250f; + private const float max_base_distance = 333f; /// /// The maximum allowed jump distance after multipliers are applied /// - private const float distance_cap = 350f; + private const float distance_cap = 380f; // The distances from the hit objects to the borders of the playfield they start to "turn around" and curve towards the middle. // The closer the hit objects draw to the border, the sharper the turn @@ -71,6 +71,11 @@ namespace osu.Game.Rulesets.Osu.Mods /// private const float edge_rotation_multiplier = 0.75f; + /// + /// Number of recent circles to check for overlap + /// + private const int overlap_check_count = 5; + /// /// Duration of the undimming animation /// @@ -291,27 +296,39 @@ namespace osu.Game.Rulesets.Osu.Mods if (obj.Kiai) distance *= 1.2f; distance = Math.Min(distance_cap, distance); - var relativePos = new Vector2( - distance * (float)Math.Cos(direction), - distance * (float)Math.Sin(direction) - ); - relativePos = getRotatedVector(lastPos, relativePos); - direction = (float)Math.Atan2(relativePos.Y, relativePos.X); + // Attempt to place the circle at a place that does not overlap with previous ones - var newPosition = Vector2.Add(lastPos, relativePos); + var tryCount = 0; - var radius = (float)obj.Radius; + do + { + if (tryCount > 0) direction = MathHelper.TwoPi * nextSingle(); - if (newPosition.Y < radius) - newPosition.Y = radius; - else if (newPosition.Y > OsuPlayfield.BASE_SIZE.Y - radius) - newPosition.Y = OsuPlayfield.BASE_SIZE.Y - radius; - if (newPosition.X < radius) - newPosition.X = radius; - else if (newPosition.X > OsuPlayfield.BASE_SIZE.X - radius) - newPosition.X = OsuPlayfield.BASE_SIZE.X - radius; + var relativePos = new Vector2( + distance * (float)Math.Cos(direction), + distance * (float)Math.Sin(direction) + ); + relativePos = getRotatedVector(lastPos, relativePos); + direction = (float)Math.Atan2(relativePos.Y, relativePos.X); - obj.Position = newPosition; + var newPosition = Vector2.Add(lastPos, relativePos); + + var radius = (float)obj.Radius; + + if (newPosition.Y < radius) + newPosition.Y = radius; + else if (newPosition.Y > OsuPlayfield.BASE_SIZE.Y - radius) + newPosition.Y = OsuPlayfield.BASE_SIZE.Y - radius; + if (newPosition.X < radius) + newPosition.X = radius; + else if (newPosition.X > OsuPlayfield.BASE_SIZE.X - radius) + newPosition.X = OsuPlayfield.BASE_SIZE.X - radius; + + obj.Position = newPosition; + + tryCount++; + if (tryCount % 10 == 0) distance *= 0.9f; + } while (distance >= obj.Radius * 2 && isOverlappingWithRecent(hitObjects, i)); if (obj.LastInCombo) direction = MathHelper.TwoPi * nextSingle(); @@ -355,6 +372,13 @@ namespace osu.Game.Rulesets.Osu.Mods return samples; } + private bool isOverlappingWithRecent(IReadOnlyList hitObjects, int idx) + { + var target = hitObjects[idx]; + return hitObjects.SkipLast(hitObjects.Count - idx).TakeLast(overlap_check_count) + .Any(h => Vector2.Distance(h.Position, target.Position) < target.Radius * 2); + } + /// /// Determines the position of the current hit object relative to the previous one. /// From 0cf3119006f8a0a9ec8d4513e646ca4705320bcf Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sat, 19 Jun 2021 11:12:29 +0800 Subject: [PATCH 0067/2442] Guard against edge cases --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 4e4e798f47..0e7dc7a9d3 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -101,6 +101,9 @@ namespace osu.Game.Rulesets.Osu.Mods Seed.Value ??= RNG.Next(); var osuBeatmap = (OsuBeatmap)beatmap; + + if (osuBeatmap.HitObjects.Count == 0) return; + controlPointInfo = osuBeatmap.ControlPointInfo; var origHitObjects = osuBeatmap.HitObjects.OrderBy(x => x.StartTime).ToList(); @@ -277,6 +280,8 @@ namespace osu.Game.Rulesets.Osu.Mods private void randomizeCirclePos(IReadOnlyList hitObjects) { + if (hitObjects.Count == 0) return; + var rng = new Random(Seed.Value.GetValueOrDefault()); float nextSingle(float max = 1f) => (float)(rng.NextDouble() * max); @@ -291,7 +296,9 @@ namespace osu.Game.Rulesets.Osu.Mods ? Vector2.Divide(OsuPlayfield.BASE_SIZE, 2) : hitObjects[i - 1].Position; - var distance = map(obj.ComboIndex, 0, maxComboIndex, (float)obj.Radius, max_base_distance); + var distance = maxComboIndex == 0 + ? (float)obj.Radius + : map(obj.ComboIndex, 0, maxComboIndex, (float)obj.Radius, max_base_distance); if (obj.NewCombo) distance *= 1.5f; if (obj.Kiai) distance *= 1.2f; distance = Math.Min(distance_cap, distance); From b09165a074c11661f656836f946c6a5ddd1f13a0 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sat, 19 Jun 2021 11:13:19 +0800 Subject: [PATCH 0068/2442] Remove the circle size buff --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 0e7dc7a9d3..9688bc7154 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -135,7 +135,6 @@ namespace osu.Game.Rulesets.Osu.Mods { // Decrease AR to increase preempt time difficulty.ApproachRate *= 0.5f; - difficulty.CircleSize *= 0.75f; } // Background metronome From c86794058442186d219391fdcf05d396c8651775 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sat, 19 Jun 2021 12:26:16 +0800 Subject: [PATCH 0069/2442] Marked target mod and traceable mod as incompatible; extracted playfield clamping logic Nothing is visible when target mod and traceable mod are enabled together. --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 33 ++++++++++++------- osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs | 2 +- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 9688bc7154..a6476541ab 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -40,6 +40,8 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => @"Practice keeping up with the beat of the song."; public override double ScoreMultiplier => 1; + public override Type[] IncompatibleMods => new[] { typeof(OsuModTraceable) }; + [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SeedSettingsControl))] public Bindable Seed { get; } = new Bindable { @@ -319,19 +321,10 @@ namespace osu.Game.Rulesets.Osu.Mods var newPosition = Vector2.Add(lastPos, relativePos); - var radius = (float)obj.Radius; - - if (newPosition.Y < radius) - newPosition.Y = radius; - else if (newPosition.Y > OsuPlayfield.BASE_SIZE.Y - radius) - newPosition.Y = OsuPlayfield.BASE_SIZE.Y - radius; - if (newPosition.X < radius) - newPosition.X = radius; - else if (newPosition.X > OsuPlayfield.BASE_SIZE.X - radius) - newPosition.X = OsuPlayfield.BASE_SIZE.X - radius; - obj.Position = newPosition; + clampToPlayfield(obj); + tryCount++; if (tryCount % 10 == 0) distance *= 0.9f; } while (distance >= obj.Radius * 2 && isOverlappingWithRecent(hitObjects, i)); @@ -460,6 +453,24 @@ namespace osu.Game.Rulesets.Osu.Mods ); } + private void clampToPlayfield(OsuHitObject obj) + { + var position = obj.Position; + var radius = (float)obj.Radius; + + if (position.Y < radius) + position.Y = radius; + else if (position.Y > OsuPlayfield.BASE_SIZE.Y - radius) + position.Y = OsuPlayfield.BASE_SIZE.Y - radius; + + if (position.X < radius) + position.X = radius; + else if (position.X > OsuPlayfield.BASE_SIZE.X - radius) + position.X = OsuPlayfield.BASE_SIZE.X - radius; + + obj.Position = position; + } + public class TargetBeatContainer : BeatSyncedContainer { private PausableSkinnableSound sample; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs index 4b0939db16..c5b0a2da3e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => "Put your faith in the approach circles..."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden), typeof(OsuModSpinIn), typeof(OsuModObjectScaleTween) }; + public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden), typeof(OsuModSpinIn), typeof(OsuModObjectScaleTween), typeof(OsuModTarget) }; protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) { From 67f0344c0c93f082ddaedf920fd741d2b25faff7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 20 Jun 2021 23:07:44 +0300 Subject: [PATCH 0070/2442] Add support for loading shaders from ruleset resources --- .../UI/DrawableRulesetDependencies.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs index e66a8c016c..b910f708a2 100644 --- a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs +++ b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs @@ -10,6 +10,7 @@ using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics.OpenGL.Textures; +using osu.Framework.Graphics.Shaders; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Framework.Platform; @@ -34,6 +35,11 @@ namespace osu.Game.Rulesets.UI /// public ISampleStore SampleStore { get; } + /// + /// The shader manager to be used for the ruleset. + /// + public ShaderManager ShaderManager { get; } + /// /// The ruleset config manager. /// @@ -52,6 +58,9 @@ namespace osu.Game.Rulesets.UI SampleStore = parent.Get().GetSampleStore(new NamespacedResourceStore(resources, @"Samples")); SampleStore.PlaybackConcurrency = OsuGameBase.SAMPLE_CONCURRENCY; CacheAs(SampleStore = new FallbackSampleStore(SampleStore, parent.Get())); + + ShaderManager = new ShaderManager(new NamespacedResourceStore(resources, @"Shaders")); + CacheAs(ShaderManager = new FallbackShaderManager(ShaderManager, parent.Get())); } RulesetConfigManager = parent.Get().GetConfigFor(ruleset); @@ -84,6 +93,7 @@ namespace osu.Game.Rulesets.UI SampleStore?.Dispose(); TextureStore?.Dispose(); + ShaderManager?.Dispose(); RulesetConfigManager = null; } @@ -172,5 +182,25 @@ namespace osu.Game.Rulesets.UI primary?.Dispose(); } } + + private class FallbackShaderManager : ShaderManager + { + private readonly ShaderManager primary; + private readonly ShaderManager fallback; + + public FallbackShaderManager(ShaderManager primary, ShaderManager fallback) + { + this.primary = primary; + this.fallback = fallback; + } + + public override byte[] LoadRaw(string name) => primary.LoadRaw(name) ?? fallback.LoadRaw(name); + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + primary?.Dispose(); + } + } } } From eabcbd1d424761d762d8d4a8fb860956db6f1049 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 20 Jun 2021 23:11:10 +0300 Subject: [PATCH 0071/2442] Consider shader manager for ruleset dependencies disposal testing --- .../TestSceneDrawableRulesetDependencies.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs b/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs index f421a30283..94cf317c20 100644 --- a/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs +++ b/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs @@ -13,6 +13,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.OpenGL.Textures; +using osu.Framework.Graphics.Shaders; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; using osu.Framework.Testing; @@ -31,12 +32,14 @@ namespace osu.Game.Tests.Rulesets DrawableWithDependencies drawable = null; TestTextureStore textureStore = null; TestSampleStore sampleStore = null; + TestShaderManager shaderManager = null; AddStep("add dependencies", () => { Child = drawable = new DrawableWithDependencies(); textureStore = drawable.ParentTextureStore; sampleStore = drawable.ParentSampleStore; + shaderManager = drawable.ParentShaderManager; }); AddStep("clear children", Clear); @@ -52,12 +55,14 @@ namespace osu.Game.Tests.Rulesets AddAssert("parent texture store not disposed", () => !textureStore.IsDisposed); AddAssert("parent sample store not disposed", () => !sampleStore.IsDisposed); + AddAssert("parent shader manager not disposed", () => !shaderManager.IsDisposed); } private class DrawableWithDependencies : CompositeDrawable { public TestTextureStore ParentTextureStore { get; private set; } public TestSampleStore ParentSampleStore { get; private set; } + public TestShaderManager ParentShaderManager { get; private set; } public DrawableWithDependencies() { @@ -70,6 +75,7 @@ namespace osu.Game.Tests.Rulesets dependencies.CacheAs(ParentTextureStore = new TestTextureStore()); dependencies.CacheAs(ParentSampleStore = new TestSampleStore()); + dependencies.CacheAs(ParentShaderManager = new TestShaderManager()); return new DrawableRulesetDependencies(new OsuRuleset(), dependencies); } @@ -135,5 +141,18 @@ namespace osu.Game.Tests.Rulesets public int PlaybackConcurrency { get; set; } } + + private class TestShaderManager : ShaderManager + { + public override byte[] LoadRaw(string name) => null; + + public bool IsDisposed { get; private set; } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + IsDisposed = true; + } + } } } From c933cbe89d0aedf6ddbef2c0f320595973a1aec5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 21 Jun 2021 00:09:47 +0300 Subject: [PATCH 0072/2442] Add sample shaders and test case for ruleset-specific shaders --- .../Resources/Shaders/sh_TestFragment.fs | 11 +++++++ .../Resources/Shaders/sh_TestVertex.vs | 31 +++++++++++++++++++ .../Testing/TestSceneRulesetDependencies.cs | 9 ++++++ 3 files changed, 51 insertions(+) create mode 100644 osu.Game.Tests/Resources/Shaders/sh_TestFragment.fs create mode 100644 osu.Game.Tests/Resources/Shaders/sh_TestVertex.vs diff --git a/osu.Game.Tests/Resources/Shaders/sh_TestFragment.fs b/osu.Game.Tests/Resources/Shaders/sh_TestFragment.fs new file mode 100644 index 0000000000..c70ad751be --- /dev/null +++ b/osu.Game.Tests/Resources/Shaders/sh_TestFragment.fs @@ -0,0 +1,11 @@ +#include "sh_Utils.h" + +varying mediump vec2 v_TexCoord; +varying mediump vec4 v_TexRect; + +void main(void) +{ + float hueValue = v_TexCoord.x / (v_TexRect[2] - v_TexRect[0]); + gl_FragColor = hsv2rgb(vec4(hueValue, 1, 1, 1)); +} + diff --git a/osu.Game.Tests/Resources/Shaders/sh_TestVertex.vs b/osu.Game.Tests/Resources/Shaders/sh_TestVertex.vs new file mode 100644 index 0000000000..4485356fa4 --- /dev/null +++ b/osu.Game.Tests/Resources/Shaders/sh_TestVertex.vs @@ -0,0 +1,31 @@ +#include "sh_Utils.h" + +attribute highp vec2 m_Position; +attribute lowp vec4 m_Colour; +attribute mediump vec2 m_TexCoord; +attribute mediump vec4 m_TexRect; +attribute mediump vec2 m_BlendRange; + +varying highp vec2 v_MaskingPosition; +varying lowp vec4 v_Colour; +varying mediump vec2 v_TexCoord; +varying mediump vec4 v_TexRect; +varying mediump vec2 v_BlendRange; + +uniform highp mat4 g_ProjMatrix; +uniform highp mat3 g_ToMaskingSpace; + +void main(void) +{ + // Transform from screen space to masking space. + highp vec3 maskingPos = g_ToMaskingSpace * vec3(m_Position, 1.0); + v_MaskingPosition = maskingPos.xy / maskingPos.z; + + v_Colour = m_Colour; + v_TexCoord = m_TexCoord; + v_TexRect = m_TexRect; + v_BlendRange = m_BlendRange; + + gl_Position = gProjMatrix * vec4(m_Position, 1.0, 1.0); +} + diff --git a/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs b/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs index 97087e31ab..433022788b 100644 --- a/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs +++ b/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio.Sample; using osu.Framework.Configuration.Tracking; +using osu.Framework.Graphics.Shaders; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Framework.Testing; @@ -45,6 +46,14 @@ namespace osu.Game.Tests.Testing Dependencies.Get().Get(@"test-sample") != null); } + [Test] + public void TestRetrieveShader() + { + AddAssert("ruleset shaders retrieved", () => + Dependencies.Get().LoadRaw(@"sh_TestVertex.vs") != null && + Dependencies.Get().LoadRaw(@"sh_TestFragment.fs") != null); + } + [Test] public void TestResolveConfigManager() { From e52a58c1bc5f17079ee3fe7fd85b10b2953ff3c0 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Mon, 21 Jun 2021 16:24:37 +0800 Subject: [PATCH 0073/2442] Switched to a more reasonable sample sound for now --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index a6476541ab..75089fe237 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -494,7 +494,7 @@ namespace osu.Game.Rulesets.Osu.Mods { InternalChildren = new Drawable[] { - sample = new PausableSkinnableSound(new SampleInfo("spinnerbonus")) // todo: use another sample + sample = new PausableSkinnableSound(new SampleInfo("Gameplay/nightcore-hat")) // todo: use another sample }; } } From 804a0433ccc40ed49e71b873b863a5507ee4dbaf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Jun 2021 17:59:55 +0900 Subject: [PATCH 0074/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 1dc99bb60a..3c4380e355 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3c52405f8e..f91620bd25 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 3689ce51f2..22c4340ba2 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 2b1f461d79f8999dea041d4d10c2dd45543b1c62 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 21 Jun 2021 12:24:57 +0300 Subject: [PATCH 0075/2442] Pass empty resource store for `FallbackShaderManager` base constructor --- osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs index b910f708a2..e49e515d2e 100644 --- a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs +++ b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs @@ -189,6 +189,7 @@ namespace osu.Game.Rulesets.UI private readonly ShaderManager fallback; public FallbackShaderManager(ShaderManager primary, ShaderManager fallback) + : base(new ResourceStore()) { this.primary = primary; this.fallback = fallback; From bea828a36433c855f1765c5686848a0f44597adf Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 22 Jun 2021 13:17:34 +0300 Subject: [PATCH 0076/2442] Also pass empty resource in `TestSceneDrawableRulesetDependencies` --- .../Rulesets/TestSceneDrawableRulesetDependencies.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs b/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs index 94cf317c20..c357fccd27 100644 --- a/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs +++ b/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs @@ -16,6 +16,7 @@ using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Shaders; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; +using osu.Framework.IO.Stores; using osu.Framework.Testing; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.UI; @@ -144,6 +145,11 @@ namespace osu.Game.Tests.Rulesets private class TestShaderManager : ShaderManager { + public TestShaderManager() + : base(new ResourceStore()) + { + } + public override byte[] LoadRaw(string name) => null; public bool IsDisposed { get; private set; } From 5a031eada861095340b1d9c7ae428b9318f240a7 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Wed, 23 Jun 2021 16:22:10 +0800 Subject: [PATCH 0077/2442] Revert "Display results after fail" This commit reverts 7815b3c7 --- osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs | 2 -- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 2 -- osu.Game/Rulesets/Mods/IApplicableFailOverride.cs | 5 ----- osu.Game/Rulesets/Mods/ModAutoplay.cs | 2 -- osu.Game/Rulesets/Mods/ModBlockFail.cs | 1 - osu.Game/Rulesets/Mods/ModEasyWithExtraLives.cs | 1 - osu.Game/Rulesets/Mods/ModFailCondition.cs | 1 - osu.Game/Screens/Play/Player.cs | 10 ++-------- 8 files changed, 2 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs index 2b92174b29..aac830801b 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs @@ -28,8 +28,6 @@ namespace osu.Game.Rulesets.Osu.Mods public bool RestartOnFail => false; - public bool DisplayResultsOnFail => false; - private OsuInputManager inputManager; private IFrameStableClock gameplayClock; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 75089fe237..bf31839991 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -51,8 +51,6 @@ namespace osu.Game.Rulesets.Osu.Mods public bool RestartOnFail => false; - public bool DisplayResultsOnFail => true; - /// /// Jump distance for circles in the last combo /// diff --git a/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs b/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs index b7d306e57b..8c99d739cb 100644 --- a/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs +++ b/osu.Game/Rulesets/Mods/IApplicableFailOverride.cs @@ -18,10 +18,5 @@ namespace osu.Game.Rulesets.Mods /// Whether we want to restart on fail. Only used if returns true. /// bool RestartOnFail { get; } - - /// - /// Whether to proceed to results screen on fail. Only used if returns true and is false. - /// - bool DisplayResultsOnFail { get; } } } diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs index e5652a8609..b84b5671e1 100644 --- a/osu.Game/Rulesets/Mods/ModAutoplay.cs +++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs @@ -24,8 +24,6 @@ namespace osu.Game.Rulesets.Mods public bool RestartOnFail => false; - public bool DisplayResultsOnFail => false; - public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModFailCondition), typeof(ModNoFail) }; public override bool HasImplementation => GetType().GenericTypeArguments.Length == 0; diff --git a/osu.Game/Rulesets/Mods/ModBlockFail.cs b/osu.Game/Rulesets/Mods/ModBlockFail.cs index 973a192378..1fde5abad4 100644 --- a/osu.Game/Rulesets/Mods/ModBlockFail.cs +++ b/osu.Game/Rulesets/Mods/ModBlockFail.cs @@ -17,7 +17,6 @@ namespace osu.Game.Rulesets.Mods public bool PerformFail() => false; public bool RestartOnFail => false; - public bool DisplayResultsOnFail => false; public void ReadFromConfig(OsuConfigManager config) { diff --git a/osu.Game/Rulesets/Mods/ModEasyWithExtraLives.cs b/osu.Game/Rulesets/Mods/ModEasyWithExtraLives.cs index 91508b7e70..2ac0f59d84 100644 --- a/osu.Game/Rulesets/Mods/ModEasyWithExtraLives.cs +++ b/osu.Game/Rulesets/Mods/ModEasyWithExtraLives.cs @@ -41,7 +41,6 @@ namespace osu.Game.Rulesets.Mods } public bool RestartOnFail => false; - public bool DisplayResultsOnFail => false; public void ApplyToHealthProcessor(HealthProcessor healthProcessor) { diff --git a/osu.Game/Rulesets/Mods/ModFailCondition.cs b/osu.Game/Rulesets/Mods/ModFailCondition.cs index 012a0f73ff..c0d7bae2b2 100644 --- a/osu.Game/Rulesets/Mods/ModFailCondition.cs +++ b/osu.Game/Rulesets/Mods/ModFailCondition.cs @@ -14,7 +14,6 @@ namespace osu.Game.Rulesets.Mods public virtual bool PerformFail() => true; public virtual bool RestartOnFail => true; - public virtual bool DisplayResultsOnFail => false; public void ApplyToHealthProcessor(HealthProcessor healthProcessor) { diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 4726d54ebc..f9036780aa 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -640,7 +640,7 @@ namespace osu.Game.Screens.Play if (!this.IsCurrentScreen()) return; - if (!ScoreProcessor.HasCompleted.Value && !HealthProcessor.HasFailed) + if (!ScoreProcessor.HasCompleted.Value) { completionProgressDelegate?.Cancel(); completionProgressDelegate = null; @@ -653,7 +653,7 @@ namespace osu.Game.Screens.Play throw new InvalidOperationException($"{nameof(updateCompletionState)} was fired more than once"); // Only show the completion screen if the player hasn't failed - if (HealthProcessor.HasFailed && !Mods.Value.OfType().Any(m => m.DisplayResultsOnFail)) + if (HealthProcessor.HasFailed) return; ValidForResume = false; @@ -751,12 +751,6 @@ namespace osu.Game.Screens.Play // Called back when the transform finishes private void onFailComplete() { - if (Mods.Value.OfType().Any(m => m.DisplayResultsOnFail)) - { - updateCompletionState(true); - return; - } - GameplayClockContainer.Stop(); FailOverlay.Retries = RestartCount; From 6dc5f406b27818051aa6423b8f7e1187414408df Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Wed, 23 Jun 2021 16:29:36 +0800 Subject: [PATCH 0078/2442] Implement IMutateApproachCircles --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index bf31839991..559ad31751 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -31,7 +31,8 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Mods { public class OsuModTarget : ModWithVisibilityAdjustment, IApplicableToDrawableRuleset, - IApplicableToHealthProcessor, IApplicableToDifficulty, IApplicableFailOverride, IHasSeed + IApplicableToHealthProcessor, IApplicableToDifficulty, IApplicableFailOverride, + IHasSeed, IMutateApproachCircles { public override string Name => "Target"; public override string Acronym => "TP"; @@ -40,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => @"Practice keeping up with the beat of the song."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(OsuModTraceable) }; + public override Type[] IncompatibleMods => new[] { typeof(IMutateApproachCircles) }; [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SeedSettingsControl))] public Bindable Seed { get; } = new Bindable From b7dd26612d2ca78fe09a38980e3f1c0b9c472cd7 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Wed, 23 Jun 2021 16:50:05 +0800 Subject: [PATCH 0079/2442] Reordered things and added regions --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 185 ++++++++++++--------- 1 file changed, 106 insertions(+), 79 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 559ad31751..6057b134e3 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Mods Value = null }; - public bool RestartOnFail => false; + #region Constants /// /// Jump distance for circles in the last combo @@ -82,12 +82,19 @@ namespace osu.Game.Rulesets.Osu.Mods /// private const double undim_duration = 96; + #endregion + + #region Private Fields + private ControlPointInfo controlPointInfo; - public bool PerformFail() - { - return true; - } + #endregion + + #region Sudden Death (IApplicableFailOverride) + + public bool PerformFail() => true; + + public bool RestartOnFail => false; public void ApplyToHealthProcessor(HealthProcessor healthProcessor) { @@ -97,6 +104,55 @@ namespace osu.Game.Rulesets.Osu.Mods && !result.IsHit; } + #endregion + + #region Reduce AR (IApplicableToDifficulty) + + public void ReadFromDifficulty(BeatmapDifficulty difficulty) + { + } + + public void ApplyToDifficulty(BeatmapDifficulty difficulty) + { + // Decrease AR to increase preempt time + difficulty.ApproachRate *= 0.5f; + } + + #endregion + + #region Circle Transforms (ModWithVisibilityAdjustment) + + protected override void ApplyIncreasedVisibilityState(DrawableHitObject drawable, ArmedState state) + { + } + + protected override void ApplyNormalVisibilityState(DrawableHitObject drawable, ArmedState state) + { + if (!(drawable is DrawableHitCircle circle)) return; + + var h = (OsuHitObject)drawable.HitObject; + + using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) + { + drawable.ScaleTo(0.5f) + .Then().ScaleTo(1f, h.TimePreempt); + + var colour = drawable.Colour; + + var avgColour = colour.AverageColour.Linear; + drawable.FadeColour(new Color4(avgColour.R * 0.45f, avgColour.G * 0.45f, avgColour.B * 0.45f, avgColour.A)) + .Then().Delay(h.TimePreempt - controlPointInfo.TimingPointAt(h.StartTime).BeatLength - undim_duration) + .FadeColour(colour, undim_duration); + + // remove approach circles + circle.ApproachCircle.Hide(); + } + } + + #endregion + + #region Beatmap Generation (IApplicableToBeatmap) + public override void ApplyToBeatmap(IBeatmap beatmap) { Seed.Value ??= RNG.Next(); @@ -128,55 +184,6 @@ namespace osu.Game.Rulesets.Osu.Mods base.ApplyToBeatmap(beatmap); } - public void ReadFromDifficulty(BeatmapDifficulty difficulty) - { - } - - public void ApplyToDifficulty(BeatmapDifficulty difficulty) - { - // Decrease AR to increase preempt time - difficulty.ApproachRate *= 0.5f; - } - - // Background metronome - - public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) - { - drawableRuleset.Overlays.Add(new TargetBeatContainer()); - } - - protected override void ApplyIncreasedVisibilityState(DrawableHitObject drawable, ArmedState state) - { - } - - protected override void ApplyNormalVisibilityState(DrawableHitObject drawable, ArmedState state) - { - if (!(drawable is DrawableHitCircle circle)) return; - - var h = (OsuHitObject)drawable.HitObject; - - using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) - { - drawable.ScaleTo(0.5f) - .Then().ScaleTo(1f, h.TimePreempt); - - var colour = drawable.Colour; - - var avgColour = colour.AverageColour.Linear; - drawable.FadeColour(new Color4(avgColour.R * 0.45f, avgColour.G * 0.45f, avgColour.B * 0.45f, avgColour.A)) - .Then().Delay(h.TimePreempt - controlPointInfo.TimingPointAt(h.StartTime).BeatLength - undim_duration) - .FadeColour(colour, undim_duration); - - // remove approach circles - circle.ApproachCircle.Hide(); - } - } - - private static float map(float value, float fromLow, float fromHigh, float toLow, float toHigh) - { - return (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow; - } - private IEnumerable generateBeats(IBeatmap beatmap, IReadOnlyCollection origHitObjects) { var startTime = origHitObjects.First().StartTime; @@ -335,6 +342,47 @@ namespace osu.Game.Rulesets.Osu.Mods } } + #endregion + + #region Metronome (IApplicableToDrawableRuleset) + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + drawableRuleset.Overlays.Add(new TargetBeatContainer()); + } + + public class TargetBeatContainer : BeatSyncedContainer + { + private PausableSkinnableSound sample; + + public TargetBeatContainer() + { + Divisor = 1; + } + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) + { + base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + + if (!IsBeatSyncedWithTrack) return; + + sample?.Play(); + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChildren = new Drawable[] + { + sample = new PausableSkinnableSound(new SampleInfo("Gameplay/nightcore-hat")) // todo: use another sample + }; + } + } + + #endregion + + #region Helper Subroutines + /// /// Get samples (if any) for a specific point in time. /// @@ -470,32 +518,11 @@ namespace osu.Game.Rulesets.Osu.Mods obj.Position = position; } - public class TargetBeatContainer : BeatSyncedContainer + private static float map(float value, float fromLow, float fromHigh, float toLow, float toHigh) { - private PausableSkinnableSound sample; - - public TargetBeatContainer() - { - Divisor = 1; - } - - protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) - { - base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); - - if (!IsBeatSyncedWithTrack) return; - - sample?.Play(); - } - - [BackgroundDependencyLoader] - private void load() - { - InternalChildren = new Drawable[] - { - sample = new PausableSkinnableSound(new SampleInfo("Gameplay/nightcore-hat")) // todo: use another sample - }; - } + return (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow; } + + #endregion } } From a7ea7b8b0be060f50ba4e20044aa220e79229037 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 09:34:39 +0800 Subject: [PATCH 0080/2442] Use `GetEndTime()` instead of a switch expression --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 6057b134e3..834876b455 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -17,6 +17,7 @@ using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Objects; @@ -188,12 +189,7 @@ namespace osu.Game.Rulesets.Osu.Mods { var startTime = origHitObjects.First().StartTime; var endObj = origHitObjects.Last(); - var endTime = endObj switch - { - Slider slider => slider.EndTime, - Spinner spinner => spinner.EndTime, - _ => endObj.StartTime - }; + var endTime = endObj.GetEndTime(); var beats = beatmap.ControlPointInfo.TimingPoints .Where(x => Precision.AlmostBigger(endTime, x.Time)) From dae7b8025da3a867e449fe3025954dbacac449b3 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 09:51:45 +0800 Subject: [PATCH 0081/2442] Converted an inline lambda into a method (`getBeatsForTimingPoint`) --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 30 ++++++++++++---------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 834876b455..130489d4c8 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -193,19 +193,7 @@ namespace osu.Game.Rulesets.Osu.Mods var beats = beatmap.ControlPointInfo.TimingPoints .Where(x => Precision.AlmostBigger(endTime, x.Time)) - .SelectMany(tp => - { - var tpBeats = new List(); - var currentTime = tp.Time; - - while (Precision.AlmostBigger(endTime, currentTime) && beatmap.ControlPointInfo.TimingPointAt(currentTime) == tp) - { - tpBeats.Add(Math.Floor(currentTime)); - currentTime += tp.BeatLength; - } - - return tpBeats; - }) + .SelectMany(timingPoint => getBeatsForTimingPoint(timingPoint, endTime)) .Where(x => Precision.AlmostBigger(x, startTime)) // Remove beats during breaks .Where(x => !beatmap.Breaks.Any(b => @@ -379,6 +367,22 @@ namespace osu.Game.Rulesets.Osu.Mods #region Helper Subroutines + private IEnumerable getBeatsForTimingPoint(TimingControlPoint timingPoint, double mapEndTime) + { + var beats = new List(); + int i = 0; + var currentTime = timingPoint.Time; + + while (Precision.AlmostBigger(mapEndTime, currentTime) && controlPointInfo.TimingPointAt(currentTime) == timingPoint) + { + beats.Add(Math.Floor(currentTime)); + i++; + currentTime = timingPoint.Time + i * timingPoint.BeatLength; + } + + return beats; + } + /// /// Get samples (if any) for a specific point in time. /// From 3eab540bcca713c73d73f052a4e0f6656b78f1fb Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 10:07:29 +0800 Subject: [PATCH 0082/2442] Converted an inline lambda into a method (`isInsideBreakPeriod`); moved `origHitObjects` to be a private class field --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 47 +++++++++++++++------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 130489d4c8..7a194f4a98 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -13,6 +13,7 @@ using osu.Framework.Utils; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Beatmaps.Timing; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -89,6 +90,8 @@ namespace osu.Game.Rulesets.Osu.Mods private ControlPointInfo controlPointInfo; + private List origHitObjects; + #endregion #region Sudden Death (IApplicableFailOverride) @@ -163,9 +166,9 @@ namespace osu.Game.Rulesets.Osu.Mods if (osuBeatmap.HitObjects.Count == 0) return; controlPointInfo = osuBeatmap.ControlPointInfo; - var origHitObjects = osuBeatmap.HitObjects.OrderBy(x => x.StartTime).ToList(); + origHitObjects = osuBeatmap.HitObjects.OrderBy(x => x.StartTime).ToList(); - var hitObjects = generateBeats(osuBeatmap, origHitObjects) + var hitObjects = generateBeats(osuBeatmap) .Select(x => { var newCircle = new HitCircle(); @@ -174,9 +177,9 @@ namespace osu.Game.Rulesets.Osu.Mods return (OsuHitObject)newCircle; }).ToList(); - addHitSamples(hitObjects, origHitObjects); + addHitSamples(hitObjects); - fixComboInfo(hitObjects, origHitObjects); + fixComboInfo(hitObjects); randomizeCirclePos(hitObjects); @@ -185,21 +188,17 @@ namespace osu.Game.Rulesets.Osu.Mods base.ApplyToBeatmap(beatmap); } - private IEnumerable generateBeats(IBeatmap beatmap, IReadOnlyCollection origHitObjects) + private IEnumerable generateBeats(IBeatmap beatmap) { var startTime = origHitObjects.First().StartTime; var endObj = origHitObjects.Last(); var endTime = endObj.GetEndTime(); var beats = beatmap.ControlPointInfo.TimingPoints - .Where(x => Precision.AlmostBigger(endTime, x.Time)) + .Where(timingPoint => Precision.AlmostBigger(endTime, timingPoint.Time)) .SelectMany(timingPoint => getBeatsForTimingPoint(timingPoint, endTime)) - .Where(x => Precision.AlmostBigger(x, startTime)) - // Remove beats during breaks - .Where(x => !beatmap.Breaks.Any(b => - Precision.AlmostBigger(x, b.StartTime) - && Precision.AlmostBigger(origHitObjects.First(y => Precision.AlmostBigger(y.StartTime, b.EndTime)).StartTime, x) - )) + .Where(beat => Precision.AlmostBigger(beat, startTime)) + .Where(beat => isInsideBreakPeriod(beatmap.Breaks, beat)) .ToList(); // Remove beats that are too close to the next one (e.g. due to timing point changes) @@ -213,7 +212,7 @@ namespace osu.Game.Rulesets.Osu.Mods return beats; } - private void addHitSamples(IEnumerable hitObjects, IReadOnlyList origHitObjects) + private void addHitSamples(IEnumerable hitObjects) { var lastSampleIdx = 0; @@ -243,7 +242,7 @@ namespace osu.Game.Rulesets.Osu.Mods } } - private void fixComboInfo(List hitObjects, List origHitObjects) + private void fixComboInfo(List hitObjects) { // First follow the combo indices in the original beatmap hitObjects.ForEach(x => @@ -367,6 +366,26 @@ namespace osu.Game.Rulesets.Osu.Mods #region Helper Subroutines + /// + /// Check if a given time is inside a . + /// + /// + /// The given time is also considered to be inside a break if it is earlier than the + /// start time of the first original hit object after the break. + /// + /// The breaks of the beatmap. + /// The time to be checked.= + private bool isInsideBreakPeriod(IEnumerable breaks, double time) + { + return !breaks.Any(breakPeriod => + { + var firstObjAfterBreak = origHitObjects.First(obj => Precision.AlmostBigger(obj.StartTime, breakPeriod.EndTime)); + + return Precision.AlmostBigger(time, breakPeriod.StartTime) + && Precision.AlmostBigger(firstObjAfterBreak.StartTime, time); + }); + } + private IEnumerable getBeatsForTimingPoint(TimingControlPoint timingPoint, double mapEndTime) { var beats = new List(); From 98003ec548f42a8a834ba42ae5c52d03abc24c35 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 10:33:54 +0800 Subject: [PATCH 0083/2442] Avoid modulo when finding slider node index --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 31 +++++++++++++++++----- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 7a194f4a98..31fb70e9c0 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -20,6 +20,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; @@ -413,30 +414,48 @@ namespace osu.Game.Rulesets.Osu.Mods /// Hit samples private IList getSamplesAtTime(IEnumerable hitObjects, double time) { - var sampleObj = hitObjects.FirstOrDefault(x => + var sampleObj = hitObjects.FirstOrDefault(hitObject => { - if (Precision.AlmostEquals(time, x.StartTime)) return true; + if (Precision.AlmostEquals(time, hitObject.StartTime)) + return true; - if (!(x is Slider s)) + if (!(hitObject is IHasPathWithRepeats s)) return false; - if (!Precision.AlmostBigger(time, s.StartTime) + if (!Precision.AlmostBigger(time, hitObject.StartTime) || !Precision.AlmostBigger(s.EndTime, time)) return false; - return Precision.AlmostEquals((time - s.StartTime) % s.SpanDuration, 0); + return nodeIndexFromTime(s, time - hitObject.StartTime) != -1; }); if (sampleObj == null) return null; IList samples; if (sampleObj is Slider slider) - samples = slider.NodeSamples[(int)Math.Round((time - slider.StartTime) % slider.SpanDuration)]; + samples = slider.NodeSamples[nodeIndexFromTime(slider, time - slider.StartTime)]; else samples = sampleObj.Samples; return samples; } + /// + /// Get the repeat node at a point in time. + /// + /// The slider. + /// The time since the start time of the slider. + /// Index of the node. -1 if there isn't a node at the specific time. + private int nodeIndexFromTime(IHasPathWithRepeats curve, double timeSinceStart) + { + double spanDuration = curve.Duration / curve.SpanCount(); + double nodeIndex = timeSinceStart / spanDuration; + + if (Precision.AlmostEquals(nodeIndex - Math.Round(nodeIndex), 0)) + return (int)Math.Round(nodeIndex); + + return -1; + } + private bool isOverlappingWithRecent(IReadOnlyList hitObjects, int idx) { var target = hitObjects[idx]; From 58b439b728c962c858f1cacbd817d6b5cb4020b4 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 10:38:21 +0800 Subject: [PATCH 0084/2442] Switch to `IHasPathWithRepeats` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 31fb70e9c0..54e6708bb8 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -431,8 +431,8 @@ namespace osu.Game.Rulesets.Osu.Mods IList samples; - if (sampleObj is Slider slider) - samples = slider.NodeSamples[nodeIndexFromTime(slider, time - slider.StartTime)]; + if (sampleObj is IHasPathWithRepeats slider) + samples = slider.NodeSamples[nodeIndexFromTime(slider, time - sampleObj.StartTime)]; else samples = sampleObj.Samples; From 04510f1acee717847765b22edbba7dc2b63e416d Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 10:54:21 +0800 Subject: [PATCH 0085/2442] Removed odd-looking decrement and checks in `addHitSamples` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 25 ++++++++++++---------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 54e6708bb8..f2b70b19a8 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -217,29 +217,32 @@ namespace osu.Game.Rulesets.Osu.Mods { var lastSampleIdx = 0; - foreach (var x in hitObjects) + foreach (var obj in hitObjects) { - var samples = getSamplesAtTime(origHitObjects, x.StartTime); + var samples = getSamplesAtTime(origHitObjects, obj.StartTime); if (samples == null) { - while (lastSampleIdx < origHitObjects.Count && origHitObjects[lastSampleIdx].StartTime <= x.StartTime) + // If samples aren't available at the exact start time of the object, + // use samples (without additions) in the closest original hit object instead + + while (lastSampleIdx < origHitObjects.Count && origHitObjects[lastSampleIdx].StartTime <= obj.StartTime) lastSampleIdx++; - lastSampleIdx--; - if (lastSampleIdx < 0 && lastSampleIdx >= origHitObjects.Count) continue; + if (lastSampleIdx >= origHitObjects.Count) continue; - if (lastSampleIdx < origHitObjects.Count - 1) + if (lastSampleIdx > 0) { - // get samples from the next hit object if it is closer in time - if (origHitObjects[lastSampleIdx + 1].StartTime - x.StartTime < x.StartTime - origHitObjects[lastSampleIdx].StartTime) - lastSampleIdx++; + // get samples from the previous hit object if it is closer in time + if (obj.StartTime - origHitObjects[lastSampleIdx - 1].StartTime < origHitObjects[lastSampleIdx].StartTime - obj.StartTime) + lastSampleIdx--; } - x.Samples = origHitObjects[lastSampleIdx].Samples.Where(s => !HitSampleInfo.AllAdditions.Contains(s.Name)).ToList(); + // Remove additions + obj.Samples = origHitObjects[lastSampleIdx].Samples.Where(s => !HitSampleInfo.AllAdditions.Contains(s.Name)).ToList(); } else - x.Samples = samples; + obj.Samples = samples; } } From 6202eed5e24d903e18834efb2801aa4a5d2e42b9 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 10:56:14 +0800 Subject: [PATCH 0086/2442] Moved a misplaced `!` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index f2b70b19a8..796abf30f4 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -199,7 +199,7 @@ namespace osu.Game.Rulesets.Osu.Mods .Where(timingPoint => Precision.AlmostBigger(endTime, timingPoint.Time)) .SelectMany(timingPoint => getBeatsForTimingPoint(timingPoint, endTime)) .Where(beat => Precision.AlmostBigger(beat, startTime)) - .Where(beat => isInsideBreakPeriod(beatmap.Breaks, beat)) + .Where(beat => !isInsideBreakPeriod(beatmap.Breaks, beat)) .ToList(); // Remove beats that are too close to the next one (e.g. due to timing point changes) @@ -207,7 +207,8 @@ namespace osu.Game.Rulesets.Osu.Mods { var beat = beats[i]; - if (Precision.AlmostBigger(beatmap.ControlPointInfo.TimingPointAt(beat).BeatLength / 2, beats[i + 1] - beat)) beats.RemoveAt(i); + if (Precision.AlmostBigger(beatmap.ControlPointInfo.TimingPointAt(beat).BeatLength / 2, beats[i + 1] - beat)) + beats.RemoveAt(i); } return beats; @@ -381,7 +382,7 @@ namespace osu.Game.Rulesets.Osu.Mods /// The time to be checked.= private bool isInsideBreakPeriod(IEnumerable breaks, double time) { - return !breaks.Any(breakPeriod => + return breaks.Any(breakPeriod => { var firstObjAfterBreak = origHitObjects.First(obj => Precision.AlmostBigger(obj.StartTime, breakPeriod.EndTime)); From 6fca8ba5b07a55ebc97326812ee1548f3c0be9ab Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 11:21:43 +0800 Subject: [PATCH 0087/2442] Better explanation for `fixComboInfo` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 796abf30f4..0c6097bc7e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -249,13 +249,20 @@ namespace osu.Game.Rulesets.Osu.Mods private void fixComboInfo(List hitObjects) { - // First follow the combo indices in the original beatmap - hitObjects.ForEach(x => + // Copy combo indices from the closest preceding object + hitObjects.ForEach(obj => { - var origObj = origHitObjects.FindLast(y => Precision.AlmostBigger(x.StartTime, y.StartTime)); - x.ComboIndex = origObj?.ComboIndex ?? 0; + var closestOrigObj = origHitObjects.FindLast(y => Precision.AlmostBigger(obj.StartTime, y.StartTime)); + + // It shouldn't be possible for origObj to be null + // But if it is, obj should be in the first combo + obj.ComboIndex = closestOrigObj?.ComboIndex ?? 0; }); - // Then reprocess them to ensure continuity in the combo indices and add indices in current combo + + // The copied combo indices may not be continuous if the original map starts and ends a combo in between beats + // e.g. A stream with each object starting a new combo + // So combo indices need to be reprocessed to ensure continuity + // Other kinds of combo info are also added in the process var combos = hitObjects.GroupBy(x => x.ComboIndex).ToList(); for (var i = 0; i < combos.Count; i++) From f74275a3b5a676fe2f56f1d974ee41b12445d982 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 11:29:10 +0800 Subject: [PATCH 0088/2442] Moved RNG initialisation to a better place --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 0c6097bc7e..dfce423399 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -93,6 +93,8 @@ namespace osu.Game.Rulesets.Osu.Mods private List origHitObjects; + private Random rng; + #endregion #region Sudden Death (IApplicableFailOverride) @@ -161,6 +163,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override void ApplyToBeatmap(IBeatmap beatmap) { Seed.Value ??= RNG.Next(); + rng = new Random(Seed.Value.GetValueOrDefault()); var osuBeatmap = (OsuBeatmap)beatmap; @@ -284,8 +287,6 @@ namespace osu.Game.Rulesets.Osu.Mods { if (hitObjects.Count == 0) return; - var rng = new Random(Seed.Value.GetValueOrDefault()); - float nextSingle(float max = 1f) => (float)(rng.NextDouble() * max); var direction = MathHelper.TwoPi * nextSingle(); From 71b5ed16c0e5182f1555e3e6cfa739b4d74742d4 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 11:37:00 +0800 Subject: [PATCH 0089/2442] Avoid using osuTK constants; Use `MathF` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index dfce423399..8bbc6f4703 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -289,7 +289,9 @@ namespace osu.Game.Rulesets.Osu.Mods float nextSingle(float max = 1f) => (float)(rng.NextDouble() * max); - var direction = MathHelper.TwoPi * nextSingle(); + const float two_pi = MathF.PI * 2; + + var direction = two_pi * nextSingle(); var maxComboIndex = hitObjects.Last().ComboIndex; for (var i = 0; i < hitObjects.Count; i++) @@ -312,14 +314,14 @@ namespace osu.Game.Rulesets.Osu.Mods do { - if (tryCount > 0) direction = MathHelper.TwoPi * nextSingle(); + if (tryCount > 0) direction = two_pi * nextSingle(); var relativePos = new Vector2( - distance * (float)Math.Cos(direction), - distance * (float)Math.Sin(direction) + distance * MathF.Cos(direction), + distance * MathF.Sin(direction) ); relativePos = getRotatedVector(lastPos, relativePos); - direction = (float)Math.Atan2(relativePos.Y, relativePos.X); + direction = MathF.Atan2(relativePos.Y, relativePos.X); var newPosition = Vector2.Add(lastPos, relativePos); @@ -332,9 +334,9 @@ namespace osu.Game.Rulesets.Osu.Mods } while (distance >= obj.Radius * 2 && isOverlappingWithRecent(hitObjects, i)); if (obj.LastInCombo) - direction = MathHelper.TwoPi * nextSingle(); + direction = two_pi * nextSingle(); else - direction += distance / distance_cap * (nextSingle() * MathHelper.TwoPi - MathHelper.Pi); + direction += distance / distance_cap * (nextSingle() * two_pi - MathF.PI); } } From f8fe4ab4828f72cc71bddebe58fe6890d683d906 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 12:02:48 +0800 Subject: [PATCH 0090/2442] Refactor and rename `isOverlappingWithRecent` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 8bbc6f4703..8a9f9bf224 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -331,7 +331,7 @@ namespace osu.Game.Rulesets.Osu.Mods tryCount++; if (tryCount % 10 == 0) distance *= 0.9f; - } while (distance >= obj.Radius * 2 && isOverlappingWithRecent(hitObjects, i)); + } while (distance >= obj.Radius * 2 && checkForOverlap(hitObjects.SkipLast(hitObjects.Count - i).TakeLast(overlap_check_count), obj)); if (obj.LastInCombo) direction = two_pi * nextSingle(); @@ -470,11 +470,9 @@ namespace osu.Game.Rulesets.Osu.Mods return -1; } - private bool isOverlappingWithRecent(IReadOnlyList hitObjects, int idx) + private bool checkForOverlap(IEnumerable objectsToCheck, OsuHitObject target) { - var target = hitObjects[idx]; - return hitObjects.SkipLast(hitObjects.Count - idx).TakeLast(overlap_check_count) - .Any(h => Vector2.Distance(h.Position, target.Position) < target.Radius * 2); + return objectsToCheck.Any(h => Vector2.Distance(h.Position, target.Position) < target.Radius * 2); } /// From 877c775e35069ee02bba3a6120036ad602c576da Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 12:20:23 +0800 Subject: [PATCH 0091/2442] Added comments --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 8a9f9bf224..1ec08b2c40 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -173,11 +173,11 @@ namespace osu.Game.Rulesets.Osu.Mods origHitObjects = osuBeatmap.HitObjects.OrderBy(x => x.StartTime).ToList(); var hitObjects = generateBeats(osuBeatmap) - .Select(x => + .Select(beat => { var newCircle = new HitCircle(); newCircle.ApplyDefaults(controlPointInfo, osuBeatmap.BeatmapInfo.BaseDifficulty); - newCircle.StartTime = x; + newCircle.StartTime = beat; return (OsuHitObject)newCircle; }).ToList(); @@ -199,9 +199,13 @@ namespace osu.Game.Rulesets.Osu.Mods var endTime = endObj.GetEndTime(); var beats = beatmap.ControlPointInfo.TimingPoints + // Ignore timing points after endTime .Where(timingPoint => Precision.AlmostBigger(endTime, timingPoint.Time)) + // Generate the beats .SelectMany(timingPoint => getBeatsForTimingPoint(timingPoint, endTime)) + // Remove beats before startTime .Where(beat => Precision.AlmostBigger(beat, startTime)) + // Remove beats during breaks .Where(beat => !isInsideBreakPeriod(beatmap.Breaks, beat)) .ToList(); @@ -320,6 +324,7 @@ namespace osu.Game.Rulesets.Osu.Mods distance * MathF.Cos(direction), distance * MathF.Sin(direction) ); + // Rotate the new circle away from playfield border relativePos = getRotatedVector(lastPos, relativePos); direction = MathF.Atan2(relativePos.Y, relativePos.X); @@ -428,6 +433,9 @@ namespace osu.Game.Rulesets.Osu.Mods /// Hit samples private IList getSamplesAtTime(IEnumerable hitObjects, double time) { + // Get a hit object that + // either has StartTime equal to the target time + // or has a repeat node at the target time var sampleObj = hitObjects.FirstOrDefault(hitObject => { if (Precision.AlmostEquals(time, hitObject.StartTime)) @@ -550,6 +558,10 @@ namespace osu.Game.Rulesets.Osu.Mods ); } + /// + /// Move the hit object into playfield, taking its radius into account. + /// + /// The hit object to be clamped. private void clampToPlayfield(OsuHitObject obj) { var position = obj.Position; From 6629f8706a741b67224423f25b473bc067e11047 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 12:31:25 +0800 Subject: [PATCH 0092/2442] Directly fade to gray instead of computing the color values --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 1ec08b2c40..9734b17ce0 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -29,7 +29,6 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Skinning; using osuTK; -using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Mods { @@ -146,8 +145,7 @@ namespace osu.Game.Rulesets.Osu.Mods var colour = drawable.Colour; - var avgColour = colour.AverageColour.Linear; - drawable.FadeColour(new Color4(avgColour.R * 0.45f, avgColour.G * 0.45f, avgColour.B * 0.45f, avgColour.A)) + drawable.FadeColour(OsuColour.Gray(0.45f)) .Then().Delay(h.TimePreempt - controlPointInfo.TimingPointAt(h.StartTime).BeatLength - undim_duration) .FadeColour(colour, undim_duration); From be55c7e075ad103bedd11dedf942ed193c731ea4 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 13:39:50 +0800 Subject: [PATCH 0093/2442] Minor fixes in comments --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 9734b17ce0..00caea0bfe 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -149,7 +149,7 @@ namespace osu.Game.Rulesets.Osu.Mods .Then().Delay(h.TimePreempt - controlPointInfo.TimingPointAt(h.StartTime).BeatLength - undim_duration) .FadeColour(colour, undim_duration); - // remove approach circles + // Remove approach circles circle.ApproachCircle.Hide(); } } @@ -239,7 +239,7 @@ namespace osu.Game.Rulesets.Osu.Mods if (lastSampleIdx > 0) { - // get samples from the previous hit object if it is closer in time + // Get samples from the previous hit object if it is closer in time if (obj.StartTime - origHitObjects[lastSampleIdx - 1].StartTime < origHitObjects[lastSampleIdx].StartTime - obj.StartTime) lastSampleIdx--; } @@ -259,7 +259,7 @@ namespace osu.Game.Rulesets.Osu.Mods { var closestOrigObj = origHitObjects.FindLast(y => Precision.AlmostBigger(obj.StartTime, y.StartTime)); - // It shouldn't be possible for origObj to be null + // It shouldn't be possible for closestOrigObj to be null // But if it is, obj should be in the first combo obj.ComboIndex = closestOrigObj?.ComboIndex ?? 0; }); From 1a47bc254d4e98407ab7799823fdba3b1992a444 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 24 Jun 2021 14:55:49 +0800 Subject: [PATCH 0094/2442] Increase acceptable difference for Precision calls --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 39 +++++++++++++++------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 00caea0bfe..bd3287634c 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -84,6 +84,11 @@ namespace osu.Game.Rulesets.Osu.Mods /// private const double undim_duration = 96; + /// + /// Acceptable difference for timing comparisons + /// + private const double timing_precision = 1; + #endregion #region Private Fields @@ -198,11 +203,11 @@ namespace osu.Game.Rulesets.Osu.Mods var beats = beatmap.ControlPointInfo.TimingPoints // Ignore timing points after endTime - .Where(timingPoint => Precision.AlmostBigger(endTime, timingPoint.Time)) + .Where(timingPoint => almostBigger(endTime, timingPoint.Time)) // Generate the beats .SelectMany(timingPoint => getBeatsForTimingPoint(timingPoint, endTime)) // Remove beats before startTime - .Where(beat => Precision.AlmostBigger(beat, startTime)) + .Where(beat => almostBigger(beat, startTime)) // Remove beats during breaks .Where(beat => !isInsideBreakPeriod(beatmap.Breaks, beat)) .ToList(); @@ -212,7 +217,7 @@ namespace osu.Game.Rulesets.Osu.Mods { var beat = beats[i]; - if (Precision.AlmostBigger(beatmap.ControlPointInfo.TimingPointAt(beat).BeatLength / 2, beats[i + 1] - beat)) + if (almostBigger(beatmap.ControlPointInfo.TimingPointAt(beat).BeatLength / 2, beats[i + 1] - beat)) beats.RemoveAt(i); } @@ -257,7 +262,7 @@ namespace osu.Game.Rulesets.Osu.Mods // Copy combo indices from the closest preceding object hitObjects.ForEach(obj => { - var closestOrigObj = origHitObjects.FindLast(y => Precision.AlmostBigger(obj.StartTime, y.StartTime)); + var closestOrigObj = origHitObjects.FindLast(y => almostBigger(obj.StartTime, y.StartTime)); // It shouldn't be possible for closestOrigObj to be null // But if it is, obj should be in the first combo @@ -397,10 +402,10 @@ namespace osu.Game.Rulesets.Osu.Mods { return breaks.Any(breakPeriod => { - var firstObjAfterBreak = origHitObjects.First(obj => Precision.AlmostBigger(obj.StartTime, breakPeriod.EndTime)); + var firstObjAfterBreak = origHitObjects.First(obj => almostBigger(obj.StartTime, breakPeriod.EndTime)); - return Precision.AlmostBigger(time, breakPeriod.StartTime) - && Precision.AlmostBigger(firstObjAfterBreak.StartTime, time); + return almostBigger(time, breakPeriod.StartTime) + && almostBigger(firstObjAfterBreak.StartTime, time); }); } @@ -410,7 +415,7 @@ namespace osu.Game.Rulesets.Osu.Mods int i = 0; var currentTime = timingPoint.Time; - while (Precision.AlmostBigger(mapEndTime, currentTime) && controlPointInfo.TimingPointAt(currentTime) == timingPoint) + while (almostBigger(mapEndTime, currentTime) && controlPointInfo.TimingPointAt(currentTime) == timingPoint) { beats.Add(Math.Floor(currentTime)); i++; @@ -436,13 +441,13 @@ namespace osu.Game.Rulesets.Osu.Mods // or has a repeat node at the target time var sampleObj = hitObjects.FirstOrDefault(hitObject => { - if (Precision.AlmostEquals(time, hitObject.StartTime)) + if (almostEquals(time, hitObject.StartTime)) return true; if (!(hitObject is IHasPathWithRepeats s)) return false; - if (!Precision.AlmostBigger(time, hitObject.StartTime) - || !Precision.AlmostBigger(s.EndTime, time)) + if (!almostBigger(time, hitObject.StartTime) + || !almostBigger(s.EndTime, time)) return false; return nodeIndexFromTime(s, time - hitObject.StartTime) != -1; @@ -470,7 +475,7 @@ namespace osu.Game.Rulesets.Osu.Mods double spanDuration = curve.Duration / curve.SpanCount(); double nodeIndex = timeSinceStart / spanDuration; - if (Precision.AlmostEquals(nodeIndex - Math.Round(nodeIndex), 0)) + if (almostEquals(nodeIndex - Math.Round(nodeIndex), 0)) return (int)Math.Round(nodeIndex); return -1; @@ -583,6 +588,16 @@ namespace osu.Game.Rulesets.Osu.Mods return (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow; } + private static bool almostBigger(double value1, double value2) + { + return Precision.AlmostBigger(value1, value2, timing_precision); + } + + private static bool almostEquals(double value1, double value2) + { + return Precision.AlmostEquals(value1, value2, timing_precision); + } + #endregion } } From 3eaa04115f3dc6240e0b7231103db9c875168294 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sat, 26 Jun 2021 11:34:10 +0800 Subject: [PATCH 0095/2442] Use `OsuHitObjectGenerationUtils` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 78 +--------------------- 1 file changed, 2 insertions(+), 76 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index bd3287634c..1a6de87745 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -25,6 +25,7 @@ using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.Osu.Utils; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Skinning; @@ -328,7 +329,7 @@ namespace osu.Game.Rulesets.Osu.Mods distance * MathF.Sin(direction) ); // Rotate the new circle away from playfield border - relativePos = getRotatedVector(lastPos, relativePos); + relativePos = OsuHitObjectGenerationUtils.RotateAwayFromEdge(lastPos, relativePos, edge_rotation_multiplier); direction = MathF.Atan2(relativePos.Y, relativePos.X); var newPosition = Vector2.Add(lastPos, relativePos); @@ -486,81 +487,6 @@ namespace osu.Game.Rulesets.Osu.Mods return objectsToCheck.Any(h => Vector2.Distance(h.Position, target.Position) < target.Radius * 2); } - /// - /// Determines the position of the current hit object relative to the previous one. - /// - /// The position of the current hit object relative to the previous one - private Vector2 getRotatedVector(Vector2 prevPosChanged, Vector2 posRelativeToPrev) - { - var relativeRotationDistance = 0f; - var playfieldMiddle = Vector2.Divide(OsuPlayfield.BASE_SIZE, 2); - - if (prevPosChanged.X < playfieldMiddle.X) - { - relativeRotationDistance = Math.Max( - (border_distance_x - prevPosChanged.X) / border_distance_x, - relativeRotationDistance - ); - } - else - { - relativeRotationDistance = Math.Max( - (prevPosChanged.X - (OsuPlayfield.BASE_SIZE.X - border_distance_x)) / border_distance_x, - relativeRotationDistance - ); - } - - if (prevPosChanged.Y < playfieldMiddle.Y) - { - relativeRotationDistance = Math.Max( - (border_distance_y - prevPosChanged.Y) / border_distance_y, - relativeRotationDistance - ); - } - else - { - relativeRotationDistance = Math.Max( - (prevPosChanged.Y - (OsuPlayfield.BASE_SIZE.Y - border_distance_y)) / border_distance_y, - relativeRotationDistance - ); - } - - return rotateVectorTowardsVector( - posRelativeToPrev, - Vector2.Subtract(playfieldMiddle, prevPosChanged), - Math.Min(1, relativeRotationDistance * edge_rotation_multiplier) - ); - } - - /// - /// Rotates vector "initial" towards vector "destination" - /// - /// Vector to rotate to "destination" - /// Vector "initial" should be rotated to - /// - /// The angle the vector should be rotated relative to the difference between the angles of - /// the the two vectors. - /// - /// Resulting vector - private Vector2 rotateVectorTowardsVector(Vector2 initial, Vector2 destination, float relativeDistance) - { - var initialAngleRad = Math.Atan2(initial.Y, initial.X); - var destAngleRad = Math.Atan2(destination.Y, destination.X); - - var diff = destAngleRad - initialAngleRad; - - while (diff < -Math.PI) diff += 2 * Math.PI; - - while (diff > Math.PI) diff -= 2 * Math.PI; - - var finalAngleRad = initialAngleRad + relativeDistance * diff; - - return new Vector2( - initial.Length * (float)Math.Cos(finalAngleRad), - initial.Length * (float)Math.Sin(finalAngleRad) - ); - } - /// /// Move the hit object into playfield, taking its radius into account. /// From ea8993d6d6363654490d210fcac4ba369ac7d3d0 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Mon, 28 Jun 2021 11:33:19 +0800 Subject: [PATCH 0096/2442] Use `IHasRepeats` instead of `IHasPathWithRepeats` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 1a6de87745..b2513e4983 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -445,7 +445,7 @@ namespace osu.Game.Rulesets.Osu.Mods if (almostEquals(time, hitObject.StartTime)) return true; - if (!(hitObject is IHasPathWithRepeats s)) + if (!(hitObject is IHasRepeats s)) return false; if (!almostBigger(time, hitObject.StartTime) || !almostBigger(s.EndTime, time)) @@ -457,7 +457,7 @@ namespace osu.Game.Rulesets.Osu.Mods IList samples; - if (sampleObj is IHasPathWithRepeats slider) + if (sampleObj is IHasRepeats slider) samples = slider.NodeSamples[nodeIndexFromTime(slider, time - sampleObj.StartTime)]; else samples = sampleObj.Samples; @@ -471,7 +471,7 @@ namespace osu.Game.Rulesets.Osu.Mods /// The slider. /// The time since the start time of the slider. /// Index of the node. -1 if there isn't a node at the specific time. - private int nodeIndexFromTime(IHasPathWithRepeats curve, double timeSinceStart) + private int nodeIndexFromTime(IHasRepeats curve, double timeSinceStart) { double spanDuration = curve.Duration / curve.SpanCount(); double nodeIndex = timeSinceStart / spanDuration; From 96e09605d8fabe836396d2cb92d0deabaacb93a2 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Tue, 29 Jun 2021 12:33:40 +0800 Subject: [PATCH 0097/2442] Osu random mod improvements - Reduce "jump streams" by increasing maximum jump angle and variance in jump angle - Reduce weird jumps to sliders by shifting hit circles in front of sliders --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 66 ++++++++++++++++++++-- 1 file changed, 61 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index d1212096bf..d3a7f4fc74 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -26,6 +26,11 @@ namespace osu.Game.Rulesets.Osu.Mods private static readonly float playfield_diagonal = OsuPlayfield.BASE_SIZE.LengthFast; + /// + /// Number of previous hit circles to be shifted together when a slider needs to be moved. + /// + private const int shift_object_count = 10; + private Random rng; public void ApplyToBeatmap(IBeatmap beatmap) @@ -43,14 +48,22 @@ namespace osu.Game.Rulesets.Osu.Mods float rateOfChangeMultiplier = 0; + int cntSinceNewCombo = 0; + for (int i = 0; i < hitObjects.Count; i++) { var hitObject = hitObjects[i]; var current = new RandomObjectInfo(hitObject); - // rateOfChangeMultiplier only changes every i iterations to prevent shaky-line-shaped streams - if (i % 3 == 0) + // rateOfChangeMultiplier only changes every 5 iterations in a combo + // to prevent shaky-line-shaped streams + if (hitObject.NewCombo) + cntSinceNewCombo = 0; + else + cntSinceNewCombo++; + + if (cntSinceNewCombo % 5 == 0) rateOfChangeMultiplier = (float)rng.NextDouble() * 2 - 1; if (hitObject is Spinner) @@ -67,7 +80,24 @@ namespace osu.Game.Rulesets.Osu.Mods current.EndPositionRandomised = current.PositionRandomised; if (hitObject is Slider slider) - moveSliderIntoPlayfield(slider, current); + { + Vector2 shift = moveSliderIntoPlayfield(slider, current); + + if (shift != Vector2.Zero) + { + var toBeShifted = new List(); + + for (int j = i - 1; j >= i - shift_object_count && j >= 0; j--) + { + if (!(hitObjects[j] is HitCircle)) break; + + toBeShifted.Add(hitObjects[j]); + } + + if (toBeShifted.Count > 0) + applyDecreasingShift(toBeShifted, shift); + } + } previous = current; } @@ -94,7 +124,9 @@ namespace osu.Game.Rulesets.Osu.Mods // The max. angle (relative to the angle of the vector pointing from the 2nd last to the last hit object) // is proportional to the distance between the last and the current hit object // to allow jumps and prevent too sharp turns during streams. - var randomAngleRad = rateOfChangeMultiplier * 2 * Math.PI * distanceToPrev / playfield_diagonal; + + // Allow maximum jump angle when jump distance is more than half of playfield diagonal length + var randomAngleRad = rateOfChangeMultiplier * 2 * Math.PI * Math.Min(1f, distanceToPrev / (playfield_diagonal * 0.5f)); current.AngleRad = (float)randomAngleRad + previous.AngleRad; if (current.AngleRad < 0) @@ -122,10 +154,13 @@ namespace osu.Game.Rulesets.Osu.Mods /// /// Moves the and all necessary nested s into the if they aren't already. /// - private void moveSliderIntoPlayfield(Slider slider, RandomObjectInfo currentObjectInfo) + /// The that this slider has been shifted by. + private Vector2 moveSliderIntoPlayfield(Slider slider, RandomObjectInfo currentObjectInfo) { var minMargin = getMinSliderMargin(slider); + var prevPosition = slider.Position; + slider.Position = new Vector2( Math.Clamp(slider.Position.X, minMargin.Left, OsuPlayfield.BASE_SIZE.X - minMargin.Right), Math.Clamp(slider.Position.Y, minMargin.Top, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom) @@ -135,6 +170,27 @@ namespace osu.Game.Rulesets.Osu.Mods currentObjectInfo.EndPositionRandomised = slider.EndPosition; shiftNestedObjects(slider, currentObjectInfo.PositionRandomised - currentObjectInfo.PositionOriginal); + + return slider.Position - prevPosition; + } + + /// + /// Decreasingly shift a list of s by a specified amount. + /// The first item in the list is shifted by the largest amount, while the last item is shifted by the smallest amount. + /// + /// The list of hit objects to be shifted. + /// The amount to be shifted. + private void applyDecreasingShift(IList hitObjects, Vector2 shift) + { + for (int i = 0; i < hitObjects.Count; i++) + { + Vector2 position = hitObjects[i].Position + shift * ((hitObjects.Count - i) / (float)(hitObjects.Count + 1)); + + position.X = MathHelper.Clamp(position.X, 0, OsuPlayfield.BASE_SIZE.X); + position.Y = MathHelper.Clamp(position.Y, 0, OsuPlayfield.BASE_SIZE.Y); + + hitObjects[i].Position = position; + } } /// From d4ff4b26f55f7532fc2a67751a5f46e976f8cd15 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Tue, 29 Jun 2021 12:49:25 +0800 Subject: [PATCH 0098/2442] Split part of `addHitSamples` to a subroutine --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 39 +++++++++------------- 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index b2513e4983..3ad432a40b 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -227,34 +227,13 @@ namespace osu.Game.Rulesets.Osu.Mods private void addHitSamples(IEnumerable hitObjects) { - var lastSampleIdx = 0; - foreach (var obj in hitObjects) { var samples = getSamplesAtTime(origHitObjects, obj.StartTime); - if (samples == null) - { - // If samples aren't available at the exact start time of the object, - // use samples (without additions) in the closest original hit object instead - - while (lastSampleIdx < origHitObjects.Count && origHitObjects[lastSampleIdx].StartTime <= obj.StartTime) - lastSampleIdx++; - - if (lastSampleIdx >= origHitObjects.Count) continue; - - if (lastSampleIdx > 0) - { - // Get samples from the previous hit object if it is closer in time - if (obj.StartTime - origHitObjects[lastSampleIdx - 1].StartTime < origHitObjects[lastSampleIdx].StartTime - obj.StartTime) - lastSampleIdx--; - } - - // Remove additions - obj.Samples = origHitObjects[lastSampleIdx].Samples.Where(s => !HitSampleInfo.AllAdditions.Contains(s.Name)).ToList(); - } - else - obj.Samples = samples; + // If samples aren't available at the exact start time of the object, + // use samples (without additions) in the closest original hit object instead + obj.Samples = samples ?? getClosestHitObject(origHitObjects, obj.StartTime).Samples.Where(s => !HitSampleInfo.AllAdditions.Contains(s.Name)).ToList(); } } @@ -426,6 +405,18 @@ namespace osu.Game.Rulesets.Osu.Mods return beats; } + private OsuHitObject getClosestHitObject(List hitObjects, double time) + { + var closestIdx = hitObjects.FindLastIndex(h => h.StartTime < time); + + if (closestIdx == hitObjects.Count - 1) return hitObjects[closestIdx]; + + // return the closest preceding/succeeding hit object, whoever is closer in time + return hitObjects[closestIdx + 1].StartTime - time < time - hitObjects[closestIdx].StartTime + ? hitObjects[closestIdx + 1] + : hitObjects[closestIdx]; + } + /// /// Get samples (if any) for a specific point in time. /// From 0c5777c2c82b4cd0cc1e7bbed59833c3e0d81a63 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Tue, 29 Jun 2021 12:56:05 +0800 Subject: [PATCH 0099/2442] Added comments --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index d3a7f4fc74..25bdac7be3 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -89,6 +89,7 @@ namespace osu.Game.Rulesets.Osu.Mods for (int j = i - 1; j >= i - shift_object_count && j >= 0; j--) { + // only shift hit circles if (!(hitObjects[j] is HitCircle)) break; toBeShifted.Add(hitObjects[j]); @@ -184,6 +185,8 @@ namespace osu.Game.Rulesets.Osu.Mods { for (int i = 0; i < hitObjects.Count; i++) { + // The first object is shifted by a vector slightly smaller than shift + // The last object is shifted by a vector slightly larger than zero Vector2 position = hitObjects[i].Position + shift * ((hitObjects.Count - i) / (float)(hitObjects.Count + 1)); position.X = MathHelper.Clamp(position.X, 0, OsuPlayfield.BASE_SIZE.X); From 2722565204b59c3b99be66ea801127863e28b3e8 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Tue, 29 Jun 2021 13:36:30 +0800 Subject: [PATCH 0100/2442] Take circle radius into account when clamping to playfield --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 36 ++++++++++++++-------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 25bdac7be3..62ca5e5fb4 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -74,6 +74,10 @@ namespace osu.Game.Rulesets.Osu.Mods applyRandomisation(rateOfChangeMultiplier, previous, current); + // Move hit objects back into the playfield if they are outside of it, + // which would sometimes happen during big jumps otherwise. + current.PositionRandomised = clampToPlayfield(current.PositionRandomised, (float)hitObject.Radius); + hitObject.Position = current.PositionRandomised; // update end position as it may have changed as a result of the position update. @@ -142,14 +146,7 @@ namespace osu.Game.Rulesets.Osu.Mods current.AngleRad = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X); - var position = previous.EndPositionRandomised + posRelativeToPrev; - - // Move hit objects back into the playfield if they are outside of it, - // which would sometimes happen during big jumps otherwise. - position.X = MathHelper.Clamp(position.X, 0, OsuPlayfield.BASE_SIZE.X); - position.Y = MathHelper.Clamp(position.Y, 0, OsuPlayfield.BASE_SIZE.Y); - - current.PositionRandomised = position; + current.PositionRandomised = previous.EndPositionRandomised + posRelativeToPrev; } /// @@ -185,14 +182,12 @@ namespace osu.Game.Rulesets.Osu.Mods { for (int i = 0; i < hitObjects.Count; i++) { + var hitObject = hitObjects[i]; // The first object is shifted by a vector slightly smaller than shift // The last object is shifted by a vector slightly larger than zero - Vector2 position = hitObjects[i].Position + shift * ((hitObjects.Count - i) / (float)(hitObjects.Count + 1)); + Vector2 position = hitObject.Position + shift * ((hitObjects.Count - i) / (float)(hitObjects.Count + 1)); - position.X = MathHelper.Clamp(position.X, 0, OsuPlayfield.BASE_SIZE.X); - position.Y = MathHelper.Clamp(position.Y, 0, OsuPlayfield.BASE_SIZE.Y); - - hitObjects[i].Position = position; + hitObject.Position = clampToPlayfield(position, (float)hitObject.Radius); } } @@ -217,6 +212,13 @@ namespace osu.Game.Rulesets.Osu.Mods minMargin.Left = Math.Min(minMargin.Left, OsuPlayfield.BASE_SIZE.X - minMargin.Right); minMargin.Top = Math.Min(minMargin.Top, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom); + var radius = (float)slider.Radius; + + minMargin.Left += radius; + minMargin.Right += radius; + minMargin.Top += radius; + minMargin.Bottom += radius; + return minMargin; } @@ -236,6 +238,14 @@ namespace osu.Game.Rulesets.Osu.Mods } } + private Vector2 clampToPlayfield(Vector2 position, float radius) + { + position.X = MathHelper.Clamp(position.X, radius, OsuPlayfield.BASE_SIZE.X - radius); + position.Y = MathHelper.Clamp(position.Y, radius, OsuPlayfield.BASE_SIZE.Y - radius); + + return position; + } + private class RandomObjectInfo { public float AngleRad { get; set; } From 8d1eae7c705c95f159304b262f658348ce2c84c8 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Tue, 29 Jun 2021 14:25:45 +0800 Subject: [PATCH 0101/2442] Use `IndexInCurrentCombo` --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 62ca5e5fb4..78a49f8e91 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -48,8 +48,6 @@ namespace osu.Game.Rulesets.Osu.Mods float rateOfChangeMultiplier = 0; - int cntSinceNewCombo = 0; - for (int i = 0; i < hitObjects.Count; i++) { var hitObject = hitObjects[i]; @@ -58,12 +56,7 @@ namespace osu.Game.Rulesets.Osu.Mods // rateOfChangeMultiplier only changes every 5 iterations in a combo // to prevent shaky-line-shaped streams - if (hitObject.NewCombo) - cntSinceNewCombo = 0; - else - cntSinceNewCombo++; - - if (cntSinceNewCombo % 5 == 0) + if (hitObject.IndexInCurrentCombo % 5 == 0) rateOfChangeMultiplier = (float)rng.NextDouble() * 2 - 1; if (hitObject is Spinner) From bc0ab7dd4f1313e448c63661f3252beb18595e1c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 29 Jun 2021 23:39:32 +0300 Subject: [PATCH 0102/2442] Fix `RestoreDefaultValueButton` not behaving correctly on number types --- osu.Game/Overlays/RestoreDefaultValueButton.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/RestoreDefaultValueButton.cs b/osu.Game/Overlays/RestoreDefaultValueButton.cs index fe36f6ba6d..2f0c43dedb 100644 --- a/osu.Game/Overlays/RestoreDefaultValueButton.cs +++ b/osu.Game/Overlays/RestoreDefaultValueButton.cs @@ -20,7 +20,7 @@ namespace osu.Game.Overlays { public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks; - private readonly BindableWithCurrent current = new BindableWithCurrent(); + private readonly IBindableWithCurrent current = IBindableWithCurrent.Create(); // this is done to ensure a click on this button doesn't trigger focus on a parent element which contains the button. public override bool AcceptsFocus => true; @@ -62,7 +62,8 @@ namespace osu.Game.Overlays Action += () => { - if (!current.Disabled) current.SetDefault(); + if (!Current.Disabled) + Current.SetDefault(); }; } @@ -96,12 +97,12 @@ namespace osu.Game.Overlays private void updateState() { - if (current == null) + if (Current == null) return; - this.FadeTo(current.IsDefault ? 0f : - hovering && !current.Disabled ? 1f : 0.65f, 200, Easing.OutQuint); - this.FadeColour(current.Disabled ? Color4.Gray : buttonColour, 200, Easing.OutQuint); + this.FadeTo(Current.IsDefault ? 0f : + hovering && !Current.Disabled ? 1f : 0.65f, 200, Easing.OutQuint); + this.FadeColour(Current.Disabled ? Color4.Gray : buttonColour, 200, Easing.OutQuint); } } } From 3f185a062234cf8f9e86511a365ea496c93d7506 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Wed, 30 Jun 2021 10:35:06 +0800 Subject: [PATCH 0103/2442] Fixed an exception when clamping large sliders --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 78a49f8e91..e0a3e83241 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -152,10 +152,15 @@ namespace osu.Game.Rulesets.Osu.Mods var prevPosition = slider.Position; - slider.Position = new Vector2( - Math.Clamp(slider.Position.X, minMargin.Left, OsuPlayfield.BASE_SIZE.X - minMargin.Right), - Math.Clamp(slider.Position.Y, minMargin.Top, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom) - ); + var newX = minMargin.Left + minMargin.Right > OsuPlayfield.BASE_SIZE.X + ? currentObjectInfo.PositionOriginal.X + : Math.Clamp(slider.Position.X, minMargin.Left, OsuPlayfield.BASE_SIZE.X - minMargin.Right); + + var newY = minMargin.Top + minMargin.Bottom > OsuPlayfield.BASE_SIZE.Y + ? currentObjectInfo.PositionOriginal.Y + : Math.Clamp(slider.Position.Y, minMargin.Top, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom); + + slider.Position = new Vector2(newX, newY); currentObjectInfo.PositionRandomised = slider.Position; currentObjectInfo.EndPositionRandomised = slider.EndPosition; From 913f7602e4829306aa6aa5e0faa5a97bd6522568 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 30 Jun 2021 20:41:08 +0200 Subject: [PATCH 0104/2442] Change seed control type in line with changes --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 3ad432a40b..12daa44477 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -17,6 +17,7 @@ using osu.Game.Beatmaps.Timing; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; @@ -46,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override Type[] IncompatibleMods => new[] { typeof(IMutateApproachCircles) }; - [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SeedSettingsControl))] + [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SettingsNumberBox))] public Bindable Seed { get; } = new Bindable { Default = null, From 3c1f0452a2263faa5b6e017fbf02aff40c4b4c3d Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 1 Jul 2021 10:06:14 +0800 Subject: [PATCH 0105/2442] Refactor and rename `getMinSliderMargin` to `getSliderBoundingBox`. --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 57 ++++++++++++---------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index e0a3e83241..71c070d91b 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; -using osu.Framework.Graphics; +using osu.Framework.Graphics.Primitives; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; @@ -148,19 +148,14 @@ namespace osu.Game.Rulesets.Osu.Mods /// The that this slider has been shifted by. private Vector2 moveSliderIntoPlayfield(Slider slider, RandomObjectInfo currentObjectInfo) { - var minMargin = getMinSliderMargin(slider); + var minMargin = getSliderBoundingBox(slider); var prevPosition = slider.Position; - var newX = minMargin.Left + minMargin.Right > OsuPlayfield.BASE_SIZE.X - ? currentObjectInfo.PositionOriginal.X - : Math.Clamp(slider.Position.X, minMargin.Left, OsuPlayfield.BASE_SIZE.X - minMargin.Right); - - var newY = minMargin.Top + minMargin.Bottom > OsuPlayfield.BASE_SIZE.Y - ? currentObjectInfo.PositionOriginal.Y - : Math.Clamp(slider.Position.Y, minMargin.Top, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom); - - slider.Position = new Vector2(newX, newY); + slider.Position = new Vector2( + Math.Clamp(slider.Position.X, minMargin.Left, minMargin.Right), + Math.Clamp(slider.Position.Y, minMargin.Top, minMargin.Bottom) + ); currentObjectInfo.PositionRandomised = slider.Position; currentObjectInfo.EndPositionRandomised = slider.EndPosition; @@ -190,34 +185,44 @@ namespace osu.Game.Rulesets.Osu.Mods } /// - /// Calculates the min. distances from the 's position to the playfield border for the slider to be fully inside of the playfield. + /// Calculates the bounding box of a 's position for the slider to be fully inside of the playfield. /// - private MarginPadding getMinSliderMargin(Slider slider) + private RectangleF getSliderBoundingBox(Slider slider) { var pathPositions = new List(); slider.Path.GetPathToProgress(pathPositions, 0, 1); - var minMargin = new MarginPadding(); + var box = new RectangleF(); foreach (var pos in pathPositions) { - minMargin.Left = Math.Max(minMargin.Left, -pos.X); - minMargin.Right = Math.Max(minMargin.Right, pos.X); - minMargin.Top = Math.Max(minMargin.Top, -pos.Y); - minMargin.Bottom = Math.Max(minMargin.Bottom, pos.Y); + box.X = Math.Max(box.X, -pos.X); + box.Y = Math.Max(box.Y, -pos.Y); + box.Width = Math.Min(box.Width, OsuPlayfield.BASE_SIZE.X - pos.X - box.X); + box.Height = Math.Min(box.Height, OsuPlayfield.BASE_SIZE.Y - pos.Y - box.Y); } - minMargin.Left = Math.Min(minMargin.Left, OsuPlayfield.BASE_SIZE.X - minMargin.Right); - minMargin.Top = Math.Min(minMargin.Top, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom); - var radius = (float)slider.Radius; - minMargin.Left += radius; - minMargin.Right += radius; - minMargin.Top += radius; - minMargin.Bottom += radius; + box.X += radius; + box.Y += radius; + box.Width -= radius * 2; + box.Height -= radius * 2; - return minMargin; + // If the slider is larger than the playfield, force the slider to stay at its original position + if (box.Width < 0) + { + box.Width = 0; + box.X = slider.Position.X; + } + + if (box.Height < 0) + { + box.Height = 0; + box.Y = slider.Position.Y; + } + + return box; } /// From 328dcb4d6b76e546d20acbb2a46b4b2d2419e54f Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 1 Jul 2021 10:07:26 +0800 Subject: [PATCH 0106/2442] Use `Math.Clamp` instead of `MathHelper.Clamp` --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 71c070d91b..f4358118c7 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -243,8 +243,8 @@ namespace osu.Game.Rulesets.Osu.Mods private Vector2 clampToPlayfield(Vector2 position, float radius) { - position.X = MathHelper.Clamp(position.X, radius, OsuPlayfield.BASE_SIZE.X - radius); - position.Y = MathHelper.Clamp(position.Y, radius, OsuPlayfield.BASE_SIZE.Y - radius); + position.X = Math.Clamp(position.X, radius, OsuPlayfield.BASE_SIZE.X - radius); + position.Y = Math.Clamp(position.Y, radius, OsuPlayfield.BASE_SIZE.Y - radius); return position; } From 6e1839fcf2e48f90806bdebf3953d8152c3eab4f Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 1 Jul 2021 10:08:28 +0800 Subject: [PATCH 0107/2442] Rename `shift_object_count` to `objects_to_shift_before_slider` --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index f4358118c7..844d8d76a1 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Mods /// /// Number of previous hit circles to be shifted together when a slider needs to be moved. /// - private const int shift_object_count = 10; + private const int objects_to_shift_before_slider = 10; private Random rng; @@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Osu.Mods { var toBeShifted = new List(); - for (int j = i - 1; j >= i - shift_object_count && j >= 0; j--) + for (int j = i - 1; j >= i - objects_to_shift_before_slider && j >= 0; j--) { // only shift hit circles if (!(hitObjects[j] is HitCircle)) break; From 7585f1f79088b63e30cebc866757c7e248da5040 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 1 Jul 2021 10:59:06 +0800 Subject: [PATCH 0108/2442] Move special case handling back to `moveSliderIntoPlayfield` --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 31 +++++++++------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 844d8d76a1..ede929bfc4 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -148,14 +148,20 @@ namespace osu.Game.Rulesets.Osu.Mods /// The that this slider has been shifted by. private Vector2 moveSliderIntoPlayfield(Slider slider, RandomObjectInfo currentObjectInfo) { - var minMargin = getSliderBoundingBox(slider); + var boundingBox = getSliderBoundingBox(slider); var prevPosition = slider.Position; - slider.Position = new Vector2( - Math.Clamp(slider.Position.X, minMargin.Left, minMargin.Right), - Math.Clamp(slider.Position.Y, minMargin.Top, minMargin.Bottom) - ); + // If the slider is larger than the playfield, force it to stay at the original position + var newX = boundingBox.Width < 0 + ? currentObjectInfo.PositionOriginal.X + : Math.Clamp(slider.Position.X, boundingBox.Left, boundingBox.Right); + + var newY = boundingBox.Height < 0 + ? currentObjectInfo.PositionOriginal.Y + : Math.Clamp(slider.Position.Y, boundingBox.Top, boundingBox.Bottom); + + slider.Position = new Vector2(newX, newY); currentObjectInfo.PositionRandomised = slider.Position; currentObjectInfo.EndPositionRandomised = slider.EndPosition; @@ -192,7 +198,7 @@ namespace osu.Game.Rulesets.Osu.Mods var pathPositions = new List(); slider.Path.GetPathToProgress(pathPositions, 0, 1); - var box = new RectangleF(); + var box = new RectangleF(Vector2.Zero, OsuPlayfield.BASE_SIZE); foreach (var pos in pathPositions) { @@ -209,19 +215,6 @@ namespace osu.Game.Rulesets.Osu.Mods box.Width -= radius * 2; box.Height -= radius * 2; - // If the slider is larger than the playfield, force the slider to stay at its original position - if (box.Width < 0) - { - box.Width = 0; - box.X = slider.Position.X; - } - - if (box.Height < 0) - { - box.Height = 0; - box.Y = slider.Position.Y; - } - return box; } From c69455cfd019d47e67f22538c2f9a83a5f0713b6 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 1 Jul 2021 11:20:55 +0800 Subject: [PATCH 0109/2442] Fixed slider bounding box calculation --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index ede929bfc4..abc7db0a82 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -202,8 +202,16 @@ namespace osu.Game.Rulesets.Osu.Mods foreach (var pos in pathPositions) { + // Reduce Width and Height accordingly after increasing X and Y + // to keep the right and bottom edge of the rectangle in place + var right = box.Right; box.X = Math.Max(box.X, -pos.X); + box.Width = right - box.X; + + var bottom = box.Bottom; box.Y = Math.Max(box.Y, -pos.Y); + box.Height = bottom - box.Y; + box.Width = Math.Min(box.Width, OsuPlayfield.BASE_SIZE.X - pos.X - box.X); box.Height = Math.Min(box.Height, OsuPlayfield.BASE_SIZE.Y - pos.Y - box.Y); } From 58f80abe322559102ea1c47c5637057cf8aa8ab8 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 1 Jul 2021 11:57:57 +0800 Subject: [PATCH 0110/2442] Several requested changes - Rename `origHitObjects` to `originalHitObjects` - Use `Value` instead of `GetValueOrDefault()` - Remove `endObj` - Added comments - Rename `closestIdx` to `precedingIndex` - Changed an `almostEquals` call --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 47 +++++++++++++--------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 12daa44477..60ca25a721 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Osu.Mods private ControlPointInfo controlPointInfo; - private List origHitObjects; + private List originalHitObjects; private Random rng; @@ -168,14 +168,14 @@ namespace osu.Game.Rulesets.Osu.Mods public override void ApplyToBeatmap(IBeatmap beatmap) { Seed.Value ??= RNG.Next(); - rng = new Random(Seed.Value.GetValueOrDefault()); + rng = new Random(Seed.Value.Value); var osuBeatmap = (OsuBeatmap)beatmap; if (osuBeatmap.HitObjects.Count == 0) return; controlPointInfo = osuBeatmap.ControlPointInfo; - origHitObjects = osuBeatmap.HitObjects.OrderBy(x => x.StartTime).ToList(); + originalHitObjects = osuBeatmap.HitObjects.OrderBy(x => x.StartTime).ToList(); var hitObjects = generateBeats(osuBeatmap) .Select(beat => @@ -199,9 +199,8 @@ namespace osu.Game.Rulesets.Osu.Mods private IEnumerable generateBeats(IBeatmap beatmap) { - var startTime = origHitObjects.First().StartTime; - var endObj = origHitObjects.Last(); - var endTime = endObj.GetEndTime(); + var startTime = originalHitObjects.First().StartTime; + var endTime = originalHitObjects.Last().GetEndTime(); var beats = beatmap.ControlPointInfo.TimingPoints // Ignore timing points after endTime @@ -230,24 +229,25 @@ namespace osu.Game.Rulesets.Osu.Mods { foreach (var obj in hitObjects) { - var samples = getSamplesAtTime(origHitObjects, obj.StartTime); + var samples = getSamplesAtTime(originalHitObjects, obj.StartTime); // If samples aren't available at the exact start time of the object, // use samples (without additions) in the closest original hit object instead - obj.Samples = samples ?? getClosestHitObject(origHitObjects, obj.StartTime).Samples.Where(s => !HitSampleInfo.AllAdditions.Contains(s.Name)).ToList(); + obj.Samples = samples ?? getClosestHitObject(originalHitObjects, obj.StartTime).Samples.Where(s => !HitSampleInfo.AllAdditions.Contains(s.Name)).ToList(); } } private void fixComboInfo(List hitObjects) { - // Copy combo indices from the closest preceding object - hitObjects.ForEach(obj => + // Copy combo indices from an original object at the same time or from the closest preceding object + // (Objects lying between two combos are assumed to belong to the preceding combo) + hitObjects.ForEach(newObj => { - var closestOrigObj = origHitObjects.FindLast(y => almostBigger(obj.StartTime, y.StartTime)); + var closestOrigObj = originalHitObjects.FindLast(y => almostBigger(newObj.StartTime, y.StartTime)); // It shouldn't be possible for closestOrigObj to be null // But if it is, obj should be in the first combo - obj.ComboIndex = closestOrigObj?.ComboIndex ?? 0; + newObj.ComboIndex = closestOrigObj?.ComboIndex ?? 0; }); // The copied combo indices may not be continuous if the original map starts and ends a combo in between beats @@ -383,7 +383,7 @@ namespace osu.Game.Rulesets.Osu.Mods { return breaks.Any(breakPeriod => { - var firstObjAfterBreak = origHitObjects.First(obj => almostBigger(obj.StartTime, breakPeriod.EndTime)); + var firstObjAfterBreak = originalHitObjects.First(obj => almostBigger(obj.StartTime, breakPeriod.EndTime)); return almostBigger(time, breakPeriod.StartTime) && almostBigger(firstObjAfterBreak.StartTime, time); @@ -408,14 +408,14 @@ namespace osu.Game.Rulesets.Osu.Mods private OsuHitObject getClosestHitObject(List hitObjects, double time) { - var closestIdx = hitObjects.FindLastIndex(h => h.StartTime < time); + var precedingIndex = hitObjects.FindLastIndex(h => h.StartTime < time); - if (closestIdx == hitObjects.Count - 1) return hitObjects[closestIdx]; + if (precedingIndex == hitObjects.Count - 1) return hitObjects[precedingIndex]; // return the closest preceding/succeeding hit object, whoever is closer in time - return hitObjects[closestIdx + 1].StartTime - time < time - hitObjects[closestIdx].StartTime - ? hitObjects[closestIdx + 1] - : hitObjects[closestIdx]; + return hitObjects[precedingIndex + 1].StartTime - time < time - hitObjects[precedingIndex].StartTime + ? hitObjects[precedingIndex + 1] + : hitObjects[precedingIndex]; } /// @@ -468,7 +468,7 @@ namespace osu.Game.Rulesets.Osu.Mods double spanDuration = curve.Duration / curve.SpanCount(); double nodeIndex = timeSinceStart / spanDuration; - if (almostEquals(nodeIndex - Math.Round(nodeIndex), 0)) + if (almostEquals(nodeIndex, Math.Round(nodeIndex))) return (int)Math.Round(nodeIndex); return -1; @@ -501,6 +501,15 @@ namespace osu.Game.Rulesets.Osu.Mods obj.Position = position; } + /// + /// Re-maps a number from one range to another. + /// + /// The number to be re-mapped. + /// Beginning of the original range. + /// End of the original range. + /// Beginning of the new range. + /// End of the new range. + /// The re-mapped number. private static float map(float value, float fromLow, float fromHigh, float toLow, float toHigh) { return (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow; From 34be437d7a6b9af4d71581e4b67481883e78fdcf Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 1 Jul 2021 12:21:41 +0800 Subject: [PATCH 0111/2442] Added `definitelyBigger` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 60ca25a721..a39dd7a90a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -204,7 +204,7 @@ namespace osu.Game.Rulesets.Osu.Mods var beats = beatmap.ControlPointInfo.TimingPoints // Ignore timing points after endTime - .Where(timingPoint => almostBigger(endTime, timingPoint.Time)) + .Where(timingPoint => !definitelyBigger(timingPoint.Time, endTime)) // Generate the beats .SelectMany(timingPoint => getBeatsForTimingPoint(timingPoint, endTime)) // Remove beats before startTime @@ -218,7 +218,7 @@ namespace osu.Game.Rulesets.Osu.Mods { var beat = beats[i]; - if (almostBigger(beatmap.ControlPointInfo.TimingPointAt(beat).BeatLength / 2, beats[i + 1] - beat)) + if (!definitelyBigger(beats[i + 1] - beat, beatmap.ControlPointInfo.TimingPointAt(beat).BeatLength / 2)) beats.RemoveAt(i); } @@ -386,7 +386,7 @@ namespace osu.Game.Rulesets.Osu.Mods var firstObjAfterBreak = originalHitObjects.First(obj => almostBigger(obj.StartTime, breakPeriod.EndTime)); return almostBigger(time, breakPeriod.StartTime) - && almostBigger(firstObjAfterBreak.StartTime, time); + && definitelyBigger(firstObjAfterBreak.StartTime, time); }); } @@ -396,7 +396,7 @@ namespace osu.Game.Rulesets.Osu.Mods int i = 0; var currentTime = timingPoint.Time; - while (almostBigger(mapEndTime, currentTime) && controlPointInfo.TimingPointAt(currentTime) == timingPoint) + while (!definitelyBigger(currentTime, mapEndTime) && controlPointInfo.TimingPointAt(currentTime) == timingPoint) { beats.Add(Math.Floor(currentTime)); i++; @@ -439,6 +439,8 @@ namespace osu.Game.Rulesets.Osu.Mods if (!(hitObject is IHasRepeats s)) return false; + // If time is outside the duration of the IHasRepeats, + // then this hitObject isn't the one we want if (!almostBigger(time, hitObject.StartTime) || !almostBigger(s.EndTime, time)) return false; @@ -520,6 +522,11 @@ namespace osu.Game.Rulesets.Osu.Mods return Precision.AlmostBigger(value1, value2, timing_precision); } + private static bool definitelyBigger(double value1, double value2) + { + return Precision.DefinitelyBigger(value1, value2, timing_precision); + } + private static bool almostEquals(double value1, double value2) { return Precision.AlmostEquals(value1, value2, timing_precision); From b0a619bb4cb0837265af38dcb86a597617fcb777 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 1 Jul 2021 12:49:34 +0800 Subject: [PATCH 0112/2442] Prevent multiple enumeration in `checkForOverlap` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index a39dd7a90a..cc3811c9b8 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -300,6 +300,9 @@ namespace osu.Game.Rulesets.Osu.Mods var tryCount = 0; + // for checking overlap + var precedingObjects = hitObjects.SkipLast(hitObjects.Count - i).TakeLast(overlap_check_count).ToList(); + do { if (tryCount > 0) direction = two_pi * nextSingle(); @@ -320,7 +323,7 @@ namespace osu.Game.Rulesets.Osu.Mods tryCount++; if (tryCount % 10 == 0) distance *= 0.9f; - } while (distance >= obj.Radius * 2 && checkForOverlap(hitObjects.SkipLast(hitObjects.Count - i).TakeLast(overlap_check_count), obj)); + } while (distance >= obj.Radius * 2 && checkForOverlap(precedingObjects, obj)); if (obj.LastInCombo) direction = two_pi * nextSingle(); From fbba32647e8ed1f44b68c25d9b2957c13142904f Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 2 Jul 2021 22:00:41 +0900 Subject: [PATCH 0113/2442] Decouple direction of catcher from its scale --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 21 ++++++++++--------- .../UI/CatcherTrailDisplay.cs | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index dcab9459ee..b6a9863002 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -84,8 +84,8 @@ namespace osu.Game.Rulesets.Catch.UI public CatcherAnimationState CurrentState { - get => body.AnimationState.Value; - private set => body.AnimationState.Value = value; + get => Body.AnimationState.Value; + private set => Body.AnimationState.Value = value; } /// @@ -108,18 +108,14 @@ namespace osu.Game.Rulesets.Catch.UI } } - public Direction VisualDirection - { - get => Scale.X > 0 ? Direction.Right : Direction.Left; - set => Scale = new Vector2((value == Direction.Right ? 1 : -1) * Math.Abs(Scale.X), Scale.Y); - } + public Direction VisualDirection { get; set; } = Direction.Right; /// /// Width of the area that can be used to attempt catches during gameplay. /// private readonly float catchWidth; - private readonly SkinnableCatcher body; + internal readonly SkinnableCatcher Body; private Color4 hyperDashColour = DEFAULT_HYPER_DASH_COLOUR; private Color4 hyperDashEndGlowColour = DEFAULT_HYPER_DASH_COLOUR; @@ -158,7 +154,7 @@ namespace osu.Game.Rulesets.Catch.UI Anchor = Anchor.TopCentre, Origin = Anchor.BottomCentre, }, - body = new SkinnableCatcher(), + Body = new SkinnableCatcher(), hitExplosionContainer = new HitExplosionContainer { Anchor = Anchor.TopCentre, @@ -354,6 +350,11 @@ namespace osu.Game.Rulesets.Catch.UI { base.Update(); + var scaleFromDirection = new Vector2((int)VisualDirection, 1); + Body.Scale = scaleFromDirection; + // TODO: don't flip plate contents on legacy skin. + caughtObjectContainer.Scale = hitExplosionContainer.Scale = scaleFromDirection; + // Correct overshooting. if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) || (hyperDashDirection < 0 && hyperDashTargetPosition > X)) @@ -465,7 +466,7 @@ namespace osu.Game.Rulesets.Catch.UI break; case DroppedObjectAnimation.Explode: - var originalX = droppedObjectTarget.ToSpaceOfOtherDrawable(d.DrawPosition, caughtObjectContainer).X * Scale.X; + float originalX = droppedObjectTarget.ToSpaceOfOtherDrawable(d.DrawPosition, caughtObjectContainer).X * caughtObjectContainer.Scale.X; d.MoveToY(d.Y - 50, 250, Easing.OutSine).Then().MoveToY(d.Y + 50, 500, Easing.InSine); d.MoveToX(d.X + originalX * 6, 1000); d.FadeOut(750); diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs index 7e4a5b6a86..b59fabcb70 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs @@ -121,7 +121,7 @@ namespace osu.Game.Rulesets.Catch.UI CatcherTrail sprite = trailPool.Get(); sprite.AnimationState = catcher.CurrentState; - sprite.Scale = catcher.Scale; + sprite.Scale = catcher.Scale * catcher.Body.Scale; sprite.Position = catcher.Position; target.Add(sprite); From 83c80291d49b5b65d056019710e400dc0d1c16c6 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 2 Jul 2021 22:18:31 +0900 Subject: [PATCH 0114/2442] Don't flip catcher plate contents in legacy skin --- .../Skinning/CatchSkinConfiguration.cs | 13 +++++++++++++ .../Skinning/Legacy/CatchLegacySkinTransformer.cs | 10 ++++++++++ osu.Game.Rulesets.Catch/UI/Catcher.cs | 13 +++++++++++-- 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Skinning/CatchSkinConfiguration.cs diff --git a/osu.Game.Rulesets.Catch/Skinning/CatchSkinConfiguration.cs b/osu.Game.Rulesets.Catch/Skinning/CatchSkinConfiguration.cs new file mode 100644 index 0000000000..ea8d742b1a --- /dev/null +++ b/osu.Game.Rulesets.Catch/Skinning/CatchSkinConfiguration.cs @@ -0,0 +1,13 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Catch.Skinning +{ + public enum CatchSkinConfiguration + { + /// + /// Whether the contents of the catcher plate should be visually flipped when the catcher direction is changed. + /// + FlipCatcherPlate + } +} diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index 287ed1b4c7..b6f6e91c29 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -103,6 +103,16 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy result.Value = LegacyColourCompatibility.DisallowZeroAlpha(result.Value); return (IBindable)result; + + case CatchSkinConfiguration config: + switch (config) + { + case CatchSkinConfiguration.FlipCatcherPlate: + // Always return `false` (don't flip catcher plate contents) for a legacy skin. + return (IBindable)new Bindable(); + } + + break; } return base.GetConfig(lookup); diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index b6a9863002..4b532b9c91 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -108,8 +108,16 @@ namespace osu.Game.Rulesets.Catch.UI } } + /// + /// The currently facing direction. + /// public Direction VisualDirection { get; set; } = Direction.Right; + /// + /// Whether the contents of the catcher plate should be visually flipped when the catcher direction is changed. + /// + private bool flipCatcherPlate; + /// /// Width of the area that can be used to attempt catches during gameplay. /// @@ -343,6 +351,8 @@ namespace osu.Game.Rulesets.Catch.UI trails.HyperDashTrailsColour = hyperDashColour; trails.EndGlowSpritesColour = hyperDashEndGlowColour; + flipCatcherPlate = skin.GetConfig(CatchSkinConfiguration.FlipCatcherPlate)?.Value ?? true; + runHyperDashStateTransition(HyperDashing); } @@ -352,8 +362,7 @@ namespace osu.Game.Rulesets.Catch.UI var scaleFromDirection = new Vector2((int)VisualDirection, 1); Body.Scale = scaleFromDirection; - // TODO: don't flip plate contents on legacy skin. - caughtObjectContainer.Scale = hitExplosionContainer.Scale = scaleFromDirection; + caughtObjectContainer.Scale = hitExplosionContainer.Scale = flipCatcherPlate ? scaleFromDirection : Vector2.One; // Correct overshooting. if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) || From 333caca38667c35b10630ed9c111daf0b992e60b Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 2 Jul 2021 23:09:37 +0900 Subject: [PATCH 0115/2442] Add test for catcher plate flipping configuration --- .../TestSceneCatchSkinConfiguration.cs | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs new file mode 100644 index 0000000000..ec186bcfb2 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs @@ -0,0 +1,114 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Framework.Utils; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Catch.Judgements; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.Objects.Drawables; +using osu.Game.Rulesets.Catch.Skinning; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Skinning; +using osu.Game.Tests.Visual; +using Direction = osu.Game.Rulesets.Catch.UI.Direction; + +namespace osu.Game.Rulesets.Catch.Tests +{ + public class TestSceneCatchSkinConfiguration : OsuTestScene + { + [Cached] + private readonly DroppedObjectContainer droppedObjectContainer; + + private Catcher catcher; + + private readonly Container container; + + public TestSceneCatchSkinConfiguration() + { + Add(droppedObjectContainer = new DroppedObjectContainer()); + Add(container = new Container { RelativeSizeAxes = Axes.Both }); + } + + [TestCase(false)] + [TestCase(true)] + public void TestCatcherPlateFlipping(bool flip) + { + AddStep("setup catcher", () => + { + var skin = new TestSkin { FlipCatcherPlate = flip }; + container.Child = new SkinProvidingContainer(skin) + { + Child = catcher = new Catcher(new Container()) + { + Anchor = Anchor.Centre + } + }; + }); + + Fruit fruit = new Fruit(); + + AddStep("catch fruit", () => catchFruit(fruit, 20)); + + float position = 0; + + AddStep("record fruit position", () => position = getCaughtObjectPosition(fruit)); + + AddStep("face left", () => catcher.VisualDirection = Direction.Left); + + if (flip) + AddAssert("fruit position changed", () => !Precision.AlmostEquals(getCaughtObjectPosition(fruit), position)); + else + AddAssert("fruit position unchanged", () => Precision.AlmostEquals(getCaughtObjectPosition(fruit), position)); + + AddStep("face right", () => catcher.VisualDirection = Direction.Right); + + AddAssert("fruit position restored", () => Precision.AlmostEquals(getCaughtObjectPosition(fruit), position)); + } + + private float getCaughtObjectPosition(Fruit fruit) + { + var caughtObject = catcher.ChildrenOfType().Single(c => c.HitObject == fruit); + return caughtObject.Parent.ToSpaceOfOtherDrawable(caughtObject.Position, catcher).X; + } + + private void catchFruit(Fruit fruit, float x) + { + fruit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + var drawableFruit = new DrawableFruit(fruit) { X = x }; + var judgement = fruit.CreateJudgement(); + catcher.OnNewResult(drawableFruit, new CatchJudgementResult(fruit, judgement) + { + Type = judgement.MaxResult + }); + } + + private class TestSkin : DefaultSkin + { + public bool FlipCatcherPlate { get; set; } + + public TestSkin() + : base(null) + { + } + + public override IBindable GetConfig(TLookup lookup) + { + if (lookup is CatchSkinConfiguration config) + { + if (config == CatchSkinConfiguration.FlipCatcherPlate) + return SkinUtils.As(new Bindable(FlipCatcherPlate)); + } + + return base.GetConfig(lookup); + } + } + } +} From 2b366e04fd1fed1506a8e356de951466e040793a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 2 Jul 2021 21:06:57 +0300 Subject: [PATCH 0116/2442] Revert "Fix `RestoreDefaultValueButton` not behaving correctly on number types" This reverts commit bc0ab7dd4f1313e448c63661f3252beb18595e1c. --- osu.Game/Overlays/RestoreDefaultValueButton.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/RestoreDefaultValueButton.cs b/osu.Game/Overlays/RestoreDefaultValueButton.cs index 2f0c43dedb..fe36f6ba6d 100644 --- a/osu.Game/Overlays/RestoreDefaultValueButton.cs +++ b/osu.Game/Overlays/RestoreDefaultValueButton.cs @@ -20,7 +20,7 @@ namespace osu.Game.Overlays { public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks; - private readonly IBindableWithCurrent current = IBindableWithCurrent.Create(); + private readonly BindableWithCurrent current = new BindableWithCurrent(); // this is done to ensure a click on this button doesn't trigger focus on a parent element which contains the button. public override bool AcceptsFocus => true; @@ -62,8 +62,7 @@ namespace osu.Game.Overlays Action += () => { - if (!Current.Disabled) - Current.SetDefault(); + if (!current.Disabled) current.SetDefault(); }; } @@ -97,12 +96,12 @@ namespace osu.Game.Overlays private void updateState() { - if (Current == null) + if (current == null) return; - this.FadeTo(Current.IsDefault ? 0f : - hovering && !Current.Disabled ? 1f : 0.65f, 200, Easing.OutQuint); - this.FadeColour(Current.Disabled ? Color4.Gray : buttonColour, 200, Easing.OutQuint); + this.FadeTo(current.IsDefault ? 0f : + hovering && !current.Disabled ? 1f : 0.65f, 200, Easing.OutQuint); + this.FadeColour(current.Disabled ? Color4.Gray : buttonColour, 200, Easing.OutQuint); } } } From 612ed6353c909da3d1099222795d8a3abf49a2d0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 2 Jul 2021 22:30:26 +0300 Subject: [PATCH 0117/2442] Resolve `RestoreDefaultValueButton` issue by internal management --- .../Overlays/RestoreDefaultValueButton.cs | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/osu.Game/Overlays/RestoreDefaultValueButton.cs b/osu.Game/Overlays/RestoreDefaultValueButton.cs index fe36f6ba6d..cd4490d452 100644 --- a/osu.Game/Overlays/RestoreDefaultValueButton.cs +++ b/osu.Game/Overlays/RestoreDefaultValueButton.cs @@ -20,15 +20,30 @@ namespace osu.Game.Overlays { public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks; - private readonly BindableWithCurrent current = new BindableWithCurrent(); - // this is done to ensure a click on this button doesn't trigger focus on a parent element which contains the button. public override bool AcceptsFocus => true; + private Bindable current; + public Bindable Current { - get => current.Current; - set => current.Current = value; + get => current; + set + { + if (current != null) + { + current.ValueChanged -= onValueChanged; + current.DefaultChanged -= onDefaultChanged; + current.DisabledChanged -= onDisabledChanged; + } + + current = value; + + current.ValueChanged += onValueChanged; + current.DefaultChanged += onDefaultChanged; + current.DisabledChanged += onDisabledChanged; + UpdateState(); + } } private Color4 buttonColour; @@ -62,21 +77,11 @@ namespace osu.Game.Overlays Action += () => { - if (!current.Disabled) current.SetDefault(); + if (!current.Disabled) + current.SetDefault(); }; } - protected override void LoadComplete() - { - base.LoadComplete(); - - Current.ValueChanged += _ => UpdateState(); - Current.DisabledChanged += _ => UpdateState(); - Current.DefaultChanged += _ => UpdateState(); - - UpdateState(); - } - public LocalisableString TooltipText => "revert to default"; protected override bool OnHover(HoverEvent e) @@ -92,6 +97,10 @@ namespace osu.Game.Overlays UpdateState(); } + private void onValueChanged(ValueChangedEvent _) => UpdateState(); + private void onDefaultChanged(ValueChangedEvent _) => UpdateState(); + private void onDisabledChanged(bool _) => UpdateState(); + public void UpdateState() => Scheduler.AddOnce(updateState); private void updateState() From 83578e7c9d878530bf68a5ae2099916ff619824f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 2 Jul 2021 23:24:51 +0300 Subject: [PATCH 0118/2442] Hold a bound copy reference instead --- osu.Game/Overlays/RestoreDefaultValueButton.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/RestoreDefaultValueButton.cs b/osu.Game/Overlays/RestoreDefaultValueButton.cs index cd4490d452..0790929ab2 100644 --- a/osu.Game/Overlays/RestoreDefaultValueButton.cs +++ b/osu.Game/Overlays/RestoreDefaultValueButton.cs @@ -30,14 +30,8 @@ namespace osu.Game.Overlays get => current; set { - if (current != null) - { - current.ValueChanged -= onValueChanged; - current.DefaultChanged -= onDefaultChanged; - current.DisabledChanged -= onDisabledChanged; - } - - current = value; + current?.UnbindAll(); + current = value.GetBoundCopy(); current.ValueChanged += onValueChanged; current.DefaultChanged += onDefaultChanged; From 1470bb15634c0946a08c91fd484b643a5e542659 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sat, 3 Jul 2021 13:02:39 +0800 Subject: [PATCH 0119/2442] Use `IHidesApproachCircles` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index cc3811c9b8..5ca9a15a0f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModTarget : ModWithVisibilityAdjustment, IApplicableToDrawableRuleset, IApplicableToHealthProcessor, IApplicableToDifficulty, IApplicableFailOverride, - IHasSeed, IMutateApproachCircles + IHasSeed, IHidesApproachCircles { public override string Name => "Target"; public override string Acronym => "TP"; @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => @"Practice keeping up with the beat of the song."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(IMutateApproachCircles) }; + public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles) }; [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SettingsNumberBox))] public Bindable Seed { get; } = new Bindable From 0be75cc4edb4943b7522857a1f2f6375ec498670 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 3 Jul 2021 22:35:46 +0900 Subject: [PATCH 0120/2442] Fix incorrect `base` call causing import optimisation to not work --- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 14bddb6319..0d16294c68 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -324,7 +324,7 @@ namespace osu.Game.Beatmaps protected override bool CanSkipImport(BeatmapSetInfo existing, BeatmapSetInfo import) { - if (!base.CanReuseExisting(existing, import)) + if (!base.CanSkipImport(existing, import)) return false; return existing.Beatmaps.Any(b => b.OnlineBeatmapID != null); From 8a23dfa6f58f09ca2a73184262d07eeeabc1fdd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 3 Jul 2021 17:58:12 +0200 Subject: [PATCH 0121/2442] Fix optimised import path buffering events without flush --- osu.Game/Database/ArchiveModelManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index f72a43fa01..87bf54f981 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -353,8 +353,6 @@ namespace osu.Game.Database { cancellationToken.ThrowIfCancellationRequested(); - delayEvents(); - bool checkedExisting = false; TModel existing = null; @@ -394,6 +392,8 @@ namespace osu.Game.Database } } + delayEvents(); + try { LogForModel(item, @"Beginning import..."); From 12371f74245632e061c5ef7bb8ab0693d98d2668 Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Sat, 3 Jul 2021 18:12:22 +0800 Subject: [PATCH 0122/2442] Fix playlist item displays as empty string if no unicode title is present --- osu.Game/Beatmaps/BeatmapMetadata.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index bfc0236db3..713f80d1fe 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.cs @@ -94,7 +94,10 @@ namespace osu.Game.Beatmaps public RomanisableString ToRomanisableString() { string author = Author == null ? string.Empty : $"({Author})"; - return new RomanisableString($"{ArtistUnicode} - {TitleUnicode} {author}".Trim(), $"{Artist} - {Title} {author}".Trim()); + var artistUnicode = string.IsNullOrEmpty(ArtistUnicode) ? Artist : ArtistUnicode; + var titleUnicode = string.IsNullOrEmpty(TitleUnicode) ? Title : TitleUnicode; + + return new RomanisableString($"{artistUnicode} - {titleUnicode} {author}".Trim(), $"{Artist} - {Title} {author}".Trim()); } [JsonIgnore] From d1862d8cff46945bfb7d90bfc984e1fb176f66af Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sun, 4 Jul 2021 10:01:56 +0800 Subject: [PATCH 0123/2442] Rename `map` to `mapRange` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 5ca9a15a0f..27c352070a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -291,7 +291,7 @@ namespace osu.Game.Rulesets.Osu.Mods var distance = maxComboIndex == 0 ? (float)obj.Radius - : map(obj.ComboIndex, 0, maxComboIndex, (float)obj.Radius, max_base_distance); + : mapRange(obj.ComboIndex, 0, maxComboIndex, (float)obj.Radius, max_base_distance); if (obj.NewCombo) distance *= 1.5f; if (obj.Kiai) distance *= 1.2f; distance = Math.Min(distance_cap, distance); @@ -515,7 +515,7 @@ namespace osu.Game.Rulesets.Osu.Mods /// Beginning of the new range. /// End of the new range. /// The re-mapped number. - private static float map(float value, float fromLow, float fromHigh, float toLow, float toHigh) + private static float mapRange(float value, float fromLow, float fromHigh, float toLow, float toHigh) { return (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow; } From 567e9f33a9b74e577761b1eb62564d35be78e8d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 4 Jul 2021 16:24:39 +0900 Subject: [PATCH 0124/2442] Fix thread safety of realm `Refresh` operation Due to the lack of locking, there was a chance the the update thread `context` was retrieved just before the `flushContexts` call, followed by `.Refresh()` being run while the blocking behaviour was invoked. This can be seen in test failures such as https://ci.appveyor.com/project/peppy/osu/builds/39859786/tests. As an aside, I tried multiple different methods to avoid `lock()` on the update thread but they felt flaky. The overhead of lock when there's no contention is reportedly around 30-50ns, so likely not of concern. We can address it at a later point if it becomes one. --- osu.Game/Database/RealmContextFactory.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/RealmContextFactory.cs b/osu.Game/Database/RealmContextFactory.cs index fb5e2faff8..3354b97849 100644 --- a/osu.Game/Database/RealmContextFactory.cs +++ b/osu.Game/Database/RealmContextFactory.cs @@ -38,6 +38,8 @@ namespace osu.Game.Database private static readonly GlobalStatistic pending_writes = GlobalStatistics.Get("Realm", "Pending writes"); private static readonly GlobalStatistic active_usages = GlobalStatistics.Get("Realm", "Active usages"); + private readonly object updateContextLock = new object(); + private Realm context; public Realm Context @@ -107,8 +109,11 @@ namespace osu.Game.Database { base.Update(); - if (context?.Refresh() == true) - refreshes.Value++; + lock (updateContextLock) + { + if (context?.Refresh() == true) + refreshes.Value++; + } } private Realm createContext() @@ -156,7 +161,9 @@ namespace osu.Game.Database Logger.Log(@"Flushing realm contexts...", LoggingTarget.Database); var previousContext = context; - context = null; + + lock (updateContextLock) + context = null; // wait for all threaded usages to finish while (active_usages.Value > 0) From 3ec7dc3bb94972fe16a7359db595faf8136bf5fa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 4 Jul 2021 17:59:39 +0900 Subject: [PATCH 0125/2442] Update tests in line with thread safety check --- .../Database/TestRealmKeyBindingStore.cs | 54 +++++++++++-------- osu.Game/Database/RealmContextFactory.cs | 21 +++++--- 2 files changed, 47 insertions(+), 28 deletions(-) diff --git a/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs b/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs index cac331451b..642ecf00b8 100644 --- a/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs +++ b/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs @@ -38,19 +38,28 @@ namespace osu.Game.Tests.Database [Test] public void TestDefaultsPopulationAndQuery() { - Assert.That(query().Count, Is.EqualTo(0)); + Assert.That(queryCount(), Is.EqualTo(0)); KeyBindingContainer testContainer = new TestKeyBindingContainer(); keyBindingStore.Register(testContainer); - Assert.That(query().Count, Is.EqualTo(3)); + Assert.That(queryCount(), Is.EqualTo(3)); - Assert.That(query().Where(k => k.ActionInt == (int)GlobalAction.Back).Count, Is.EqualTo(1)); - Assert.That(query().Where(k => k.ActionInt == (int)GlobalAction.Select).Count, Is.EqualTo(2)); + Assert.That(queryCount(GlobalAction.Back), Is.EqualTo(1)); + Assert.That(queryCount(GlobalAction.Select), Is.EqualTo(2)); } - private IQueryable query() => realmContextFactory.Context.All(); + private int queryCount(GlobalAction? match = null) + { + using (var usage = realmContextFactory.GetForRead()) + { + var results = usage.Realm.All(); + if (match.HasValue) + results = results.Where(k => k.ActionInt == (int)match.Value); + return results.Count(); + } + } [Test] public void TestUpdateViaQueriedReference() @@ -59,25 +68,28 @@ namespace osu.Game.Tests.Database keyBindingStore.Register(testContainer); - var backBinding = query().Single(k => k.ActionInt == (int)GlobalAction.Back); - - Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.Escape })); - - var tsr = ThreadSafeReference.Create(backBinding); - - using (var usage = realmContextFactory.GetForWrite()) + using (var primaryUsage = realmContextFactory.GetForRead()) { - var binding = usage.Realm.ResolveReference(tsr); - binding.KeyCombination = new KeyCombination(InputKey.BackSpace); + var backBinding = primaryUsage.Realm.All().Single(k => k.ActionInt == (int)GlobalAction.Back); - usage.Commit(); + Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.Escape })); + + var tsr = ThreadSafeReference.Create(backBinding); + + using (var usage = realmContextFactory.GetForWrite()) + { + var binding = usage.Realm.ResolveReference(tsr); + binding.KeyCombination = new KeyCombination(InputKey.BackSpace); + + usage.Commit(); + } + + Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.BackSpace })); + + // check still correct after re-query. + backBinding = primaryUsage.Realm.All().Single(k => k.ActionInt == (int)GlobalAction.Back); + Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.BackSpace })); } - - Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.BackSpace })); - - // check still correct after re-query. - backBinding = query().Single(k => k.ActionInt == (int)GlobalAction.Back); - Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.BackSpace })); } [TearDown] diff --git a/osu.Game/Database/RealmContextFactory.cs b/osu.Game/Database/RealmContextFactory.cs index 3354b97849..f706c37419 100644 --- a/osu.Game/Database/RealmContextFactory.cs +++ b/osu.Game/Database/RealmContextFactory.cs @@ -4,6 +4,7 @@ using System; using System.Threading; using osu.Framework.Allocation; +using osu.Framework.Development; using osu.Framework.Graphics; using osu.Framework.Logging; using osu.Framework.Platform; @@ -46,15 +47,21 @@ namespace osu.Game.Database { get { - if (context == null) + if (!ThreadSafety.IsUpdateThread) + throw new InvalidOperationException($"Use {nameof(GetForRead)} when performing realm operations from a non-update thread"); + + lock (updateContextLock) { - context = createContext(); - Logger.Log($"Opened realm \"{context.Config.DatabasePath}\" at version {context.Config.SchemaVersion}"); + if (context == null) + { + context = createContext(); + Logger.Log($"Opened realm \"{context.Config.DatabasePath}\" at version {context.Config.SchemaVersion}"); + } + + // creating a context will ensure our schema is up-to-date and migrated. + + return context; } - - // creating a context will ensure our schema is up-to-date and migrated. - - return context; } } From b89521314f7b8f72691a865f79b3574f24ba575c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 4 Jul 2021 12:07:31 +0200 Subject: [PATCH 0126/2442] Mention alternatives to `Context` when not on update thread in xmldoc --- osu.Game/Database/IRealmFactory.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Database/IRealmFactory.cs b/osu.Game/Database/IRealmFactory.cs index c79442134c..0e93e5bf4f 100644 --- a/osu.Game/Database/IRealmFactory.cs +++ b/osu.Game/Database/IRealmFactory.cs @@ -9,6 +9,7 @@ namespace osu.Game.Database { /// /// The main realm context, bound to the update thread. + /// If querying from a non-update thread is needed, use or to receive a context instead. /// Realm Context { get; } From 3291f15dccee50bcaac9f042e7b830fff528236e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 4 Jul 2021 12:08:15 +0200 Subject: [PATCH 0127/2442] Mention `GetForWrite()` as another alternative to `Context` accesses --- osu.Game/Database/RealmContextFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/RealmContextFactory.cs b/osu.Game/Database/RealmContextFactory.cs index f706c37419..b0241fb93c 100644 --- a/osu.Game/Database/RealmContextFactory.cs +++ b/osu.Game/Database/RealmContextFactory.cs @@ -48,7 +48,7 @@ namespace osu.Game.Database get { if (!ThreadSafety.IsUpdateThread) - throw new InvalidOperationException($"Use {nameof(GetForRead)} when performing realm operations from a non-update thread"); + throw new InvalidOperationException($"Use {nameof(GetForRead)} or {nameof(GetForWrite)} when performing realm operations from a non-update thread"); lock (updateContextLock) { From d1553f086413f786e4450aa3ac0333efd2e3359d Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Sun, 4 Jul 2021 14:47:07 +0200 Subject: [PATCH 0128/2442] Implement ability to switch between volume meters --- .../Input/Bindings/GlobalActionContainer.cs | 9 +++ .../Overlays/Volume/VolumeControlReceptor.cs | 2 + osu.Game/Overlays/Volume/VolumeMeter.cs | 26 ++++++- osu.Game/Overlays/VolumeOverlay.cs | 69 +++++++++++++++---- 4 files changed, 91 insertions(+), 15 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index c8227c0887..2482be90ee 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -103,6 +103,9 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.Alt, InputKey.Up }, GlobalAction.IncreaseVolume), new KeyBinding(new[] { InputKey.Alt, InputKey.Down }, GlobalAction.DecreaseVolume), + new KeyBinding(new[] { InputKey.Alt, InputKey.Left }, GlobalAction.PreviousVolumeMeter), + new KeyBinding(new[] { InputKey.Alt, InputKey.Right }, GlobalAction.NextVolumeMeter), + new KeyBinding(new[] { InputKey.Control, InputKey.F4 }, GlobalAction.ToggleMute), new KeyBinding(InputKey.TrackPrevious, GlobalAction.MusicPrev), @@ -263,5 +266,11 @@ namespace osu.Game.Input.Bindings [Description("Toggle skin editor")] ToggleSkinEditor, + + [Description("Previous volume meter")] + PreviousVolumeMeter, + + [Description("Next volume meter")] + NextVolumeMeter, } } diff --git a/osu.Game/Overlays/Volume/VolumeControlReceptor.cs b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs index ae9c2eb394..b24214ff3d 100644 --- a/osu.Game/Overlays/Volume/VolumeControlReceptor.cs +++ b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs @@ -30,6 +30,8 @@ namespace osu.Game.Overlays.Volume return true; case GlobalAction.ToggleMute: + case GlobalAction.NextVolumeMeter: + case GlobalAction.PreviousVolumeMeter: ActionRequested?.Invoke(action); return true; } diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs index 532b0f4a81..df8f1b7012 100644 --- a/osu.Game/Overlays/Volume/VolumeMeter.cs +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Transforms; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; @@ -27,6 +28,11 @@ namespace osu.Game.Overlays.Volume { public class VolumeMeter : Container, IKeyBindingHandler { + [Resolved(canBeNull: true)] + private Bindable focusedMeter { get; set; } + + private bool isFocused => focusedMeter == null || focusedMeter.Value == this; + private CircularProgress volumeCircle; private CircularProgress volumeCircleGlow; @@ -41,6 +47,8 @@ namespace osu.Game.Overlays.Volume private Sample sample; private double sampleLastPlaybackTime; + public Action RequestFocus; + public VolumeMeter(string name, float circleSize, Color4 meterColour) { this.circleSize = circleSize; @@ -310,20 +318,32 @@ namespace osu.Game.Overlays.Volume private const float transition_length = 500; + public void Focus() + { + if (focusedMeter != null) + focusedMeter.Value = this; + + this.ScaleTo(1.04f, transition_length, Easing.OutExpo); + } + + public void Unfocus() + { + this.ScaleTo(1f, transition_length, Easing.OutExpo); + } + protected override bool OnHover(HoverEvent e) { - this.ScaleTo(1.04f, transition_length, Easing.OutExpo); + Focus(); return false; } protected override void OnHoverLost(HoverLostEvent e) { - this.ScaleTo(1f, transition_length, Easing.OutExpo); } public bool OnPressed(GlobalAction action) { - if (!IsHovered) + if (!IsHovered || !isFocused) return false; switch (action) diff --git a/osu.Game/Overlays/VolumeOverlay.cs b/osu.Game/Overlays/VolumeOverlay.cs index eb639431ae..750d54e091 100644 --- a/osu.Game/Overlays/VolumeOverlay.cs +++ b/osu.Game/Overlays/VolumeOverlay.cs @@ -19,6 +19,7 @@ using osuTK.Graphics; namespace osu.Game.Overlays { + [Cached] public class VolumeOverlay : VisibilityContainer { private const float offset = 10; @@ -32,6 +33,8 @@ namespace osu.Game.Overlays public Bindable IsMuted { get; } = new Bindable(); + private FillFlowContainer volumeMeters; + [BackgroundDependencyLoader] private void load(AudioManager audio, OsuColour colours) { @@ -53,7 +56,7 @@ namespace osu.Game.Overlays Margin = new MarginPadding(10), Current = { BindTarget = IsMuted } }, - new FillFlowContainer + volumeMeters = new FillFlowContainer { Direction = FillDirection.Vertical, AutoSizeAxes = Axes.Both, @@ -61,7 +64,7 @@ namespace osu.Game.Overlays Origin = Anchor.CentreLeft, Spacing = new Vector2(0, offset), Margin = new MarginPadding { Left = offset }, - Children = new Drawable[] + Children = new VolumeMeter[] { volumeMeterEffect = new VolumeMeter("EFFECTS", 125, colours.BlueDarker), volumeMeterMaster = new VolumeMeter("MASTER", 150, colours.PinkDarker), @@ -81,8 +84,13 @@ namespace osu.Game.Overlays else audio.RemoveAdjustment(AdjustableProperty.Volume, muteAdjustment); }); + + focusedMeter.BindValueChanged(meter => meter.OldValue?.Unfocus()); } + [Cached] + private Bindable focusedMeter = new Bindable(); + protected override void LoadComplete() { base.LoadComplete(); @@ -93,6 +101,23 @@ namespace osu.Game.Overlays muteButton.Current.ValueChanged += _ => Show(); } + public bool HandleAction(GlobalAction action) + { + if (!IsLoaded) return false; + + switch (action) + { + case GlobalAction.DecreaseVolume: + case GlobalAction.IncreaseVolume: + return Adjust(action); + case GlobalAction.NextVolumeMeter: + return true; + + } + + return true; + } + public bool Adjust(GlobalAction action, float amount = 1, bool isPrecise = false) { if (!IsLoaded) return false; @@ -102,25 +127,32 @@ namespace osu.Game.Overlays case GlobalAction.DecreaseVolume: if (State.Value == Visibility.Hidden) Show(); - else if (volumeMeterMusic.IsHovered) - volumeMeterMusic.Decrease(amount, isPrecise); - else if (volumeMeterEffect.IsHovered) - volumeMeterEffect.Decrease(amount, isPrecise); else - volumeMeterMaster.Decrease(amount, isPrecise); + focusedMeter.Value.Decrease(amount, isPrecise); return true; case GlobalAction.IncreaseVolume: if (State.Value == Visibility.Hidden) Show(); - else if (volumeMeterMusic.IsHovered) - volumeMeterMusic.Increase(amount, isPrecise); - else if (volumeMeterEffect.IsHovered) - volumeMeterEffect.Increase(amount, isPrecise); else - volumeMeterMaster.Increase(amount, isPrecise); + focusedMeter.Value.Increase(amount, isPrecise); return true; + case GlobalAction.NextVolumeMeter: + if (State.Value == Visibility.Hidden) + Show(); + else + focusShift(1); + return true; + + case GlobalAction.PreviousVolumeMeter: + if (State.Value == Visibility.Hidden) + Show(); + else + focusShift(-1); + return true; + + case GlobalAction.ToggleMute: Show(); muteButton.Current.Value = !muteButton.Current.Value; @@ -130,10 +162,23 @@ namespace osu.Game.Overlays return false; } + private void focusShift(int direction = 1) + { + Show(); + var newIndex = volumeMeters.IndexOf(focusedMeter.Value) + direction; + if (newIndex < 0) + newIndex += volumeMeters.Count; + + volumeMeters.Children[newIndex % volumeMeters.Count].Focus(); + } + private ScheduledDelegate popOutDelegate; public override void Show() { + if (State.Value == Visibility.Hidden) + volumeMeterMaster.Focus(); + if (State.Value == Visibility.Visible) schedulePopOut(); From e151c7ffd017fe07aa7a6555dad88425b1fb1c1c Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Sun, 4 Jul 2021 15:31:43 +0200 Subject: [PATCH 0129/2442] Let VolumeMeter request focus instead of taking it --- osu.Game/Overlays/Volume/VolumeMeter.cs | 13 +++------- osu.Game/Overlays/VolumeOverlay.cs | 33 +++++++++++++++++-------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs index df8f1b7012..530a10ea7b 100644 --- a/osu.Game/Overlays/Volume/VolumeMeter.cs +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -12,7 +12,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Transforms; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; @@ -28,10 +27,7 @@ namespace osu.Game.Overlays.Volume { public class VolumeMeter : Container, IKeyBindingHandler { - [Resolved(canBeNull: true)] - private Bindable focusedMeter { get; set; } - - private bool isFocused => focusedMeter == null || focusedMeter.Value == this; + private bool isFocused = true; private CircularProgress volumeCircle; private CircularProgress volumeCircleGlow; @@ -320,20 +316,19 @@ namespace osu.Game.Overlays.Volume public void Focus() { - if (focusedMeter != null) - focusedMeter.Value = this; - + isFocused = true; this.ScaleTo(1.04f, transition_length, Easing.OutExpo); } public void Unfocus() { + isFocused = false; this.ScaleTo(1f, transition_length, Easing.OutExpo); } protected override bool OnHover(HoverEvent e) { - Focus(); + RequestFocus?.Invoke(this); return false; } diff --git a/osu.Game/Overlays/VolumeOverlay.cs b/osu.Game/Overlays/VolumeOverlay.cs index 750d54e091..c9321f5d60 100644 --- a/osu.Game/Overlays/VolumeOverlay.cs +++ b/osu.Game/Overlays/VolumeOverlay.cs @@ -19,7 +19,6 @@ using osuTK.Graphics; namespace osu.Game.Overlays { - [Cached] public class VolumeOverlay : VisibilityContainer { private const float offset = 10; @@ -64,11 +63,20 @@ namespace osu.Game.Overlays Origin = Anchor.CentreLeft, Spacing = new Vector2(0, offset), Margin = new MarginPadding { Left = offset }, - Children = new VolumeMeter[] + Children = new [] { - volumeMeterEffect = new VolumeMeter("EFFECTS", 125, colours.BlueDarker), - volumeMeterMaster = new VolumeMeter("MASTER", 150, colours.PinkDarker), - volumeMeterMusic = new VolumeMeter("MUSIC", 125, colours.BlueDarker), + volumeMeterEffect = new VolumeMeter("EFFECTS", 125, colours.BlueDarker) + { + RequestFocus = v => focusedMeter.Value = v + }, + volumeMeterMaster = new VolumeMeter("MASTER", 150, colours.PinkDarker) + { + RequestFocus = v => focusedMeter.Value = v + }, + volumeMeterMusic = new VolumeMeter("MUSIC", 125, colours.BlueDarker) + { + RequestFocus = v => focusedMeter.Value = v + }, } } }); @@ -85,11 +93,14 @@ namespace osu.Game.Overlays audio.RemoveAdjustment(AdjustableProperty.Volume, muteAdjustment); }); - focusedMeter.BindValueChanged(meter => meter.OldValue?.Unfocus()); + focusedMeter.BindValueChanged(meter => + { + meter.OldValue?.Unfocus(); + meter.NewValue?.Focus(); + }); } - [Cached] - private Bindable focusedMeter = new Bindable(); + private readonly Bindable focusedMeter = new Bindable(); protected override void LoadComplete() { @@ -165,19 +176,21 @@ namespace osu.Game.Overlays private void focusShift(int direction = 1) { Show(); + var newIndex = volumeMeters.IndexOf(focusedMeter.Value) + direction; if (newIndex < 0) newIndex += volumeMeters.Count; - volumeMeters.Children[newIndex % volumeMeters.Count].Focus(); + focusedMeter.Value = volumeMeters.Children[newIndex % volumeMeters.Count]; } private ScheduledDelegate popOutDelegate; public override void Show() { + // Focus on the master meter as a default if previously hidden if (State.Value == Visibility.Hidden) - volumeMeterMaster.Focus(); + focusedMeter.Value = volumeMeterMaster; if (State.Value == Visibility.Visible) schedulePopOut(); From d0707079b1011d2107a0fd00d3117bd83c78c6d4 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Sun, 4 Jul 2021 15:35:51 +0200 Subject: [PATCH 0130/2442] Remove unused method --- osu.Game/Overlays/VolumeOverlay.cs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/osu.Game/Overlays/VolumeOverlay.cs b/osu.Game/Overlays/VolumeOverlay.cs index c9321f5d60..cad2c81bb5 100644 --- a/osu.Game/Overlays/VolumeOverlay.cs +++ b/osu.Game/Overlays/VolumeOverlay.cs @@ -112,23 +112,6 @@ namespace osu.Game.Overlays muteButton.Current.ValueChanged += _ => Show(); } - public bool HandleAction(GlobalAction action) - { - if (!IsLoaded) return false; - - switch (action) - { - case GlobalAction.DecreaseVolume: - case GlobalAction.IncreaseVolume: - return Adjust(action); - case GlobalAction.NextVolumeMeter: - return true; - - } - - return true; - } - public bool Adjust(GlobalAction action, float amount = 1, bool isPrecise = false) { if (!IsLoaded) return false; From 50c9e17e52ff6963fda8574d520e8122fdf1b755 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Sun, 4 Jul 2021 15:42:26 +0200 Subject: [PATCH 0131/2442] Return focus when using UP/DOWN on unfocused meter --- osu.Game/Overlays/Volume/VolumeMeter.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs index 530a10ea7b..1c997a0abc 100644 --- a/osu.Game/Overlays/Volume/VolumeMeter.cs +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -338,9 +338,13 @@ namespace osu.Game.Overlays.Volume public bool OnPressed(GlobalAction action) { - if (!IsHovered || !isFocused) + if (!IsHovered) return false; + // Grab back focus is UP/DOWN input is directed at this meter + if (!isFocused) + RequestFocus?.Invoke(this); + switch (action) { case GlobalAction.SelectPrevious: From 14a861003af7104d4ad06ca19f4ae299678eca79 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Sun, 4 Jul 2021 16:06:28 +0200 Subject: [PATCH 0132/2442] Fix code quality errors --- osu.Game/Overlays/VolumeOverlay.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/VolumeOverlay.cs b/osu.Game/Overlays/VolumeOverlay.cs index cad2c81bb5..dca39774f2 100644 --- a/osu.Game/Overlays/VolumeOverlay.cs +++ b/osu.Game/Overlays/VolumeOverlay.cs @@ -63,7 +63,7 @@ namespace osu.Game.Overlays Origin = Anchor.CentreLeft, Spacing = new Vector2(0, offset), Margin = new MarginPadding { Left = offset }, - Children = new [] + Children = new[] { volumeMeterEffect = new VolumeMeter("EFFECTS", 125, colours.BlueDarker) { @@ -146,7 +146,6 @@ namespace osu.Game.Overlays focusShift(-1); return true; - case GlobalAction.ToggleMute: Show(); muteButton.Current.Value = !muteButton.Current.Value; From 44d540eb53f6e169de2cede447e8079fac89054a Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Sun, 4 Jul 2021 22:09:23 +0800 Subject: [PATCH 0133/2442] Add test --- .../BeatmapMetadataRomanisationTest.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 osu.Game.Tests/Localisation/BeatmapMetadataRomanisationTest.cs diff --git a/osu.Game.Tests/Localisation/BeatmapMetadataRomanisationTest.cs b/osu.Game.Tests/Localisation/BeatmapMetadataRomanisationTest.cs new file mode 100644 index 0000000000..582864079d --- /dev/null +++ b/osu.Game.Tests/Localisation/BeatmapMetadataRomanisationTest.cs @@ -0,0 +1,31 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Beatmaps; + +namespace osu.Game.Tests.Localisation +{ + [TestFixture] + public class BeatmapMetadataRomanisationTest + { + [Test] + public void TestNoUnicode() + { + var beatmap = new Beatmap + { + BeatmapInfo = new BeatmapInfo + { + Metadata = new BeatmapMetadata + { + Artist = "Artist", + Title = "Romanised title" + } + } + }; + var romanisableString = beatmap.Metadata.ToRomanisableString(); + + Assert.AreEqual(romanisableString.Romanised, romanisableString.Original); + } + } +} From 356f5dceef8e898dc592b8630b82b1bfda3eecfa Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Sun, 4 Jul 2021 22:31:08 +0800 Subject: [PATCH 0134/2442] Add more test case --- .../BeatmapMetadataRomanisationTest.cs | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Localisation/BeatmapMetadataRomanisationTest.cs b/osu.Game.Tests/Localisation/BeatmapMetadataRomanisationTest.cs index 582864079d..dab4825919 100644 --- a/osu.Game.Tests/Localisation/BeatmapMetadataRomanisationTest.cs +++ b/osu.Game.Tests/Localisation/BeatmapMetadataRomanisationTest.cs @@ -10,20 +10,30 @@ namespace osu.Game.Tests.Localisation public class BeatmapMetadataRomanisationTest { [Test] - public void TestNoUnicode() + public void TestRomanisation() { - var beatmap = new Beatmap + var metadata = new BeatmapMetadata { - BeatmapInfo = new BeatmapInfo - { - Metadata = new BeatmapMetadata - { - Artist = "Artist", - Title = "Romanised title" - } - } + Artist = "Romanised Artist", + ArtistUnicode = "Unicode Artist", + Title = "Romanised title", + TitleUnicode = "Unicode Title" }; - var romanisableString = beatmap.Metadata.ToRomanisableString(); + var romanisableString = metadata.ToRomanisableString(); + + Assert.AreEqual(metadata.ToString(), romanisableString.Romanised); + Assert.AreEqual($"{metadata.ArtistUnicode} - {metadata.TitleUnicode}", romanisableString.Original); + } + + [Test] + public void TestRomanisationNoUnicode() + { + var metadata = new BeatmapMetadata + { + Artist = "Romanised Artist", + Title = "Romanised title" + }; + var romanisableString = metadata.ToRomanisableString(); Assert.AreEqual(romanisableString.Romanised, romanisableString.Original); } From 69803105efbaee00bd3aa8494cb0d4bf5c74b99f Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Sun, 4 Jul 2021 17:19:44 +0200 Subject: [PATCH 0135/2442] Fix volume meter requesting focus for any action --- osu.Game/Overlays/Volume/VolumeMeter.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs index 1c997a0abc..55f7588741 100644 --- a/osu.Game/Overlays/Volume/VolumeMeter.cs +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -27,8 +27,6 @@ namespace osu.Game.Overlays.Volume { public class VolumeMeter : Container, IKeyBindingHandler { - private bool isFocused = true; - private CircularProgress volumeCircle; private CircularProgress volumeCircleGlow; @@ -316,13 +314,11 @@ namespace osu.Game.Overlays.Volume public void Focus() { - isFocused = true; this.ScaleTo(1.04f, transition_length, Easing.OutExpo); } public void Unfocus() { - isFocused = false; this.ScaleTo(1f, transition_length, Easing.OutExpo); } @@ -341,17 +337,15 @@ namespace osu.Game.Overlays.Volume if (!IsHovered) return false; - // Grab back focus is UP/DOWN input is directed at this meter - if (!isFocused) - RequestFocus?.Invoke(this); - switch (action) { case GlobalAction.SelectPrevious: + RequestFocus?.Invoke(this); adjust(1, false); return true; case GlobalAction.SelectNext: + RequestFocus?.Invoke(this); adjust(-1, false); return true; } From 32b068fbdc93d3298bf3257c7d6aa9e6b9749119 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Sun, 4 Jul 2021 21:50:58 +0200 Subject: [PATCH 0136/2442] Fix typo causing nested windows to be ignored --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 8dcc1ca164..cccd73f19f 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -496,7 +496,7 @@ namespace osu.Game.Rulesets.UI foreach (var n in h.NestedHitObjects) { - if (h.HitWindows.WindowFor(HitResult.Miss) > 0) + if (n.HitWindows.WindowFor(HitResult.Miss) > 0) return n.HitWindows; } } From 6d2ffe3a94ec9edab261682d16887e6dd519c70e Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Sun, 4 Jul 2021 22:51:35 +0200 Subject: [PATCH 0137/2442] Add basic tests --- .../NonVisual/FirstAvailableHitWindowTest.cs | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 osu.Game.Tests/NonVisual/FirstAvailableHitWindowTest.cs diff --git a/osu.Game.Tests/NonVisual/FirstAvailableHitWindowTest.cs b/osu.Game.Tests/NonVisual/FirstAvailableHitWindowTest.cs new file mode 100644 index 0000000000..92766d8334 --- /dev/null +++ b/osu.Game.Tests/NonVisual/FirstAvailableHitWindowTest.cs @@ -0,0 +1,121 @@ +// 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.Diagnostics.CodeAnalysis; +using NUnit.Framework; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Scoring; + +namespace osu.Game.Tests.NonVisual +{ + public class FirstAvailableHitWindowsTest + { + private TestDrawableRuleset testDrawableRuleset = new TestDrawableRuleset(); + + [Test] + public void TestResultIfOnlyParentHitWindowIsEmpty() + { + var testObject = new TestHitObject(hitWindows: HitWindows.Empty); + HitObject nested = new TestHitObject(hitWindows: new HitWindows()); + testObject.AddNested(nested); + testDrawableRuleset.HitObjects = new List { testObject }; + + // If the parent window is empty, but its nested object isn't, return the nested object + Assert.AreSame(testDrawableRuleset.FirstAvailableHitWindows, nested.HitWindows); + } + + [Test] + public void TestResultIfParentHitWindowsIsNotEmpty() + { + var testObject = new TestHitObject(hitWindows: new HitWindows()); + HitObject nested = new TestHitObject(hitWindows: new HitWindows()); + testObject.AddNested(nested); + testDrawableRuleset.HitObjects = new List { testObject }; + + // If the parent window is not empty, return that immediately + Assert.AreSame(testDrawableRuleset.FirstAvailableHitWindows, testObject.HitWindows); + } + + [Test] + public void TestResultIfParentAndChildHitWindowsAreEmpty() + { + var firstObject = new TestHitObject(hitWindows: HitWindows.Empty); + HitObject nested = new TestHitObject(hitWindows: HitWindows.Empty); + firstObject.AddNested(nested); + + var secondObject = new TestHitObject(hitWindows: new HitWindows()); + testDrawableRuleset.HitObjects = new List { firstObject, secondObject }; + + // If the parent and child windows are empty, return the next object if window isn't empty + Assert.AreSame(testDrawableRuleset.FirstAvailableHitWindows, secondObject.HitWindows); + } + + [Test] + public void TestResultIfAllHitWindowsAreEmpty() + { + var firstObject = new TestHitObject(hitWindows: HitWindows.Empty); + HitObject nested = new TestHitObject(hitWindows: HitWindows.Empty); + firstObject.AddNested(nested); + + testDrawableRuleset.HitObjects = new List { firstObject }; + + // If all windows are empty, this should return null + Assert.IsNull(testDrawableRuleset.FirstAvailableHitWindows); + } + + [SuppressMessage("ReSharper", "UnassignedGetOnlyAutoProperty")] + private class TestDrawableRuleset : DrawableRuleset + { + public List HitObjects; + public override IEnumerable Objects => HitObjects; + + public override event Action NewResult; + public override event Action RevertResult; + + public override Playfield Playfield { get; } + public override Container Overlays { get; } + public override Container FrameStableComponents { get; } + public override IFrameStableClock FrameStableClock { get; } + internal override bool FrameStablePlayback { get; set; } + public override IReadOnlyList Mods { get; } + + public override double GameplayStartTime { get; } + public override GameplayCursorContainer Cursor { get; } + + public TestDrawableRuleset() + : base(new OsuRuleset()) + { + // won't compile without this. + NewResult?.Invoke(null); + RevertResult?.Invoke(null); + } + + public override void SetReplayScore(Score replayScore) => throw new NotImplementedException(); + + public override void SetRecordTarget(Score score) => throw new NotImplementedException(); + + public override void RequestResume(Action continueResume) => throw new NotImplementedException(); + + public override void CancelResume() => throw new NotImplementedException(); + } + } + + public class TestHitObject : HitObject + { + public TestHitObject(HitWindows hitWindows) + { + HitWindows = hitWindows; + HitWindows.SetDifficulty(0.5f); + } + + public new void AddNested(HitObject nested) => base.AddNested(nested); + } +} From 1facdcf4838c4948f03d58c8b23fcf8dd5cd8759 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Sun, 4 Jul 2021 23:23:24 +0200 Subject: [PATCH 0138/2442] Apply changes to tests --- .../NonVisual/FirstAvailableHitWindowTest.cs | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/NonVisual/FirstAvailableHitWindowTest.cs b/osu.Game.Tests/NonVisual/FirstAvailableHitWindowTest.cs index 92766d8334..2f1ad194bb 100644 --- a/osu.Game.Tests/NonVisual/FirstAvailableHitWindowTest.cs +++ b/osu.Game.Tests/NonVisual/FirstAvailableHitWindowTest.cs @@ -23,51 +23,47 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestResultIfOnlyParentHitWindowIsEmpty() { - var testObject = new TestHitObject(hitWindows: HitWindows.Empty); - HitObject nested = new TestHitObject(hitWindows: new HitWindows()); + var testObject = new TestHitObject(HitWindows.Empty); + HitObject nested = new TestHitObject(new HitWindows()); testObject.AddNested(nested); testDrawableRuleset.HitObjects = new List { testObject }; - // If the parent window is empty, but its nested object isn't, return the nested object Assert.AreSame(testDrawableRuleset.FirstAvailableHitWindows, nested.HitWindows); } [Test] public void TestResultIfParentHitWindowsIsNotEmpty() { - var testObject = new TestHitObject(hitWindows: new HitWindows()); - HitObject nested = new TestHitObject(hitWindows: new HitWindows()); + var testObject = new TestHitObject(new HitWindows()); + HitObject nested = new TestHitObject(new HitWindows()); testObject.AddNested(nested); testDrawableRuleset.HitObjects = new List { testObject }; - // If the parent window is not empty, return that immediately Assert.AreSame(testDrawableRuleset.FirstAvailableHitWindows, testObject.HitWindows); } [Test] public void TestResultIfParentAndChildHitWindowsAreEmpty() { - var firstObject = new TestHitObject(hitWindows: HitWindows.Empty); - HitObject nested = new TestHitObject(hitWindows: HitWindows.Empty); + var firstObject = new TestHitObject(HitWindows.Empty); + HitObject nested = new TestHitObject(HitWindows.Empty); firstObject.AddNested(nested); - var secondObject = new TestHitObject(hitWindows: new HitWindows()); + var secondObject = new TestHitObject(new HitWindows()); testDrawableRuleset.HitObjects = new List { firstObject, secondObject }; - // If the parent and child windows are empty, return the next object if window isn't empty Assert.AreSame(testDrawableRuleset.FirstAvailableHitWindows, secondObject.HitWindows); } [Test] public void TestResultIfAllHitWindowsAreEmpty() { - var firstObject = new TestHitObject(hitWindows: HitWindows.Empty); - HitObject nested = new TestHitObject(hitWindows: HitWindows.Empty); + var firstObject = new TestHitObject(HitWindows.Empty); + HitObject nested = new TestHitObject(HitWindows.Empty); firstObject.AddNested(nested); testDrawableRuleset.HitObjects = new List { firstObject }; - // If all windows are empty, this should return null Assert.IsNull(testDrawableRuleset.FirstAvailableHitWindows); } From 216e52d6d0333d730ef13efdbd09534ee0c59e62 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Sun, 4 Jul 2021 23:24:17 +0200 Subject: [PATCH 0139/2442] Avoid using single letter variable names --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index cccd73f19f..daf46dcdcc 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -489,15 +489,15 @@ namespace osu.Game.Rulesets.UI { get { - foreach (var h in Objects) + foreach (var hitObject in Objects) { - if (h.HitWindows.WindowFor(HitResult.Miss) > 0) - return h.HitWindows; + if (hitObject.HitWindows.WindowFor(HitResult.Miss) > 0) + return hitObject.HitWindows; - foreach (var n in h.NestedHitObjects) + foreach (var nested in hitObject.NestedHitObjects) { - if (n.HitWindows.WindowFor(HitResult.Miss) > 0) - return n.HitWindows; + if (nested.HitWindows.WindowFor(HitResult.Miss) > 0) + return nested.HitWindows; } } From cc877f88e2fd97dcd0556e233067a7cc0e2b9d57 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 5 Jul 2021 10:13:01 +0900 Subject: [PATCH 0140/2442] Fix inspection (create a new ruleset every time) --- osu.Game.Tests/NonVisual/FirstAvailableHitWindowTest.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/NonVisual/FirstAvailableHitWindowTest.cs b/osu.Game.Tests/NonVisual/FirstAvailableHitWindowTest.cs index 2f1ad194bb..aed2a5c122 100644 --- a/osu.Game.Tests/NonVisual/FirstAvailableHitWindowTest.cs +++ b/osu.Game.Tests/NonVisual/FirstAvailableHitWindowTest.cs @@ -18,7 +18,13 @@ namespace osu.Game.Tests.NonVisual { public class FirstAvailableHitWindowsTest { - private TestDrawableRuleset testDrawableRuleset = new TestDrawableRuleset(); + private TestDrawableRuleset testDrawableRuleset; + + [SetUp] + public void Setup() + { + testDrawableRuleset = new TestDrawableRuleset(); + } [Test] public void TestResultIfOnlyParentHitWindowIsEmpty() From eecf4af0293384a6fe3c0da24eaf322529ba7951 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Mon, 5 Jul 2021 09:16:01 +0800 Subject: [PATCH 0141/2442] Rename `getSliderBoundingBox` and add comments --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index abc7db0a82..4c856ef4a0 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -148,18 +148,19 @@ namespace osu.Game.Rulesets.Osu.Mods /// The that this slider has been shifted by. private Vector2 moveSliderIntoPlayfield(Slider slider, RandomObjectInfo currentObjectInfo) { - var boundingBox = getSliderBoundingBox(slider); + var area = getSliderPlacementArea(slider); var prevPosition = slider.Position; + // Clamp slider position to the placement area // If the slider is larger than the playfield, force it to stay at the original position - var newX = boundingBox.Width < 0 + var newX = area.Width < 0 ? currentObjectInfo.PositionOriginal.X - : Math.Clamp(slider.Position.X, boundingBox.Left, boundingBox.Right); + : Math.Clamp(slider.Position.X, area.Left, area.Right); - var newY = boundingBox.Height < 0 + var newY = area.Height < 0 ? currentObjectInfo.PositionOriginal.Y - : Math.Clamp(slider.Position.Y, boundingBox.Top, boundingBox.Bottom); + : Math.Clamp(slider.Position.Y, area.Top, area.Bottom); slider.Position = new Vector2(newX, newY); @@ -191,15 +192,21 @@ namespace osu.Game.Rulesets.Osu.Mods } /// - /// Calculates the bounding box of a 's position for the slider to be fully inside of the playfield. + /// Calculates a that includes all possible positions of the slider such that + /// the entire slider is inside the playfield. /// - private RectangleF getSliderBoundingBox(Slider slider) + /// + /// If the slider is larger than the playfield, the returned may have negative width/height. + /// + private RectangleF getSliderPlacementArea(Slider slider) { var pathPositions = new List(); slider.Path.GetPathToProgress(pathPositions, 0, 1); + // Initially, assume that the slider can be placed anywhere in the playfield var box = new RectangleF(Vector2.Zero, OsuPlayfield.BASE_SIZE); + // Then narrow down the area with each path position foreach (var pos in pathPositions) { // Reduce Width and Height accordingly after increasing X and Y @@ -216,6 +223,7 @@ namespace osu.Game.Rulesets.Osu.Mods box.Height = Math.Min(box.Height, OsuPlayfield.BASE_SIZE.Y - pos.Y - box.Y); } + // Reduce the area by slider radius, so that the slider fits inside the playfield completely var radius = (float)slider.Radius; box.X += radius; From f510ef9153618d238ea534edb943b1c732b95690 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 5 Jul 2021 10:49:55 +0900 Subject: [PATCH 0142/2442] Move `previousContext` assign within `lock` to make things look safer Not an actual requirement, but no harm. --- osu.Game/Database/RealmContextFactory.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/RealmContextFactory.cs b/osu.Game/Database/RealmContextFactory.cs index b0241fb93c..ed3dc01f15 100644 --- a/osu.Game/Database/RealmContextFactory.cs +++ b/osu.Game/Database/RealmContextFactory.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using System.Threading; using osu.Framework.Allocation; using osu.Framework.Development; @@ -166,11 +167,15 @@ namespace osu.Game.Database private void flushContexts() { Logger.Log(@"Flushing realm contexts...", LoggingTarget.Database); + Debug.Assert(blockingLock.CurrentCount == 0); - var previousContext = context; + Realm previousContext; lock (updateContextLock) + { + previousContext = context; context = null; + } // wait for all threaded usages to finish while (active_usages.Value > 0) From bfb603cfebed6b77b192c87309365f108a6d8d11 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Mon, 5 Jul 2021 09:51:24 +0800 Subject: [PATCH 0143/2442] Change the test for unimplemented mod to use a mock ruleset and mod --- .../TestSceneModSelectOverlay.cs | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index df8ef92a05..07bd28ac64 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -109,8 +109,6 @@ namespace osu.Game.Tests.Visual.UserInterface var doubleTimeMod = harderMods.OfType().FirstOrDefault(m => m.Mods.Any(a => a is OsuModDoubleTime)); - var targetMod = conversionMods.FirstOrDefault(m => m is OsuModTarget); - var easy = easierMods.FirstOrDefault(m => m is OsuModEasy); var hardRock = harderMods.FirstOrDefault(m => m is OsuModHardRock); @@ -118,8 +116,6 @@ namespace osu.Game.Tests.Visual.UserInterface testMultiMod(doubleTimeMod); testIncompatibleMods(easy, hardRock); testDeselectAll(easierMods.Where(m => !(m is MultiMod))); - - testUnimplementedMod(targetMod); } [Test] @@ -249,6 +245,19 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("DT + HD still selected", () => modSelect.ChildrenOfType().Count(b => b.Selected) == 2); } + [Test] + public void TestUnimplementedModIsUnselectable() + { + var testRuleset = new TestUnimplementedModOsuRuleset(); + changeTestRuleset(testRuleset.RulesetInfo); + + var conversionMods = testRuleset.GetModsFor(ModType.Conversion); + + var unimplementedMod = conversionMods.FirstOrDefault(m => m is TestUnimplementedMod); + + testUnimplementedMod(unimplementedMod); + } + private void testSingleMod(Mod mod) { selectNext(mod); @@ -343,6 +352,12 @@ namespace osu.Game.Tests.Visual.UserInterface waitForLoad(); } + private void changeTestRuleset(RulesetInfo rulesetInfo) + { + AddStep($"change ruleset to {rulesetInfo.Name}", () => { Ruleset.Value = rulesetInfo; }); + waitForLoad(); + } + private void waitForLoad() => AddUntilStep("wait for icons to load", () => modSelect.AllLoaded); @@ -401,5 +416,24 @@ namespace osu.Game.Tests.Visual.UserInterface { protected override bool Stacked => false; } + + private class TestUnimplementedMod : Mod + { + public override string Name => "Unimplemented mod"; + public override string Acronym => "UM"; + public override string Description => "A mod that is not implemented."; + public override double ScoreMultiplier => 1; + public override ModType Type => ModType.Conversion; + } + + private class TestUnimplementedModOsuRuleset : OsuRuleset + { + public override IEnumerable GetModsFor(ModType type) + { + if (type == ModType.Conversion) return base.GetModsFor(type).Concat(new[] { new TestUnimplementedMod() }); + + return base.GetModsFor(type); + } + } } } From 3c371404264a9f792f28ace262e6d7ebe8296ca1 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Mon, 5 Jul 2021 09:52:13 +0800 Subject: [PATCH 0144/2442] Remove an unused local variable --- osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 07bd28ac64..4a1d90d871 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -103,7 +103,6 @@ namespace osu.Game.Tests.Visual.UserInterface var easierMods = osu.GetModsFor(ModType.DifficultyReduction); var harderMods = osu.GetModsFor(ModType.DifficultyIncrease); - var conversionMods = osu.GetModsFor(ModType.Conversion); var noFailMod = osu.GetModsFor(ModType.DifficultyReduction).FirstOrDefault(m => m is OsuModNoFail); From 1e4beddd2d37ef522045a35c1bb263b46e4579c4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 5 Jul 2021 15:41:48 +0900 Subject: [PATCH 0145/2442] Disable foreign key enforcing at an sqlite level --- osu.Game/Database/OsuDbContext.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index e0c0f56cb3..68d186c65d 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -77,6 +77,9 @@ namespace osu.Game.Database { cmd.CommandText = "PRAGMA journal_mode=WAL;"; cmd.ExecuteNonQuery(); + + cmd.CommandText = "PRAGMA foreign_keys=OFF;"; + cmd.ExecuteNonQuery(); } } catch From 96c0ab8dedbc5702977f07785643e8eb2a5d41c7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 5 Jul 2021 17:11:59 +0900 Subject: [PATCH 0146/2442] Adjust last frame position when not waiting --- osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index 7b0cf651c8..a9b5a2d0ef 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -249,6 +249,18 @@ namespace osu.Game.Rulesets.Osu.Replays ((lastPosition - targetPos).Length > h.Radius * (1.5 + 100.0 / timeDifference) || // Either the distance is big enough timeDifference >= 266)) // ... or the beats are slow enough to tap anyway. { + OsuReplayFrame lastLastFrame = Frames.Count >= 2 ? (OsuReplayFrame)Frames[^2] : null; + + // The last frame may be a key-up frame if it shares a position with the second-last frame. + // If it is a key-up frame and its time occurs after the "wait time" (i.e. there was no wait period), adjust its position to begin eased movement instantaneously. + if (lastLastFrame?.Position == lastFrame.Position && lastFrame.Time >= waitTime) + { + // [lastLastFrame] ... [lastFrame] ... [current frame] + // We want to find the cursor position at lastFrame, so interpolate between lastLastFrame and the new target position. + lastFrame.Position = Interpolation.ValueAt(lastFrame.Time, lastFrame.Position, targetPos, lastLastFrame.Time, h.StartTime, easing); + lastPosition = lastFrame.Position; + } + // Perform eased movement for (double time = lastFrame.Time + GetFrameDelay(lastFrame.Time); time < h.StartTime; time += GetFrameDelay(time)) { From 6a2c0f772e84cd9dddb3ca9b8f35295956b6d990 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 5 Jul 2021 17:22:48 +0900 Subject: [PATCH 0147/2442] Always apply easing, adjust heuristic a bit --- .../Replays/OsuAutoGenerator.cs | 49 ++++++++----------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index a9b5a2d0ef..0df5909d50 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -240,40 +240,33 @@ namespace osu.Game.Rulesets.Osu.Replays AddFrameToReplay(lastFrame); } - Vector2 lastPosition = lastFrame.Position; - double timeDifference = ApplyModsToTimeDelta(lastFrame.Time, h.StartTime); - // Only "snap" to hitcircles if they are far enough apart. As the time between hitcircles gets shorter the snapping threshold goes up. - if (timeDifference > 0 && // Sanity checks - ((lastPosition - targetPos).Length > h.Radius * (1.5 + 100.0 / timeDifference) || // Either the distance is big enough - timeDifference >= 266)) // ... or the beats are slow enough to tap anyway. + OsuReplayFrame lastLastFrame = Frames.Count >= 2 ? (OsuReplayFrame)Frames[^2] : null; + + // The last frame may be a key-up frame if it shares a position with the second-last frame. + // If it is a key-up frame and its time occurs after the "wait time" (i.e. there was no wait period), adjust its position to begin eased movement instantaneously. + if (lastLastFrame?.Position == lastFrame.Position && lastFrame.Time >= waitTime) { - OsuReplayFrame lastLastFrame = Frames.Count >= 2 ? (OsuReplayFrame)Frames[^2] : null; - - // The last frame may be a key-up frame if it shares a position with the second-last frame. - // If it is a key-up frame and its time occurs after the "wait time" (i.e. there was no wait period), adjust its position to begin eased movement instantaneously. - if (lastLastFrame?.Position == lastFrame.Position && lastFrame.Time >= waitTime) - { - // [lastLastFrame] ... [lastFrame] ... [current frame] - // We want to find the cursor position at lastFrame, so interpolate between lastLastFrame and the new target position. - lastFrame.Position = Interpolation.ValueAt(lastFrame.Time, lastFrame.Position, targetPos, lastLastFrame.Time, h.StartTime, easing); - lastPosition = lastFrame.Position; - } - - // Perform eased movement - for (double time = lastFrame.Time + GetFrameDelay(lastFrame.Time); time < h.StartTime; time += GetFrameDelay(time)) - { - Vector2 currentPosition = Interpolation.ValueAt(time, lastPosition, targetPos, lastFrame.Time, h.StartTime, easing); - AddFrameToReplay(new OsuReplayFrame((int)time, new Vector2(currentPosition.X, currentPosition.Y)) { Actions = lastFrame.Actions }); - } - - buttonIndex = 0; + // [lastLastFrame] ... [lastFrame] ... [current frame] + // We want to find the cursor position at lastFrame, so interpolate between lastLastFrame and the new target position. + lastFrame.Position = Interpolation.ValueAt(lastFrame.Time, lastFrame.Position, targetPos, lastLastFrame.Time, h.StartTime, easing); } - else + + Vector2 lastPosition = lastFrame.Position; + + // Perform eased movement. + for (double time = lastFrame.Time + GetFrameDelay(lastFrame.Time); time < h.StartTime; time += GetFrameDelay(time)) { + Vector2 currentPosition = Interpolation.ValueAt(time, lastPosition, targetPos, lastFrame.Time, h.StartTime, easing); + AddFrameToReplay(new OsuReplayFrame((int)time, new Vector2(currentPosition.X, currentPosition.Y)) { Actions = lastFrame.Actions }); + } + + // Start alternating once the time separation is too small (equivalent 120BPM @ 1/4 divisor). + if (timeDifference > 0 && timeDifference < 125) buttonIndex++; - } + else + buttonIndex = 0; } /// From 12ca845e55d7f0bf1fe4594a3f0330307b39a780 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 5 Jul 2021 17:24:23 +0900 Subject: [PATCH 0148/2442] Use marker class (cleanup) --- .../Replays/OsuAutoGenerator.cs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index 0df5909d50..37d3e9d57a 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -233,20 +233,20 @@ namespace osu.Game.Rulesets.Osu.Replays // Wait until Auto could "see and react" to the next note. double waitTime = h.StartTime - Math.Max(0.0, h.TimePreempt - getReactionTime(h.StartTime - h.TimePreempt)); + bool hasWaited = false; if (waitTime > lastFrame.Time) { lastFrame = new OsuReplayFrame(waitTime, lastFrame.Position) { Actions = lastFrame.Actions }; + hasWaited = true; AddFrameToReplay(lastFrame); } double timeDifference = ApplyModsToTimeDelta(lastFrame.Time, h.StartTime); - OsuReplayFrame lastLastFrame = Frames.Count >= 2 ? (OsuReplayFrame)Frames[^2] : null; - // The last frame may be a key-up frame if it shares a position with the second-last frame. - // If it is a key-up frame and its time occurs after the "wait time" (i.e. there was no wait period), adjust its position to begin eased movement instantaneously. - if (lastLastFrame?.Position == lastFrame.Position && lastFrame.Time >= waitTime) + // If the last frame is a key-up frame and there has been no wait period, adjust the last frame's position such that it begins eased movement instantaneously. + if (lastLastFrame != null && lastFrame is OsuKeyUpReplayFrame && !hasWaited) { // [lastLastFrame] ... [lastFrame] ... [current frame] // We want to find the cursor position at lastFrame, so interpolate between lastLastFrame and the new target position. @@ -289,7 +289,7 @@ namespace osu.Game.Rulesets.Osu.Replays // TODO: Why do we delay 1 ms if the object is a spinner? There already is KEY_UP_DELAY from hEndTime. double hEndTime = h.GetEndTime() + KEY_UP_DELAY; int endDelay = h is Spinner ? 1 : 0; - var endFrame = new OsuReplayFrame(hEndTime + endDelay, new Vector2(h.StackedEndPosition.X, h.StackedEndPosition.Y)); + var endFrame = new OsuKeyUpReplayFrame(hEndTime + endDelay, new Vector2(h.StackedEndPosition.X, h.StackedEndPosition.Y)); // Decrement because we want the previous frame, not the next one int index = FindInsertionIndex(startFrame) - 1; @@ -386,5 +386,13 @@ namespace osu.Game.Rulesets.Osu.Replays } #endregion + + private class OsuKeyUpReplayFrame : OsuReplayFrame + { + public OsuKeyUpReplayFrame(double time, Vector2 position) + : base(time, position) + { + } + } } } From 229bba14e6261142692bda7bba8bf5cdb7f325eb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 5 Jul 2021 17:45:08 +0900 Subject: [PATCH 0149/2442] Fix master clock becoming incorrectly paused when all spectator players are too far ahead --- .../OnlinePlay/Multiplayer/Spectate/CatchUpSyncManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/CatchUpSyncManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/CatchUpSyncManager.cs index 94278a47b6..cf0dfbb585 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/CatchUpSyncManager.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/CatchUpSyncManager.cs @@ -130,6 +130,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate // This is a quiet case in which the catchup is done by the master clock, so IsCatchingUp is not set on the player clock. if (timeDelta < -SYNC_TARGET) { + // Importantly, set the clock to a non-catchup state. if this isn't done, updateMasterState may incorrectly pause the master clock + // when it is required to be running (ie. if all players are ahead of the master). + clock.IsCatchingUp = false; clock.Stop(); continue; } From 2b8efe21caeb6c0450388749089183a184d42cf8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 5 Jul 2021 17:52:10 +0900 Subject: [PATCH 0150/2442] Don't ease with 0 time difference --- .../Replays/OsuAutoGenerator.cs | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index 37d3e9d57a..20a5829274 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -245,21 +245,25 @@ namespace osu.Game.Rulesets.Osu.Replays double timeDifference = ApplyModsToTimeDelta(lastFrame.Time, h.StartTime); OsuReplayFrame lastLastFrame = Frames.Count >= 2 ? (OsuReplayFrame)Frames[^2] : null; - // If the last frame is a key-up frame and there has been no wait period, adjust the last frame's position such that it begins eased movement instantaneously. - if (lastLastFrame != null && lastFrame is OsuKeyUpReplayFrame && !hasWaited) + if (timeDifference > 0) { - // [lastLastFrame] ... [lastFrame] ... [current frame] - // We want to find the cursor position at lastFrame, so interpolate between lastLastFrame and the new target position. - lastFrame.Position = Interpolation.ValueAt(lastFrame.Time, lastFrame.Position, targetPos, lastLastFrame.Time, h.StartTime, easing); - } + // If the last frame is a key-up frame and there has been no wait period, adjust the last frame's position such that it begins eased movement instantaneously. + if (lastLastFrame != null && lastFrame is OsuKeyUpReplayFrame && !hasWaited + && lastFrame.Time > lastLastFrame.Time) // + { + // [lastLastFrame] ... [lastFrame] ... [current frame] + // We want to find the cursor position at lastFrame, so interpolate between lastLastFrame and the new target position. + lastFrame.Position = Interpolation.ValueAt(lastFrame.Time, lastFrame.Position, targetPos, lastLastFrame.Time, h.StartTime, easing); + } - Vector2 lastPosition = lastFrame.Position; + Vector2 lastPosition = lastFrame.Position; - // Perform eased movement. - for (double time = lastFrame.Time + GetFrameDelay(lastFrame.Time); time < h.StartTime; time += GetFrameDelay(time)) - { - Vector2 currentPosition = Interpolation.ValueAt(time, lastPosition, targetPos, lastFrame.Time, h.StartTime, easing); - AddFrameToReplay(new OsuReplayFrame((int)time, new Vector2(currentPosition.X, currentPosition.Y)) { Actions = lastFrame.Actions }); + // Perform eased movement. + for (double time = lastFrame.Time + GetFrameDelay(lastFrame.Time); time < h.StartTime; time += GetFrameDelay(time)) + { + Vector2 currentPosition = Interpolation.ValueAt(time, lastPosition, targetPos, lastFrame.Time, h.StartTime, easing); + AddFrameToReplay(new OsuReplayFrame((int)time, new Vector2(currentPosition.X, currentPosition.Y)) { Actions = lastFrame.Actions }); + } } // Start alternating once the time separation is too small (equivalent 120BPM @ 1/4 divisor). From 7645da7d379b19b6d9a106efd4fc463d0eaccc69 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 5 Jul 2021 18:20:04 +0900 Subject: [PATCH 0151/2442] Fix incorrect filename --- .../NonVisual/FirstAvailableHitWindowTest.cs | 104 ---------------- .../NonVisual/FirstAvailableHitWindowsTest.cs | 112 ++++++++++++++++++ 2 files changed, 112 insertions(+), 104 deletions(-) create mode 100644 osu.Game.Tests/NonVisual/FirstAvailableHitWindowsTest.cs diff --git a/osu.Game.Tests/NonVisual/FirstAvailableHitWindowTest.cs b/osu.Game.Tests/NonVisual/FirstAvailableHitWindowTest.cs index aed2a5c122..51e15d2426 100644 --- a/osu.Game.Tests/NonVisual/FirstAvailableHitWindowTest.cs +++ b/osu.Game.Tests/NonVisual/FirstAvailableHitWindowTest.cs @@ -1,115 +1,11 @@ // 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.Diagnostics.CodeAnalysis; -using NUnit.Framework; -using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; -using osu.Game.Scoring; namespace osu.Game.Tests.NonVisual { - public class FirstAvailableHitWindowsTest - { - private TestDrawableRuleset testDrawableRuleset; - - [SetUp] - public void Setup() - { - testDrawableRuleset = new TestDrawableRuleset(); - } - - [Test] - public void TestResultIfOnlyParentHitWindowIsEmpty() - { - var testObject = new TestHitObject(HitWindows.Empty); - HitObject nested = new TestHitObject(new HitWindows()); - testObject.AddNested(nested); - testDrawableRuleset.HitObjects = new List { testObject }; - - Assert.AreSame(testDrawableRuleset.FirstAvailableHitWindows, nested.HitWindows); - } - - [Test] - public void TestResultIfParentHitWindowsIsNotEmpty() - { - var testObject = new TestHitObject(new HitWindows()); - HitObject nested = new TestHitObject(new HitWindows()); - testObject.AddNested(nested); - testDrawableRuleset.HitObjects = new List { testObject }; - - Assert.AreSame(testDrawableRuleset.FirstAvailableHitWindows, testObject.HitWindows); - } - - [Test] - public void TestResultIfParentAndChildHitWindowsAreEmpty() - { - var firstObject = new TestHitObject(HitWindows.Empty); - HitObject nested = new TestHitObject(HitWindows.Empty); - firstObject.AddNested(nested); - - var secondObject = new TestHitObject(new HitWindows()); - testDrawableRuleset.HitObjects = new List { firstObject, secondObject }; - - Assert.AreSame(testDrawableRuleset.FirstAvailableHitWindows, secondObject.HitWindows); - } - - [Test] - public void TestResultIfAllHitWindowsAreEmpty() - { - var firstObject = new TestHitObject(HitWindows.Empty); - HitObject nested = new TestHitObject(HitWindows.Empty); - firstObject.AddNested(nested); - - testDrawableRuleset.HitObjects = new List { firstObject }; - - Assert.IsNull(testDrawableRuleset.FirstAvailableHitWindows); - } - - [SuppressMessage("ReSharper", "UnassignedGetOnlyAutoProperty")] - private class TestDrawableRuleset : DrawableRuleset - { - public List HitObjects; - public override IEnumerable Objects => HitObjects; - - public override event Action NewResult; - public override event Action RevertResult; - - public override Playfield Playfield { get; } - public override Container Overlays { get; } - public override Container FrameStableComponents { get; } - public override IFrameStableClock FrameStableClock { get; } - internal override bool FrameStablePlayback { get; set; } - public override IReadOnlyList Mods { get; } - - public override double GameplayStartTime { get; } - public override GameplayCursorContainer Cursor { get; } - - public TestDrawableRuleset() - : base(new OsuRuleset()) - { - // won't compile without this. - NewResult?.Invoke(null); - RevertResult?.Invoke(null); - } - - public override void SetReplayScore(Score replayScore) => throw new NotImplementedException(); - - public override void SetRecordTarget(Score score) => throw new NotImplementedException(); - - public override void RequestResume(Action continueResume) => throw new NotImplementedException(); - - public override void CancelResume() => throw new NotImplementedException(); - } - } - public class TestHitObject : HitObject { public TestHitObject(HitWindows hitWindows) diff --git a/osu.Game.Tests/NonVisual/FirstAvailableHitWindowsTest.cs b/osu.Game.Tests/NonVisual/FirstAvailableHitWindowsTest.cs new file mode 100644 index 0000000000..d4364b15e7 --- /dev/null +++ b/osu.Game.Tests/NonVisual/FirstAvailableHitWindowsTest.cs @@ -0,0 +1,112 @@ +// 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.Diagnostics.CodeAnalysis; +using NUnit.Framework; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Scoring; + +namespace osu.Game.Tests.NonVisual +{ + public class FirstAvailableHitWindowsTest + { + private TestDrawableRuleset testDrawableRuleset; + + [SetUp] + public void Setup() + { + testDrawableRuleset = new TestDrawableRuleset(); + } + + [Test] + public void TestResultIfOnlyParentHitWindowIsEmpty() + { + var testObject = new TestHitObject(HitWindows.Empty); + HitObject nested = new TestHitObject(new HitWindows()); + testObject.AddNested(nested); + testDrawableRuleset.HitObjects = new List { testObject }; + + Assert.AreSame(testDrawableRuleset.FirstAvailableHitWindows, nested.HitWindows); + } + + [Test] + public void TestResultIfParentHitWindowsIsNotEmpty() + { + var testObject = new TestHitObject(new HitWindows()); + HitObject nested = new TestHitObject(new HitWindows()); + testObject.AddNested(nested); + testDrawableRuleset.HitObjects = new List { testObject }; + + Assert.AreSame(testDrawableRuleset.FirstAvailableHitWindows, testObject.HitWindows); + } + + [Test] + public void TestResultIfParentAndChildHitWindowsAreEmpty() + { + var firstObject = new TestHitObject(HitWindows.Empty); + HitObject nested = new TestHitObject(HitWindows.Empty); + firstObject.AddNested(nested); + + var secondObject = new TestHitObject(new HitWindows()); + testDrawableRuleset.HitObjects = new List { firstObject, secondObject }; + + Assert.AreSame(testDrawableRuleset.FirstAvailableHitWindows, secondObject.HitWindows); + } + + [Test] + public void TestResultIfAllHitWindowsAreEmpty() + { + var firstObject = new TestHitObject(HitWindows.Empty); + HitObject nested = new TestHitObject(HitWindows.Empty); + firstObject.AddNested(nested); + + testDrawableRuleset.HitObjects = new List { firstObject }; + + Assert.IsNull(testDrawableRuleset.FirstAvailableHitWindows); + } + + [SuppressMessage("ReSharper", "UnassignedGetOnlyAutoProperty")] + private class TestDrawableRuleset : DrawableRuleset + { + public List HitObjects; + public override IEnumerable Objects => HitObjects; + + public override event Action NewResult; + public override event Action RevertResult; + + public override Playfield Playfield { get; } + public override Container Overlays { get; } + public override Container FrameStableComponents { get; } + public override IFrameStableClock FrameStableClock { get; } + internal override bool FrameStablePlayback { get; set; } + public override IReadOnlyList Mods { get; } + + public override double GameplayStartTime { get; } + public override GameplayCursorContainer Cursor { get; } + + public TestDrawableRuleset() + : base(new OsuRuleset()) + { + // won't compile without this. + NewResult?.Invoke(null); + RevertResult?.Invoke(null); + } + + public override void SetReplayScore(Score replayScore) => throw new NotImplementedException(); + + public override void SetRecordTarget(Score score) => throw new NotImplementedException(); + + public override void RequestResume(Action continueResume) => throw new NotImplementedException(); + + public override void CancelResume() => throw new NotImplementedException(); + } + } +} From 695af31c58d696f4cd6d5897ea56d1c80e3f3167 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 5 Jul 2021 18:38:02 +0900 Subject: [PATCH 0152/2442] Start alternating at 225BPM as previously --- osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index 20a5829274..68743c2a86 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -266,8 +266,8 @@ namespace osu.Game.Rulesets.Osu.Replays } } - // Start alternating once the time separation is too small (equivalent 120BPM @ 1/4 divisor). - if (timeDifference > 0 && timeDifference < 125) + // Start alternating once the time separation is too small (faster than ~225BPM). + if (timeDifference > 0 && timeDifference < 266) buttonIndex++; else buttonIndex = 0; From 7d6ab08bb3c1bd870216351379587f66482f62fb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 5 Jul 2021 18:49:09 +0900 Subject: [PATCH 0153/2442] Remove unnecessary conditional --- osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index 68743c2a86..73dbcbd501 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -248,8 +248,7 @@ namespace osu.Game.Rulesets.Osu.Replays if (timeDifference > 0) { // If the last frame is a key-up frame and there has been no wait period, adjust the last frame's position such that it begins eased movement instantaneously. - if (lastLastFrame != null && lastFrame is OsuKeyUpReplayFrame && !hasWaited - && lastFrame.Time > lastLastFrame.Time) // + if (lastLastFrame != null && lastFrame is OsuKeyUpReplayFrame && !hasWaited) { // [lastLastFrame] ... [lastFrame] ... [current frame] // We want to find the cursor position at lastFrame, so interpolate between lastLastFrame and the new target position. From 8b7ccdc8b5c591120f09b30baed07c919082a864 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 5 Jul 2021 18:51:23 +0900 Subject: [PATCH 0154/2442] Adjust comment --- osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index 73dbcbd501..b88bf9108b 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -257,7 +257,7 @@ namespace osu.Game.Rulesets.Osu.Replays Vector2 lastPosition = lastFrame.Position; - // Perform eased movement. + // Perform the rest of the eased movement until the target position is reached. for (double time = lastFrame.Time + GetFrameDelay(lastFrame.Time); time < h.StartTime; time += GetFrameDelay(time)) { Vector2 currentPosition = Interpolation.ValueAt(time, lastPosition, targetPos, lastFrame.Time, h.StartTime, easing); From cd2916f778c7c740793a1b72502461269851368e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 5 Jul 2021 18:56:59 +0900 Subject: [PATCH 0155/2442] Fix remaining incorrect filename --- .../NonVisual/FirstAvailableHitWindowTest.cs | 19 ------------------- .../NonVisual/FirstAvailableHitWindowsTest.cs | 11 +++++++++++ 2 files changed, 11 insertions(+), 19 deletions(-) delete mode 100644 osu.Game.Tests/NonVisual/FirstAvailableHitWindowTest.cs diff --git a/osu.Game.Tests/NonVisual/FirstAvailableHitWindowTest.cs b/osu.Game.Tests/NonVisual/FirstAvailableHitWindowTest.cs deleted file mode 100644 index 51e15d2426..0000000000 --- a/osu.Game.Tests/NonVisual/FirstAvailableHitWindowTest.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Tests.NonVisual -{ - public class TestHitObject : HitObject - { - public TestHitObject(HitWindows hitWindows) - { - HitWindows = hitWindows; - HitWindows.SetDifficulty(0.5f); - } - - public new void AddNested(HitObject nested) => base.AddNested(nested); - } -} diff --git a/osu.Game.Tests/NonVisual/FirstAvailableHitWindowsTest.cs b/osu.Game.Tests/NonVisual/FirstAvailableHitWindowsTest.cs index d4364b15e7..97105b6b6a 100644 --- a/osu.Game.Tests/NonVisual/FirstAvailableHitWindowsTest.cs +++ b/osu.Game.Tests/NonVisual/FirstAvailableHitWindowsTest.cs @@ -108,5 +108,16 @@ namespace osu.Game.Tests.NonVisual public override void CancelResume() => throw new NotImplementedException(); } + + public class TestHitObject : HitObject + { + public TestHitObject(HitWindows hitWindows) + { + HitWindows = hitWindows; + HitWindows.SetDifficulty(0.5f); + } + + public new void AddNested(HitObject nested) => base.AddNested(nested); + } } } From d3bb4ddbee09ebcab963add6fcce38945d01b99f Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 5 Jul 2021 19:05:49 +0900 Subject: [PATCH 0156/2442] Add an ad-hoc way to provide dependency to children --- .../Visual/DependencyProvidingContainer.cs | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 osu.Game/Tests/Visual/DependencyProvidingContainer.cs diff --git a/osu.Game/Tests/Visual/DependencyProvidingContainer.cs b/osu.Game/Tests/Visual/DependencyProvidingContainer.cs new file mode 100644 index 0000000000..cd3ae1123b --- /dev/null +++ b/osu.Game/Tests/Visual/DependencyProvidingContainer.cs @@ -0,0 +1,50 @@ +// 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.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Tests.Visual +{ + /// + /// A which providing ad-hoc dependencies to the child drawables. + /// + /// To provide a dependency, specify the dependency type to , then specify the dependency value to either or . + /// For each type specified in , the first value compatible with the type is selected and provided to the children. + /// + /// + /// + /// The and values of the dependencies must be set while this is not loaded. + /// + public class DependencyProvidingContainer : Container + { + /// + /// The types of the dependencies provided to the children. + /// + // TODO: should be an init-only property when C# 9 + public Type[] Types { get; set; } = Array.Empty(); + + /// + /// The dependency values provided to the children. + /// + public object[] Values { get; set; } = Array.Empty(); + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + var dependencyContainer = new DependencyContainer(base.CreateChildDependencies(parent)); + + foreach (var type in Types) + { + object value = Values.FirstOrDefault(v => type.IsInstanceOfType(v)) ?? + Children.FirstOrDefault(d => type.IsInstanceOfType(d)) ?? + throw new InvalidOperationException($"The type {type} is specified in this {nameof(DependencyProvidingContainer)}, but no corresponding value is provided."); + + dependencyContainer.CacheAs(type, value); + } + + return dependencyContainer; + } + } +} From 5a0a223b1b96d2f10630d888d54f68e06a9acb95 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 5 Jul 2021 19:07:29 +0900 Subject: [PATCH 0157/2442] Use `DependencyProvidingContainer` in `TestSceneCatcher` --- .../TestSceneCatcher.cs | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index 8359657f84..0cf5e2b7a0 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -31,22 +31,9 @@ namespace osu.Game.Rulesets.Catch.Tests [Resolved] private OsuConfigManager config { get; set; } - [Cached] - private readonly DroppedObjectContainer droppedObjectContainer; - - private readonly Container trailContainer; - private TestCatcher catcher; - public TestSceneCatcher() - { - Add(trailContainer = new Container - { - Anchor = Anchor.Centre, - Depth = -1 - }); - Add(droppedObjectContainer = new DroppedObjectContainer()); - } + private DroppedObjectContainer droppedObjectContainer; [SetUp] public void SetUp() => Schedule(() => @@ -56,13 +43,24 @@ namespace osu.Game.Rulesets.Catch.Tests CircleSize = 0, }; - if (catcher != null) - Remove(catcher); - - Add(catcher = new TestCatcher(trailContainer, difficulty) + var trailContainer = new Container { + Anchor = Anchor.Centre, + }; + Child = new DependencyProvidingContainer + { + Types = new[] + { + typeof(DroppedObjectContainer), + }, + Children = new Drawable[] + { + droppedObjectContainer = new DroppedObjectContainer(), + catcher = new TestCatcher(trailContainer, difficulty), + trailContainer + }, Anchor = Anchor.Centre - }); + }; }); [Test] From 9c2fed4806217e05312892a2e339bce6cebda0eb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 5 Jul 2021 19:10:27 +0900 Subject: [PATCH 0158/2442] Move setup steps to `SetUpSteps` and add empty test case --- .../Multiplayer/TestSceneMultiplayer.cs | 70 +++++++++---------- 1 file changed, 32 insertions(+), 38 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index c93640e7b5..7f3e17985e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -39,11 +39,6 @@ namespace osu.Game.Tests.Visual.Multiplayer private TestMultiplayer multiplayerScreen; private TestMultiplayerClient client; - public TestSceneMultiplayer() - { - loadMultiplayer(); - } - [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) { @@ -51,18 +46,43 @@ namespace osu.Game.Tests.Visual.Multiplayer Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); } - [SetUp] - public void Setup() => Schedule(() => + public override void SetUpSteps() { - beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); - importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); - }); + base.SetUpSteps(); + + AddStep("import beatmap", () => + { + beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); + importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); + }); + + AddStep("create multiplayer screen", () => multiplayerScreen = new TestMultiplayer()); + + AddStep("load dependencies", () => + { + client = new TestMultiplayerClient(multiplayerScreen.RoomManager); + + // The screen gets suspended so it stops receiving updates. + Child = client; + + LoadScreen(dependenciesScreen = new DependenciesScreen(client)); + }); + + AddUntilStep("wait for dependencies to load", () => dependenciesScreen.IsLoaded); + + AddStep("load multiplayer", () => LoadScreen(multiplayerScreen)); + AddUntilStep("wait for multiplayer to load", () => multiplayerScreen.IsLoaded); + } + + [Test] + public void TestEmpty() + { + // used to test the flow of multiplayer from visual tests. + } [Test] public void TestUserSetToIdleWhenBeatmapDeleted() { - loadMultiplayer(); - createRoom(() => new Room { Name = { Value = "Test Room" }, @@ -85,8 +105,6 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestLocalPlayDoesNotStartWhileSpectatingWithNoBeatmap() { - loadMultiplayer(); - createRoom(() => new Room { Name = { Value = "Test Room" }, @@ -123,8 +141,6 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestLocalPlayStartsWhileSpectatingWhenBeatmapBecomesAvailable() { - loadMultiplayer(); - createRoom(() => new Room { Name = { Value = "Test Room" }, @@ -167,8 +183,6 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestLeaveNavigation() { - loadMultiplayer(); - createRoom(() => new Room { Name = { Value = "Test Room" }, @@ -227,26 +241,6 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for join", () => client.Room != null); } - private void loadMultiplayer() - { - AddStep("create multiplayer screen", () => multiplayerScreen = new TestMultiplayer()); - - AddStep("load dependencies", () => - { - client = new TestMultiplayerClient(multiplayerScreen.RoomManager); - - // The screen gets suspended so it stops receiving updates. - Child = client; - - LoadScreen(dependenciesScreen = new DependenciesScreen(client)); - }); - - AddUntilStep("wait for dependencies to load", () => dependenciesScreen.IsLoaded); - - AddStep("load multiplayer", () => LoadScreen(multiplayerScreen)); - AddUntilStep("wait for multiplayer to load", () => multiplayerScreen.IsLoaded); - } - /// /// Used for the sole purpose of adding as a resolvable dependency. /// From 10e7c846e55a1940ce9230768de1ed0af2f0e45d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 5 Jul 2021 19:41:00 +0900 Subject: [PATCH 0159/2442] Add local `UserLookupCache` to avoid online requests being fired from multiplayer tests --- osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 7f3e17985e..2bb3129f68 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -11,6 +11,7 @@ using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Database; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; @@ -39,6 +40,9 @@ namespace osu.Game.Tests.Visual.Multiplayer private TestMultiplayer multiplayerScreen; private TestMultiplayerClient client; + [Cached(typeof(UserLookupCache))] + private UserLookupCache lookupCache = new TestUserLookupCache(); + [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) { From 9c311a6d8a60db4a3318478b007b4e7e09dc78c6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 5 Jul 2021 19:56:16 +0900 Subject: [PATCH 0160/2442] Add ability to lookup multiple users at once to `UserLookupCache` --- osu.Game/Database/UserLookupCache.cs | 24 ++++++++++++++++++++ osu.Game/Screens/Spectate/SpectatorScreen.cs | 21 +---------------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/osu.Game/Database/UserLookupCache.cs b/osu.Game/Database/UserLookupCache.cs index 19cc211709..e5fd9c82ac 100644 --- a/osu.Game/Database/UserLookupCache.cs +++ b/osu.Game/Database/UserLookupCache.cs @@ -27,6 +27,30 @@ namespace osu.Game.Database [ItemCanBeNull] public Task GetUserAsync(int userId, CancellationToken token = default) => GetAsync(userId, token); + /// + /// Perform an API lookup on the specified users, populating a model. + /// + /// The users to lookup. + /// An optional cancellation token. + /// . + public Task GetUsersAsync(int[] userIds, CancellationToken token = default) + { + var userLookupTasks = new List>(); + + foreach (var u in userIds) + { + userLookupTasks.Add(GetUserAsync(u, token).ContinueWith(task => + { + if (!task.IsCompletedSuccessfully) + return null; + + return task.Result; + }, token)); + } + + return Task.WhenAll(userLookupTasks); + } + protected override async Task ComputeValueAsync(int lookup, CancellationToken token = default) => await queryUser(lookup).ConfigureAwait(false); diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs index 8fc9222f59..cd386a3d48 100644 --- a/osu.Game/Screens/Spectate/SpectatorScreen.cs +++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -61,7 +60,7 @@ namespace osu.Game.Screens.Spectate { base.LoadComplete(); - getAllUsers().ContinueWith(users => Schedule(() => + userLookupCache.GetUsersAsync(userIds.ToArray()).ContinueWith(users => Schedule(() => { foreach (var u in users.Result) userMap[u.Id] = u; @@ -77,24 +76,6 @@ namespace osu.Game.Screens.Spectate })); } - private Task getAllUsers() - { - var userLookupTasks = new List>(); - - foreach (var u in userIds) - { - userLookupTasks.Add(userLookupCache.GetUserAsync(u).ContinueWith(task => - { - if (!task.IsCompletedSuccessfully) - return null; - - return task.Result; - })); - } - - return Task.WhenAll(userLookupTasks); - } - private void beatmapUpdated(ValueChangedEvent> e) { if (!e.NewValue.TryGetTarget(out var beatmapSet)) From 77adf687c6946471301a31a0c52e2857fe4cd5a6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 5 Jul 2021 19:56:36 +0900 Subject: [PATCH 0161/2442] Consume ability to lookup multiple users in `MultiplayerGameplayLeaderboard` Avoids syncrhonously blocking on asynchronous operations (which was leading to LCA starvation in tests). --- .../Multiplayer/MultiplayerPlayer.cs | 4 +-- .../HUD/MultiplayerGameplayLeaderboard.cs | 26 +++++++++---------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index 1bbe49a705..043cce4630 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -125,9 +125,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { const float padding = 44; // enough margin to avoid the hit error display. - leaderboard.Position = new Vector2( - padding, - padding + HUDOverlay.TopScoringElementsHeight); + leaderboard.Position = new Vector2(padding, padding + HUDOverlay.TopScoringElementsHeight); } private void onMatchStarted() => Scheduler.Add(() => diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index c3bfe19b29..45f871ed13 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -53,22 +53,22 @@ namespace osu.Game.Screens.Play.HUD { scoringMode = config.GetBindable(OsuSetting.ScoreDisplayMode); - foreach (var userId in playingUsers) + userLookupCache.GetUsersAsync(playingUsers.ToArray()).ContinueWith(users => Schedule(() => { - // probably won't be required in the final implementation. - var resolvedUser = userLookupCache.GetUserAsync(userId).Result; + foreach (var user in users.Result) + { + var trackedUser = CreateUserData(user.Id, scoreProcessor); + trackedUser.ScoringMode.BindTo(scoringMode); - var trackedUser = CreateUserData(userId, scoreProcessor); - trackedUser.ScoringMode.BindTo(scoringMode); + var leaderboardScore = AddPlayer(user, user.Id == api.LocalUser.Value.Id); + leaderboardScore.Accuracy.BindTo(trackedUser.Accuracy); + leaderboardScore.TotalScore.BindTo(trackedUser.Score); + leaderboardScore.Combo.BindTo(trackedUser.CurrentCombo); + leaderboardScore.HasQuit.BindTo(trackedUser.UserQuit); - var leaderboardScore = AddPlayer(resolvedUser, resolvedUser?.Id == api.LocalUser.Value.Id); - leaderboardScore.Accuracy.BindTo(trackedUser.Accuracy); - leaderboardScore.TotalScore.BindTo(trackedUser.Score); - leaderboardScore.Combo.BindTo(trackedUser.CurrentCombo); - leaderboardScore.HasQuit.BindTo(trackedUser.UserQuit); - - UserScores[userId] = trackedUser; - } + UserScores[user.Id] = trackedUser; + } + })); } protected override void LoadComplete() From d247b8042e2ccc4f1e54e308887737c41259ee6e Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 5 Jul 2021 20:05:08 +0900 Subject: [PATCH 0162/2442] Fix default skin catcher not flipping catcher plate When legacy beatmap skin is present but catcher is not provided, it was using the legacy setting (always false). --- .../Skinning/Legacy/CatchLegacySkinTransformer.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index b6f6e91c29..5e744ec001 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -108,8 +108,11 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy switch (config) { case CatchSkinConfiguration.FlipCatcherPlate: - // Always return `false` (don't flip catcher plate contents) for a legacy skin. - return (IBindable)new Bindable(); + // Don't flip catcher plate contents if the catcher is provided by this legacy skin. + if (GetDrawableComponent(new CatchSkinComponent(CatchSkinComponents.Catcher)) != null) + return (IBindable)new Bindable(); + + break; } break; From 459f9a04654da0e7142601efa74503ac0e9a99ac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 5 Jul 2021 21:30:24 +0900 Subject: [PATCH 0163/2442] Handle nulls and fix missing documentation --- osu.Game/Database/UserLookupCache.cs | 2 +- osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 3 +++ osu.Game/Screens/Spectate/SpectatorScreen.cs | 5 +++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/UserLookupCache.cs b/osu.Game/Database/UserLookupCache.cs index e5fd9c82ac..13c37ddfe9 100644 --- a/osu.Game/Database/UserLookupCache.cs +++ b/osu.Game/Database/UserLookupCache.cs @@ -32,7 +32,7 @@ namespace osu.Game.Database /// /// The users to lookup. /// An optional cancellation token. - /// . + /// The populated users. May include null results for failed retrievals. public Task GetUsersAsync(int[] userIds, CancellationToken token = default) { var userLookupTasks = new List>(); diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 45f871ed13..a32aca51c5 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -57,6 +57,9 @@ namespace osu.Game.Screens.Play.HUD { foreach (var user in users.Result) { + if (user == null) + continue; + var trackedUser = CreateUserData(user.Id, scoreProcessor); trackedUser.ScoringMode.BindTo(scoringMode); diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs index cd386a3d48..b6eafe496f 100644 --- a/osu.Game/Screens/Spectate/SpectatorScreen.cs +++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs @@ -63,7 +63,12 @@ namespace osu.Game.Screens.Spectate userLookupCache.GetUsersAsync(userIds.ToArray()).ContinueWith(users => Schedule(() => { foreach (var u in users.Result) + { + if (u == null) + continue; + userMap[u.Id] = u; + } playingUserStates.BindTo(spectatorClient.PlayingUserStates); playingUserStates.BindCollectionChanged(onPlayingUserStatesChanged, true); From f1014af2848b9875a9f9db1e60e13e7a110a410d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 5 Jul 2021 21:35:36 +0900 Subject: [PATCH 0164/2442] Move `LoadComplete` content to run after user retrieval has completed Feels safer, I think. --- osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index a32aca51c5..0c13e87f9f 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -71,13 +71,13 @@ namespace osu.Game.Screens.Play.HUD UserScores[user.Id] = trackedUser; } + + prepareDataStreams(); })); } - protected override void LoadComplete() + private void prepareDataStreams() { - base.LoadComplete(); - // BindableList handles binding in a really bad way (Clear then AddRange) so we need to do this manually.. foreach (int userId in playingUsers) { From 3fe875efb2a28edfcd88036e83e81cfa67624c48 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Mon, 5 Jul 2021 15:47:35 +0200 Subject: [PATCH 0165/2442] Add glow to focused meter --- osu.Game/Overlays/Volume/VolumeMeter.cs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs index 7132d8e3ea..9210001a3a 100644 --- a/osu.Game/Overlays/Volume/VolumeMeter.cs +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -38,6 +38,8 @@ namespace osu.Game.Overlays.Volume private OsuSpriteText text; private BufferedContainer maxGlow; + private Container focusGlowContainer; + private Sample sample; private double sampleLastPlaybackTime; @@ -75,6 +77,25 @@ namespace osu.Game.Overlays.Volume Size = new Vector2(circleSize), Children = new Drawable[] { + focusGlowContainer = new CircularContainer + { + Masking = true, + RelativeSizeAxes = Axes.Both, + Alpha = 0, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true, + }, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = meterColour.Opacity(0.5f), + Radius = 5, + Hollow = true, + } + }, new BufferedContainer { Alpha = 0.9f, @@ -312,11 +333,13 @@ namespace osu.Game.Overlays.Volume public void Focus() { this.ScaleTo(1.04f, transition_length, Easing.OutExpo); + focusGlowContainer.FadeIn(transition_length, Easing.OutExpo); } public void Unfocus() { this.ScaleTo(1f, transition_length, Easing.OutExpo); + focusGlowContainer.FadeOut(transition_length, Easing.OutExpo); } protected override bool OnHover(HoverEvent e) From fdfdfeecab2a4775f7b6b0733421112d14c4446b Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Mon, 5 Jul 2021 23:35:17 +0800 Subject: [PATCH 0166/2442] Suppress IDE0057 --- .editorconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.editorconfig b/.editorconfig index f4d7e08d08..bbe091a852 100644 --- a/.editorconfig +++ b/.editorconfig @@ -168,8 +168,8 @@ dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent #Style - C# 8 features csharp_prefer_static_local_function = true:warning csharp_prefer_simple_using_statement = true:silent -csharp_style_prefer_index_operator = true:warning -csharp_style_prefer_range_operator = true:warning +csharp_style_prefer_index_operator = false:silent +csharp_style_prefer_range_operator = false:silent csharp_style_prefer_switch_expression = false:none #Supressing roslyn built-in analyzers From a4545051f2463fdeeff7567ba0687fe7c96388e4 Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Mon, 5 Jul 2021 23:36:15 +0800 Subject: [PATCH 0167/2442] Suppress IDE0042 --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index bbe091a852..19bd89c52f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -157,7 +157,7 @@ csharp_style_unused_value_assignment_preference = discard_variable:warning #Style - variable declaration csharp_style_inlined_variable_declaration = true:warning -csharp_style_deconstructed_variable_declaration = true:warning +csharp_style_deconstructed_variable_declaration = false:silent #Style - other C# 7.x features dotnet_style_prefer_inferred_tuple_names = true:warning From e1c646b9b297397bbb02fed8acb6b8e70cfe4158 Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Mon, 5 Jul 2021 23:52:39 +0800 Subject: [PATCH 0168/2442] Remove redundant arguments --- .../TestSceneHyperDashColouring.cs | 2 +- .../TestSceneGameplayCursor.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | 6 ++--- osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs | 4 ++-- .../Skinning/Default/DefaultSpinnerDisc.cs | 14 ++++++------ .../Skinning/Default/SliderBall.cs | 2 +- .../Skinning/Legacy/LegacyNewStyleSpinner.cs | 8 +++---- .../Skinning/Legacy/LegacyOldStyleSpinner.cs | 4 ++-- .../Skinning/Legacy/LegacySpinner.cs | 8 +++---- .../Objects/Drawables/DrawableSwell.cs | 2 +- .../Beatmaps/SliderEventGenerationTest.cs | 10 ++++----- .../TestSceneCompletionCancellation.cs | 5 ----- .../Gameplay/TestSceneSkinnableDrawable.cs | 2 +- .../Gameplay/TestSceneSpectatorPlayback.cs | 2 +- .../TestSceneMultiSpectatorScreen.cs | 4 ++-- .../TestSceneProfileSubsectionHeader.cs | 2 +- .../Screens/Gameplay/GameplayScreen.cs | 2 +- .../Containers/BeatSyncedContainer.cs | 2 +- .../Graphics/UserInterface/StarCounter.cs | 2 +- .../Requests/GetSpotlightRankingsRequest.cs | 2 +- osu.Game/Online/Leaderboards/Leaderboard.cs | 2 +- .../Online/Leaderboards/LeaderboardScore.cs | 6 ++--- osu.Game/Overlays/MedalOverlay.cs | 4 ++-- osu.Game/Overlays/Mods/ModButton.cs | 2 +- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 +- osu.Game/Overlays/News/NewsHeader.cs | 2 +- .../Header/Components/PreviousUsernames.cs | 2 +- .../Overlays/Rankings/Tables/RankingsTable.cs | 2 +- .../Rulesets/Judgements/DrawableJudgement.cs | 2 +- osu.Game/Rulesets/Mods/ModBarrelRoll.cs | 2 +- .../Objects/Drawables/DrawableHitObject.cs | 6 ++--- osu.Game/Screens/Menu/ButtonSystem.cs | 2 +- osu.Game/Screens/Menu/Disclaimer.cs | 2 +- osu.Game/Screens/Menu/IntroSequence.cs | 14 ++++++------ osu.Game/Screens/Menu/IntroTriangles.cs | 22 +++++++++---------- osu.Game/Screens/Menu/IntroWelcome.cs | 2 +- .../Match/MultiplayerMatchSettingsOverlay.cs | 2 +- .../PlaylistsMatchSettingsOverlay.cs | 2 +- osu.Game/Screens/Play/BreakOverlay.cs | 4 ++-- .../Screens/Play/HUD/LegacyComboCounter.cs | 10 ++++----- .../Expanded/ExpandedPanelMiddleContent.cs | 4 ++-- .../Expanded/Statistics/ComboStatistic.cs | 2 +- osu.Game/Screens/Ranking/ScorePanel.cs | 2 +- osu.Game/Screens/ScreenWhiteBox.cs | 2 +- osu.Game/Skinning/LegacyJudgementPieceNew.cs | 2 +- osu.Game/Tests/Visual/EditorTestScene.cs | 2 +- osu.Game/Tests/Visual/OsuTestScene.cs | 2 +- 50 files changed, 97 insertions(+), 102 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index 7fa981d492..e7b0259ea2 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Catch.Tests AddStep("finish hyper-dashing", () => { - catcherArea.MovableCatcher.SetHyperDashState(1); + catcherArea.MovableCatcher.SetHyperDashState(); catcherArea.MovableCatcher.FinishTransforms(); }); diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs index 78bb88322a..2326a0c391 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs @@ -121,7 +121,7 @@ namespace osu.Game.Rulesets.Osu.Tests { case OsuSkinConfiguration osuLookup: if (osuLookup == OsuSkinConfiguration.CursorCentre) - return SkinUtils.As(new BindableBool(false)); + return SkinUtils.As(new BindableBool()); break; } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs index ebf6f9dda7..636cd63c69 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -158,17 +158,17 @@ namespace osu.Game.Rulesets.Osu.Mods var firstObj = beatmap.HitObjects[0]; var startDelay = firstObj.StartTime - firstObj.TimePreempt; - using (BeginAbsoluteSequence(startDelay + break_close_late, true)) + using (BeginAbsoluteSequence(startDelay + break_close_late)) leaveBreak(); foreach (var breakInfo in beatmap.Breaks) { if (breakInfo.HasEffect) { - using (BeginAbsoluteSequence(breakInfo.StartTime - break_open_early, true)) + using (BeginAbsoluteSequence(breakInfo.StartTime - break_open_early)) { enterBreak(); - using (BeginDelayedSequence(breakInfo.Duration + break_open_early + break_close_late, true)) + using (BeginDelayedSequence(breakInfo.Duration + break_open_early + break_close_late)) leaveBreak(); } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs index 56c246953e..95e7d13ee7 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Osu.Mods switch (drawable) { case DrawableHitCircle circle: - using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) + using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) { circle.ApproachCircle.Hide(); diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs index a05e4dea03..07ce009cf8 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Mods private void applyCirclePieceState(DrawableOsuHitObject hitObject, IDrawable hitCircle = null) { var h = hitObject.HitObject; - using (hitObject.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) + using (hitObject.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) (hitCircle ?? hitObject).Hide(); } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs index b5905d7015..8122ab563e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Mods double appearTime = hitObject.StartTime - hitObject.TimePreempt - 1; double moveDuration = hitObject.TimePreempt + 1; - using (drawable.BeginAbsoluteSequence(appearTime, true)) + using (drawable.BeginAbsoluteSequence(appearTime)) { drawable .MoveToOffset(appearOffset) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs index a01cec4bb3..ff6ba6e121 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Osu.Mods for (int i = 0; i < amountWiggles; i++) { - using (drawable.BeginAbsoluteSequence(osuObject.StartTime - osuObject.TimePreempt + i * wiggle_duration, true)) + using (drawable.BeginAbsoluteSequence(osuObject.StartTime - osuObject.TimePreempt + i * wiggle_duration)) wiggle(); } @@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.Mods for (int i = 0; i < amountWiggles; i++) { - using (drawable.BeginAbsoluteSequence(osuObject.StartTime + i * wiggle_duration, true)) + using (drawable.BeginAbsoluteSequence(osuObject.StartTime + i * wiggle_duration)) wiggle(); } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSpinnerDisc.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSpinnerDisc.cs index 542f3eff0d..4ea0831627 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSpinnerDisc.cs @@ -130,18 +130,18 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default Spinner spinner = drawableSpinner.HitObject; - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt)) { this.ScaleTo(initial_scale); this.RotateTo(0); - using (BeginDelayedSequence(spinner.TimePreempt / 2, true)) + using (BeginDelayedSequence(spinner.TimePreempt / 2)) { // constant ambient rotation to give the spinner "spinning" character. this.RotateTo((float)(25 * spinner.Duration / 2000), spinner.TimePreempt + spinner.Duration); } - using (BeginDelayedSequence(spinner.TimePreempt + spinner.Duration + drawableHitObject.Result.TimeOffset, true)) + using (BeginDelayedSequence(spinner.TimePreempt + spinner.Duration + drawableHitObject.Result.TimeOffset)) { switch (state) { @@ -157,17 +157,17 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default } } - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt)) { centre.ScaleTo(0); mainContainer.ScaleTo(0); - using (BeginDelayedSequence(spinner.TimePreempt / 2, true)) + using (BeginDelayedSequence(spinner.TimePreempt / 2)) { centre.ScaleTo(0.3f, spinner.TimePreempt / 4, Easing.OutQuint); mainContainer.ScaleTo(0.2f, spinner.TimePreempt / 4, Easing.OutQuint); - using (BeginDelayedSequence(spinner.TimePreempt / 2, true)) + using (BeginDelayedSequence(spinner.TimePreempt / 2)) { centre.ScaleTo(0.5f, spinner.TimePreempt / 2, Easing.OutQuint); mainContainer.ScaleTo(1, spinner.TimePreempt / 2, Easing.OutQuint); @@ -176,7 +176,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default } // transforms we have from completing the spinner will be rolled back, so reapply immediately. - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt)) updateComplete(state == ArmedState.Hit, 0); } diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs index 8feeca56e8..1f988df947 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs @@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default public override void ApplyTransformsAt(double time, bool propagateChildren = false) { // For the same reasons as above w.r.t rewinding, we shouldn't propagate to children here either. - base.ApplyTransformsAt(time, false); + base.ApplyTransformsAt(time); } private bool tracking; diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyNewStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyNewStyleSpinner.cs index ae8d6a61f8..1e170036e4 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyNewStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyNewStyleSpinner.cs @@ -100,17 +100,17 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy case DrawableSpinner d: Spinner spinner = d.HitObject; - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt)) this.FadeOut(); - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2, true)) + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2)) this.FadeInFromZero(spinner.TimeFadeIn / 2); - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt)) { fixedMiddle.FadeColour(Color4.White); - using (BeginDelayedSequence(spinner.TimePreempt, true)) + using (BeginDelayedSequence(spinner.TimePreempt)) fixedMiddle.FadeColour(Color4.Red, spinner.Duration); } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyOldStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyOldStyleSpinner.cs index cbe721d21d..e3e8f3ce88 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyOldStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyOldStyleSpinner.cs @@ -89,10 +89,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Spinner spinner = d.HitObject; - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt)) this.FadeOut(); - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2, true)) + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2)) this.FadeInFromZero(spinner.TimeFadeIn / 2); } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs index b2311dcb91..93aba608e6 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs @@ -140,7 +140,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { double startTime = Math.Min(Time.Current, DrawableSpinner.HitStateUpdateTime - 400); - using (BeginAbsoluteSequence(startTime, true)) + using (BeginAbsoluteSequence(startTime)) { clear.FadeInFromZero(400, Easing.Out); @@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy } const double fade_out_duration = 50; - using (BeginAbsoluteSequence(DrawableSpinner.HitStateUpdateTime - fade_out_duration, true)) + using (BeginAbsoluteSequence(DrawableSpinner.HitStateUpdateTime - fade_out_duration)) clear.FadeOut(fade_out_duration); } else @@ -182,14 +182,14 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy double spinFadeOutLength = Math.Min(400, d.HitObject.Duration); - using (BeginAbsoluteSequence(drawableHitObject.HitStateUpdateTime - spinFadeOutLength, true)) + using (BeginAbsoluteSequence(drawableHitObject.HitStateUpdateTime - spinFadeOutLength)) spin.FadeOutFromOne(spinFadeOutLength); break; case DrawableSpinnerTick d: if (state == ArmedState.Hit) { - using (BeginAbsoluteSequence(d.HitStateUpdateTime, true)) + using (BeginAbsoluteSequence(d.HitStateUpdateTime)) spin.FadeOut(300); } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index 60f9521996..888f47d341 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -227,7 +227,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { base.UpdateStartTimeStateTransforms(); - using (BeginDelayedSequence(-ring_appear_offset, true)) + using (BeginDelayedSequence(-ring_appear_offset)) targetRing.ScaleTo(target_ring_scale, 400, Easing.OutQuint); } diff --git a/osu.Game.Tests/Beatmaps/SliderEventGenerationTest.cs b/osu.Game.Tests/Beatmaps/SliderEventGenerationTest.cs index 6c8133660f..9fba0f1668 100644 --- a/osu.Game.Tests/Beatmaps/SliderEventGenerationTest.cs +++ b/osu.Game.Tests/Beatmaps/SliderEventGenerationTest.cs @@ -16,7 +16,7 @@ namespace osu.Game.Tests.Beatmaps [Test] public void TestSingleSpan() { - var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, null, default).ToArray(); + var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, null).ToArray(); Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head)); Assert.That(events[0].Time, Is.EqualTo(start_time)); @@ -31,7 +31,7 @@ namespace osu.Game.Tests.Beatmaps [Test] public void TestRepeat() { - var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 2, null, default).ToArray(); + var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 2, null).ToArray(); Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head)); Assert.That(events[0].Time, Is.EqualTo(start_time)); @@ -52,7 +52,7 @@ namespace osu.Game.Tests.Beatmaps [Test] public void TestNonEvenTicks() { - var events = SliderEventGenerator.Generate(start_time, span_duration, 1, 300, span_duration, 2, null, default).ToArray(); + var events = SliderEventGenerator.Generate(start_time, span_duration, 1, 300, span_duration, 2, null).ToArray(); Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head)); Assert.That(events[0].Time, Is.EqualTo(start_time)); @@ -85,7 +85,7 @@ namespace osu.Game.Tests.Beatmaps [Test] public void TestLegacyLastTickOffset() { - var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, 100, default).ToArray(); + var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, 100).ToArray(); Assert.That(events[2].Type, Is.EqualTo(SliderEventType.LegacyLastTick)); Assert.That(events[2].Time, Is.EqualTo(900)); @@ -97,7 +97,7 @@ namespace osu.Game.Tests.Beatmaps const double velocity = 5; const double min_distance = velocity * 10; - var events = SliderEventGenerator.Generate(start_time, span_duration, velocity, velocity, span_duration, 2, 0, default).ToArray(); + var events = SliderEventGenerator.Generate(start_time, span_duration, velocity, velocity, span_duration, 2, 0).ToArray(); Assert.Multiple(() => { diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs index 4ee48fd853..11bd701e19 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneCompletionCancellation.cs @@ -114,11 +114,6 @@ namespace osu.Game.Tests.Visual.Gameplay { public bool ResultsCreated { get; private set; } - public FakeRankingPushPlayer() - : base(true, true) - { - } - protected override ResultsScreen CreateResults(ScoreInfo score) { var results = base.CreateResults(score); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs index f29fbbf52b..02b1959dab 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs @@ -75,7 +75,7 @@ namespace osu.Game.Tests.Visual.Gameplay Children = new[] { new ExposedSkinnableDrawable("default", _ => new DefaultBox()), - new ExposedSkinnableDrawable("available", _ => new DefaultBox(), ConfineMode.ScaleToFit), + new ExposedSkinnableDrawable("available", _ => new DefaultBox()), new ExposedSkinnableDrawable("available", _ => new DefaultBox(), ConfineMode.NoScaling) } }, diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs index 469f594fdc..bb577886cc 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs @@ -179,7 +179,7 @@ namespace osu.Game.Tests.Visual.Gameplay foreach (var legacyFrame in frames.Frames) { var frame = new TestReplayFrame(); - frame.FromLegacy(legacyFrame, null, null); + frame.FromLegacy(legacyFrame, null); replay.Frames.Add(frame); } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index b8db4067fb..072e32370d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -137,7 +137,7 @@ namespace osu.Game.Tests.Visual.Multiplayer // Send initial frames for both players. A few more for player 1. sendFrames(PLAYER_1_ID, 20); - sendFrames(PLAYER_2_ID, 10); + sendFrames(PLAYER_2_ID); checkPausedInstant(PLAYER_1_ID, false); checkPausedInstant(PLAYER_2_ID, false); @@ -194,7 +194,7 @@ namespace osu.Game.Tests.Visual.Multiplayer assertMuted(PLAYER_1_ID, true); assertMuted(PLAYER_2_ID, true); - sendFrames(PLAYER_1_ID, 10); + sendFrames(PLAYER_1_ID); sendFrames(PLAYER_2_ID, 20); checkPaused(PLAYER_1_ID, false); assertOneNotMuted(); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneProfileSubsectionHeader.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneProfileSubsectionHeader.cs index cd226662d7..4ce684d5af 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneProfileSubsectionHeader.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneProfileSubsectionHeader.cs @@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestInitialVisibility() { - AddStep("Create header with 0 value", () => createHeader("Header with visible when zero counter", CounterVisibilityState.VisibleWhenZero, 0)); + AddStep("Create header with 0 value", () => createHeader("Header with visible when zero counter", CounterVisibilityState.VisibleWhenZero)); AddAssert("Value is 0", () => header.Current.Value == 0); AddAssert("Counter is visible", () => header.ChildrenOfType().First().Alpha == 1); diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index e4ec45c00e..6e4c6784c8 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -172,7 +172,7 @@ namespace osu.Game.Tournament.Screens.Gameplay { chat?.Contract(); - using (BeginDelayedSequence(300, true)) + using (BeginDelayedSequence(300)) { scoreDisplay.FadeIn(100); SongBar.Expanded = true; diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 1c9cdc174a..e2a0c09a6b 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -97,7 +97,7 @@ namespace osu.Game.Graphics.Containers if (timingPoint == lastTimingPoint && beatIndex == lastBeat) return; - using (BeginDelayedSequence(-TimeSinceLastBeat, true)) + using (BeginDelayedSequence(-TimeSinceLastBeat)) OnNewBeat(beatIndex, timingPoint, effectPoint, track?.CurrentAmplitudes ?? ChannelAmplitudes.Empty); lastBeat = beatIndex; diff --git a/osu.Game/Graphics/UserInterface/StarCounter.cs b/osu.Game/Graphics/UserInterface/StarCounter.cs index 894a21fcf3..32b788b5dc 100644 --- a/osu.Game/Graphics/UserInterface/StarCounter.cs +++ b/osu.Game/Graphics/UserInterface/StarCounter.cs @@ -113,7 +113,7 @@ namespace osu.Game.Graphics.UserInterface double delay = (current <= newValue ? Math.Max(i - current, 0) : Math.Max(current - 1 - i, 0)) * AnimationDelay; - using (star.BeginDelayedSequence(delay, true)) + using (star.BeginDelayedSequence(delay)) star.DisplayAt(getStarScale(i, newValue)); } } diff --git a/osu.Game/Online/API/Requests/GetSpotlightRankingsRequest.cs b/osu.Game/Online/API/Requests/GetSpotlightRankingsRequest.cs index 25e6b3f1af..20856c2768 100644 --- a/osu.Game/Online/API/Requests/GetSpotlightRankingsRequest.cs +++ b/osu.Game/Online/API/Requests/GetSpotlightRankingsRequest.cs @@ -13,7 +13,7 @@ namespace osu.Game.Online.API.Requests private readonly RankingsSortCriteria sort; public GetSpotlightRankingsRequest(RulesetInfo ruleset, int spotlight, RankingsSortCriteria sort) - : base(ruleset, 1) + : base(ruleset) { this.spotlight = spotlight; this.sort = sort; diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index 70e38e421d..4f8b27602b 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -82,7 +82,7 @@ namespace osu.Game.Online.Leaderboards foreach (var s in scrollFlow.Children) { - using (s.BeginDelayedSequence(i++ * 50, true)) + using (s.BeginDelayedSequence(i++ * 50)) s.Show(); } diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index e35d3d6461..7108a23e44 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -248,7 +248,7 @@ namespace osu.Game.Online.Leaderboards this.FadeIn(200); content.MoveToY(0, 800, Easing.OutQuint); - using (BeginDelayedSequence(100, true)) + using (BeginDelayedSequence(100)) { avatar.FadeIn(300, Easing.OutQuint); nameLabel.FadeIn(350, Easing.OutQuint); @@ -256,12 +256,12 @@ namespace osu.Game.Online.Leaderboards avatar.MoveToX(0, 300, Easing.OutQuint); nameLabel.MoveToX(0, 350, Easing.OutQuint); - using (BeginDelayedSequence(250, true)) + using (BeginDelayedSequence(250)) { scoreLabel.FadeIn(200); scoreRank.FadeIn(200); - using (BeginDelayedSequence(50, true)) + using (BeginDelayedSequence(50)) { var drawables = new Drawable[] { flagBadgeContainer, modsContainer }.Concat(statisticsLabels).ToArray(); for (int i = 0; i < drawables.Length; i++) diff --git a/osu.Game/Overlays/MedalOverlay.cs b/osu.Game/Overlays/MedalOverlay.cs index 0feae16b68..e15625a4b3 100644 --- a/osu.Game/Overlays/MedalOverlay.cs +++ b/osu.Game/Overlays/MedalOverlay.cs @@ -213,7 +213,7 @@ namespace osu.Game.Overlays innerSpin.Spin(20000, RotationDirection.Clockwise); outerSpin.Spin(40000, RotationDirection.Clockwise); - using (BeginDelayedSequence(200, true)) + using (BeginDelayedSequence(200)) { disc.FadeIn(initial_duration) .ScaleTo(1f, initial_duration * 2, Easing.OutElastic); @@ -221,7 +221,7 @@ namespace osu.Game.Overlays particleContainer.FadeIn(initial_duration); outerSpin.FadeTo(0.1f, initial_duration * 2); - using (BeginDelayedSequence(initial_duration + 200, true)) + using (BeginDelayedSequence(initial_duration + 200)) { backgroundStrip.FadeIn(step_duration); leftStrip.ResizeWidthTo(1f, step_duration, Easing.OutQuint); diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index d0bd24496a..572ff0d1aa 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -91,7 +91,7 @@ namespace osu.Game.Overlays.Mods backgroundIcon.Mod = newSelection; - using (BeginDelayedSequence(mod_switch_duration, true)) + using (BeginDelayedSequence(mod_switch_duration)) { foregroundIcon .RotateTo(-rotate_angle * direction) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index e4aab978fc..6c47b92d29 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -128,7 +128,7 @@ namespace osu.Game.Overlays.Mods RowDimensions = new[] { new Dimension(GridSizeMode.Absolute, 90), - new Dimension(GridSizeMode.Distributed), + new Dimension(), new Dimension(GridSizeMode.AutoSize), }, Content = new[] diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs index 94bfd62c32..56c54425bd 100644 --- a/osu.Game/Overlays/News/NewsHeader.cs +++ b/osu.Game/Overlays/News/NewsHeader.cs @@ -13,7 +13,7 @@ namespace osu.Game.Overlays.News public Action ShowFrontPage; - private readonly Bindable article = new Bindable(null); + private readonly Bindable article = new Bindable(); public NewsHeader() { diff --git a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs index e4c0fe3a5a..3cdf110090 100644 --- a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs +++ b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs @@ -57,7 +57,7 @@ namespace osu.Game.Overlays.Profile.Header.Components ColumnDimensions = new[] { new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.Distributed) + new Dimension() }, Content = new[] { diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 943897581e..e8a76c64ec 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -61,7 +61,7 @@ namespace osu.Game.Overlays.Rankings.Tables private static TableColumn[] mainHeaders => new[] { new TableColumn(string.Empty, Anchor.Centre, new Dimension(GridSizeMode.Absolute, 40)), // place - new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed)), // flag and username (country name) + new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension()), // flag and username (country name) }; protected abstract TableColumn[] CreateAdditionalHeaders(); diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index 8a57b4af91..0f22d35bb5 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -117,7 +117,7 @@ namespace osu.Game.Rulesets.Judgements LifetimeStart = Result.TimeAbsolute; - using (BeginAbsoluteSequence(Result.TimeAbsolute, true)) + using (BeginAbsoluteSequence(Result.TimeAbsolute)) { // not sure if this should remain going forward. JudgementBody.ResetAnimation(); diff --git a/osu.Game/Rulesets/Mods/ModBarrelRoll.cs b/osu.Game/Rulesets/Mods/ModBarrelRoll.cs index 0d344b5269..872daadd46 100644 --- a/osu.Game/Rulesets/Mods/ModBarrelRoll.cs +++ b/osu.Game/Rulesets/Mods/ModBarrelRoll.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mods }; [SettingSource("Direction", "The direction of rotation")] - public Bindable Direction { get; } = new Bindable(RotationDirection.Clockwise); + public Bindable Direction { get; } = new Bindable(); public override string Name => "Barrel Roll"; public override string Acronym => "BR"; diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index a0717ec38e..c5db806918 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -404,13 +404,13 @@ namespace osu.Game.Rulesets.Objects.Drawables clearExistingStateTransforms(); - using (BeginAbsoluteSequence(transformTime, true)) + using (BeginAbsoluteSequence(transformTime)) UpdateInitialTransforms(); - using (BeginAbsoluteSequence(StateUpdateTime, true)) + using (BeginAbsoluteSequence(StateUpdateTime)) UpdateStartTimeStateTransforms(); - using (BeginAbsoluteSequence(HitStateUpdateTime, true)) + using (BeginAbsoluteSequence(HitStateUpdateTime)) UpdateHitStateTransforms(newState); state.Value = newState; diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 38290a6530..bdb0157746 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -297,7 +297,7 @@ namespace osu.Game.Screens.Menu Logger.Log($"{nameof(ButtonSystem)}'s state changed from {lastState} to {state}"); - using (buttonArea.BeginDelayedSequence(lastState == ButtonSystemState.Initial ? 150 : 0, true)) + using (buttonArea.BeginDelayedSequence(lastState == ButtonSystemState.Initial ? 150 : 0)) { buttonArea.ButtonSystemState = state; diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs index a71b53157a..7f34e1e395 100644 --- a/osu.Game/Screens/Menu/Disclaimer.cs +++ b/osu.Game/Screens/Menu/Disclaimer.cs @@ -181,7 +181,7 @@ namespace osu.Game.Screens.Menu icon.Delay(500).FadeIn(500).ScaleTo(1, 500, Easing.OutQuint); - using (BeginDelayedSequence(3000, true)) + using (BeginDelayedSequence(3000)) { icon.FadeColour(iconColour, 200, Easing.OutQuint); icon.MoveToY(icon_y * 1.3f, 500, Easing.OutCirc) diff --git a/osu.Game/Screens/Menu/IntroSequence.cs b/osu.Game/Screens/Menu/IntroSequence.cs index d92d38da45..3a5cd6857a 100644 --- a/osu.Game/Screens/Menu/IntroSequence.cs +++ b/osu.Game/Screens/Menu/IntroSequence.cs @@ -189,7 +189,7 @@ namespace osu.Game.Screens.Menu double remainingTime() => length - TransformDelay; - using (BeginDelayedSequence(250, true)) + using (BeginDelayedSequence(250)) { welcomeText.FadeIn(700); welcomeText.TransformSpacingTo(new Vector2(20, 0), remainingTime(), Easing.Out); @@ -212,17 +212,17 @@ namespace osu.Game.Screens.Menu lineBottomLeft.MoveTo(new Vector2(-line_end_offset, line_end_offset), line_duration, Easing.OutQuint); lineBottomRight.MoveTo(new Vector2(line_end_offset, line_end_offset), line_duration, Easing.OutQuint); - using (BeginDelayedSequence(length * 0.56, true)) + using (BeginDelayedSequence(length * 0.56)) { bigRing.ResizeTo(logo_size, 500, Easing.InOutQuint); bigRing.Foreground.Delay(250).ResizeTo(1, 850, Easing.OutQuint); - using (BeginDelayedSequence(250, true)) + using (BeginDelayedSequence(250)) { backgroundFill.ResizeHeightTo(1, remainingTime(), Easing.InOutQuart); backgroundFill.RotateTo(-90, remainingTime(), Easing.InOutQuart); - using (BeginDelayedSequence(50, true)) + using (BeginDelayedSequence(50)) { foregroundFill.ResizeWidthTo(1, remainingTime(), Easing.InOutQuart); foregroundFill.RotateTo(-90, remainingTime(), Easing.InOutQuart); @@ -239,19 +239,19 @@ namespace osu.Game.Screens.Menu purpleCircle.Delay(rotation_delay).RotateTo(-180, remainingTime() - rotation_delay, Easing.InOutQuart); purpleCircle.ResizeTo(circle_size, remainingTime(), Easing.InOutQuart); - using (BeginDelayedSequence(appear_delay, true)) + using (BeginDelayedSequence(appear_delay)) { yellowCircle.MoveToY(-circle_size / 2, remainingTime(), Easing.InOutQuart); yellowCircle.Delay(rotation_delay).RotateTo(-180, remainingTime() - rotation_delay, Easing.InOutQuart); yellowCircle.ResizeTo(circle_size, remainingTime(), Easing.InOutQuart); - using (BeginDelayedSequence(appear_delay, true)) + using (BeginDelayedSequence(appear_delay)) { blueCircle.MoveToX(-circle_size / 2, remainingTime(), Easing.InOutQuart); blueCircle.Delay(rotation_delay).RotateTo(-180, remainingTime() - rotation_delay, Easing.InOutQuart); blueCircle.ResizeTo(circle_size, remainingTime(), Easing.InOutQuart); - using (BeginDelayedSequence(appear_delay, true)) + using (BeginDelayedSequence(appear_delay)) { pinkCircle.MoveToX(circle_size / 2, remainingTime(), Easing.InOutQuart); pinkCircle.Delay(rotation_delay).RotateTo(-180, remainingTime() - rotation_delay, Easing.InOutQuart); diff --git a/osu.Game/Screens/Menu/IntroTriangles.cs b/osu.Game/Screens/Menu/IntroTriangles.cs index abe6c62461..0ea83fe5e7 100644 --- a/osu.Game/Screens/Menu/IntroTriangles.cs +++ b/osu.Game/Screens/Menu/IntroTriangles.cs @@ -172,27 +172,27 @@ namespace osu.Game.Screens.Menu lazerLogo.Hide(); background.ApplyToBackground(b => b.Hide()); - using (BeginAbsoluteSequence(0, true)) + using (BeginAbsoluteSequence(0)) { - using (BeginDelayedSequence(text_1, true)) + using (BeginDelayedSequence(text_1)) welcomeText.FadeIn().OnComplete(t => t.Text = "wel"); - using (BeginDelayedSequence(text_2, true)) + using (BeginDelayedSequence(text_2)) welcomeText.FadeIn().OnComplete(t => t.Text = "welcome"); - using (BeginDelayedSequence(text_3, true)) + using (BeginDelayedSequence(text_3)) welcomeText.FadeIn().OnComplete(t => t.Text = "welcome to"); - using (BeginDelayedSequence(text_4, true)) + using (BeginDelayedSequence(text_4)) { welcomeText.FadeIn().OnComplete(t => t.Text = "welcome to osu!"); welcomeText.TransformTo(nameof(welcomeText.Spacing), new Vector2(50, 0), 5000); } - using (BeginDelayedSequence(text_glitch, true)) + using (BeginDelayedSequence(text_glitch)) triangles.FadeIn(); - using (BeginDelayedSequence(rulesets_1, true)) + using (BeginDelayedSequence(rulesets_1)) { rulesetsScale.ScaleTo(0.8f, 1000); rulesets.FadeIn().ScaleTo(1).TransformSpacingTo(new Vector2(200, 0)); @@ -200,18 +200,18 @@ namespace osu.Game.Screens.Menu triangles.FadeOut(); } - using (BeginDelayedSequence(rulesets_2, true)) + using (BeginDelayedSequence(rulesets_2)) { rulesets.ScaleTo(2).TransformSpacingTo(new Vector2(30, 0)); } - using (BeginDelayedSequence(rulesets_3, true)) + using (BeginDelayedSequence(rulesets_3)) { rulesets.ScaleTo(4).TransformSpacingTo(new Vector2(10, 0)); rulesetsScale.ScaleTo(1.3f, 1000); } - using (BeginDelayedSequence(logo_1, true)) + using (BeginDelayedSequence(logo_1)) { rulesets.FadeOut(); @@ -223,7 +223,7 @@ namespace osu.Game.Screens.Menu logoContainerSecondary.ScaleTo(scale_start).Then().ScaleTo(scale_start - scale_adjust * 0.25f, logo_scale_duration, Easing.InQuad); } - using (BeginDelayedSequence(logo_2, true)) + using (BeginDelayedSequence(logo_2)) { lazerLogo.FadeOut().OnComplete(_ => { diff --git a/osu.Game/Screens/Menu/IntroWelcome.cs b/osu.Game/Screens/Menu/IntroWelcome.cs index 521e863683..f74043b045 100644 --- a/osu.Game/Screens/Menu/IntroWelcome.cs +++ b/osu.Game/Screens/Menu/IntroWelcome.cs @@ -143,7 +143,7 @@ namespace osu.Game.Screens.Menu { base.LoadComplete(); - using (BeginDelayedSequence(0, true)) + using (BeginDelayedSequence(0)) { scaleContainer.ScaleTo(0.9f).ScaleTo(1, delay_step_two).OnComplete(_ => Expire()); scaleContainer.FadeInFromZero(1800); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index fe9979b161..2e180f31fd 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -93,7 +93,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match RelativeSizeAxes = Axes.Both, RowDimensions = new[] { - new Dimension(GridSizeMode.Distributed), + new Dimension(), new Dimension(GridSizeMode.AutoSize), }, Content = new[] diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs index 5062a296a8..5eb2b545cb 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs @@ -75,7 +75,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists RelativeSizeAxes = Axes.Both, RowDimensions = new[] { - new Dimension(GridSizeMode.Distributed), + new Dimension(), new Dimension(GridSizeMode.AutoSize), }, Content = new[] diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index 36f825b8f6..1665ee83ae 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -126,7 +126,7 @@ namespace osu.Game.Screens.Play if (!b.HasEffect) continue; - using (BeginAbsoluteSequence(b.StartTime, true)) + using (BeginAbsoluteSequence(b.StartTime)) { fadeContainer.FadeIn(BREAK_FADE_DURATION); breakArrows.Show(BREAK_FADE_DURATION); @@ -143,7 +143,7 @@ namespace osu.Game.Screens.Play remainingTimeCounter.CountTo(b.Duration).CountTo(0, b.Duration); - using (BeginDelayedSequence(b.Duration - BREAK_FADE_DURATION, true)) + using (BeginDelayedSequence(b.Duration - BREAK_FADE_DURATION)) { fadeContainer.FadeOut(BREAK_FADE_DURATION); breakArrows.Hide(BREAK_FADE_DURATION); diff --git a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs index acff949353..edddc06746 100644 --- a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs @@ -170,9 +170,9 @@ namespace osu.Game.Screens.Play.HUD popOutCount.FadeTo(0.75f); popOutCount.MoveTo(Vector2.Zero); - popOutCount.ScaleTo(1, pop_out_duration, pop_out_easing); - popOutCount.FadeOut(pop_out_duration, pop_out_easing); - popOutCount.MoveTo(displayedCountSpriteText.Position, pop_out_duration, pop_out_easing); + popOutCount.ScaleTo(1, pop_out_duration); + popOutCount.FadeOut(pop_out_duration); + popOutCount.MoveTo(displayedCountSpriteText.Position, pop_out_duration); } private void transformNoPopOut(int newValue) @@ -186,7 +186,7 @@ namespace osu.Game.Screens.Play.HUD { ((IHasText)displayedCountSpriteText).Text = formatCount(newValue); displayedCountSpriteText.ScaleTo(1.1f); - displayedCountSpriteText.ScaleTo(1, pop_out_duration, pop_out_easing); + displayedCountSpriteText.ScaleTo(1, pop_out_duration); } private void scheduledPopOutSmall(uint id) @@ -261,7 +261,7 @@ namespace osu.Game.Screens.Play.HUD } private void transformRoll(int currentValue, int newValue) => - this.TransformTo(nameof(DisplayedCount), newValue, getProportionalDuration(currentValue, newValue), Easing.None); + this.TransformTo(nameof(DisplayedCount), newValue, getProportionalDuration(currentValue, newValue)); private string formatCount(int count) => $@"{count}x"; diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index 0a10eee644..4d3f7a4184 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -256,7 +256,7 @@ namespace osu.Game.Screens.Ranking.Expanded // Score counter value setting must be scheduled so it isn't transferred instantaneously ScheduleAfterChildren(() => { - using (BeginDelayedSequence(AccuracyCircle.ACCURACY_TRANSFORM_DELAY, true)) + using (BeginDelayedSequence(AccuracyCircle.ACCURACY_TRANSFORM_DELAY)) { scoreCounter.FadeIn(); scoreCounter.Current = scoreManager.GetBindableTotalScore(score); @@ -265,7 +265,7 @@ namespace osu.Game.Screens.Ranking.Expanded foreach (var stat in statisticDisplays) { - using (BeginDelayedSequence(delay, true)) + using (BeginDelayedSequence(delay)) stat.Appear(); delay += 200; diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/ComboStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/ComboStatistic.cs index e13138c5a0..b92c244174 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/ComboStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/ComboStatistic.cs @@ -38,7 +38,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics if (isPerfect) { - using (BeginDelayedSequence(AccuracyCircle.ACCURACY_TRANSFORM_DURATION / 2, true)) + using (BeginDelayedSequence(AccuracyCircle.ACCURACY_TRANSFORM_DURATION / 2)) perfectText.FadeIn(50); } } diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index f66a998db6..6ddecf8297 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -234,7 +234,7 @@ namespace osu.Game.Screens.Ranking bool topLayerExpanded = topLayerContainer.Y < 0; // If the top layer was already expanded, then we don't need to wait for the resize and can instead transform immediately. This looks better when changing the panel state. - using (BeginDelayedSequence(topLayerExpanded ? 0 : RESIZE_DURATION + TOP_LAYER_EXPAND_DELAY, true)) + using (BeginDelayedSequence(topLayerExpanded ? 0 : RESIZE_DURATION + TOP_LAYER_EXPAND_DELAY)) { topLayerContainer.FadeIn(); diff --git a/osu.Game/Screens/ScreenWhiteBox.cs b/osu.Game/Screens/ScreenWhiteBox.cs index cf0c183766..8b38b67f5c 100644 --- a/osu.Game/Screens/ScreenWhiteBox.cs +++ b/osu.Game/Screens/ScreenWhiteBox.cs @@ -191,7 +191,7 @@ namespace osu.Game.Screens boxContainer.ScaleTo(0.2f); boxContainer.RotateTo(-20); - using (BeginDelayedSequence(300, true)) + using (BeginDelayedSequence(300)) { boxContainer.ScaleTo(1, transition_time, Easing.OutElastic); boxContainer.RotateTo(0, transition_time / 2, Easing.OutQuint); diff --git a/osu.Game/Skinning/LegacyJudgementPieceNew.cs b/osu.Game/Skinning/LegacyJudgementPieceNew.cs index ca25efaa01..e76f251ce5 100644 --- a/osu.Game/Skinning/LegacyJudgementPieceNew.cs +++ b/osu.Game/Skinning/LegacyJudgementPieceNew.cs @@ -72,7 +72,7 @@ namespace osu.Game.Skinning if (particles != null) { // start the particles already some way into their animation to break cluster away from centre. - using (particles.BeginDelayedSequence(-100, true)) + using (particles.BeginDelayedSequence(-100)) particles.Restart(); } diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index d7b02ef797..a393802309 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -94,7 +94,7 @@ namespace osu.Game.Tests.Visual private readonly WorkingBeatmap testBeatmap; public TestBeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore resources, GameHost host, WorkingBeatmap defaultBeatmap, WorkingBeatmap testBeatmap) - : base(storage, contextFactory, rulesets, api, audioManager, resources, host, defaultBeatmap, false) + : base(storage, contextFactory, rulesets, api, audioManager, resources, host, defaultBeatmap) { this.testBeatmap = testBeatmap; } diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 98aad821ce..57e400a77e 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -176,7 +176,7 @@ namespace osu.Game.Tests.Visual protected virtual IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset); protected WorkingBeatmap CreateWorkingBeatmap(RulesetInfo ruleset) => - CreateWorkingBeatmap(CreateBeatmap(ruleset), null); + CreateWorkingBeatmap(CreateBeatmap(ruleset)); protected virtual WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) => new ClockBackedTestWorkingBeatmap(beatmap, storyboard, Clock, Audio); From f5ddd2a53bd0db4d49e6352a386ae62bf92d6d31 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jul 2021 01:15:30 +0900 Subject: [PATCH 0169/2442] Fix critical failure causing scores to not update on the leaderboard --- .../Play/HUD/MultiplayerGameplayLeaderboard.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 0c13e87f9f..4783ce2f40 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -71,11 +71,19 @@ namespace osu.Game.Screens.Play.HUD UserScores[user.Id] = trackedUser; } - - prepareDataStreams(); })); } + protected override void LoadComplete() + { + base.LoadComplete(); + + // this is *required* to be here due to the spectator leaderboard not correctly populating clocks if done later. + // note that running this here is probably not 100% correct (if a user quits before user population occurs for instance, + // an incorrect state will be reached). + prepareDataStreams(); + } + private void prepareDataStreams() { // BindableList handles binding in a really bad way (Clear then AddRange) so we need to do this manually.. From d495196b66d7d3b63cd3909f08778b21f1e071bf Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Mon, 5 Jul 2021 19:22:55 +0200 Subject: [PATCH 0170/2442] Share item cycling logic with GameplayMenuOverlay --- .../Gameplay/TestSceneGameplayMenuOverlay.cs | 34 ++++----- .../SelectionCycleFillFlowContainer.cs | 55 ++++++++++++++ .../Graphics/UserInterface/DialogButton.cs | 36 +++++---- .../Graphics/UserInterface/ISelectable.cs | 10 +++ osu.Game/Overlays/Volume/VolumeMeter.cs | 45 +++++++----- osu.Game/Overlays/VolumeOverlay.cs | 70 +++++++----------- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 73 +++++-------------- 7 files changed, 174 insertions(+), 149 deletions(-) create mode 100644 osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs create mode 100644 osu.Game/Graphics/UserInterface/ISelectable.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs index d69ac665cc..0aafbda951 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs @@ -87,7 +87,7 @@ namespace osu.Game.Tests.Visual.Gameplay showOverlay(); AddStep("Up arrow", () => InputManager.Key(Key.Up)); - AddAssert("Last button selected", () => pauseOverlay.Buttons.Last().Selected.Value); + AddAssert("Last button selected", () => pauseOverlay.Buttons.Last().Selected); } /// @@ -99,7 +99,7 @@ namespace osu.Game.Tests.Visual.Gameplay showOverlay(); AddStep("Down arrow", () => InputManager.Key(Key.Down)); - AddAssert("First button selected", () => getButton(0).Selected.Value); + AddAssert("First button selected", () => getButton(0).Selected); } /// @@ -111,11 +111,11 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Show overlay", () => failOverlay.Show()); AddStep("Up arrow", () => InputManager.Key(Key.Up)); - AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected.Value); + AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected); AddStep("Up arrow", () => InputManager.Key(Key.Up)); - AddAssert("First button selected", () => failOverlay.Buttons.First().Selected.Value); + AddAssert("First button selected", () => failOverlay.Buttons.First().Selected); AddStep("Up arrow", () => InputManager.Key(Key.Up)); - AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected.Value); + AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected); } /// @@ -127,11 +127,11 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Show overlay", () => failOverlay.Show()); AddStep("Down arrow", () => InputManager.Key(Key.Down)); - AddAssert("First button selected", () => failOverlay.Buttons.First().Selected.Value); + AddAssert("First button selected", () => failOverlay.Buttons.First().Selected); AddStep("Down arrow", () => InputManager.Key(Key.Down)); - AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected.Value); + AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected); AddStep("Down arrow", () => InputManager.Key(Key.Down)); - AddAssert("First button selected", () => failOverlay.Buttons.First().Selected.Value); + AddAssert("First button selected", () => failOverlay.Buttons.First().Selected); } /// @@ -145,7 +145,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Hover first button", () => InputManager.MoveMouseTo(failOverlay.Buttons.First())); AddStep("Hide overlay", () => failOverlay.Hide()); - AddAssert("Overlay state is reset", () => !failOverlay.Buttons.Any(b => b.Selected.Value)); + AddAssert("Overlay state is reset", () => !failOverlay.Buttons.Any(b => b.Selected)); } /// @@ -162,11 +162,11 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Hide overlay", () => pauseOverlay.Hide()); showOverlay(); - AddAssert("First button not selected", () => !getButton(0).Selected.Value); + AddAssert("First button not selected", () => !getButton(0).Selected); AddStep("Move slightly", () => InputManager.MoveMouseTo(InputManager.CurrentState.Mouse.Position + new Vector2(1))); - AddAssert("First button selected", () => getButton(0).Selected.Value); + AddAssert("First button selected", () => getButton(0).Selected); } /// @@ -179,8 +179,8 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Down arrow", () => InputManager.Key(Key.Down)); AddStep("Hover second button", () => InputManager.MoveMouseTo(getButton(1))); - AddAssert("First button not selected", () => !getButton(0).Selected.Value); - AddAssert("Second button selected", () => getButton(1).Selected.Value); + AddAssert("First button not selected", () => !getButton(0).Selected); + AddAssert("Second button selected", () => getButton(1).Selected); } /// @@ -196,8 +196,8 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Hover second button", () => InputManager.MoveMouseTo(getButton(1))); AddStep("Up arrow", () => InputManager.Key(Key.Up)); - AddAssert("Second button not selected", () => !getButton(1).Selected.Value); - AddAssert("First button selected", () => getButton(0).Selected.Value); + AddAssert("Second button not selected", () => !getButton(1).Selected); + AddAssert("First button selected", () => getButton(0).Selected); } /// @@ -211,7 +211,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Hover second button", () => InputManager.MoveMouseTo(getButton(1))); AddStep("Unhover second button", () => InputManager.MoveMouseTo(Vector2.Zero)); AddStep("Down arrow", () => InputManager.Key(Key.Down)); - AddAssert("First button selected", () => getButton(0).Selected.Value); // Initial state condition + AddAssert("First button selected", () => getButton(0).Selected); // Initial state condition } /// @@ -282,7 +282,7 @@ namespace osu.Game.Tests.Visual.Gameplay showOverlay(); AddAssert("No button selected", - () => pauseOverlay.Buttons.All(button => !button.Selected.Value)); + () => pauseOverlay.Buttons.All(button => !button.Selected)); } private void showOverlay() => AddStep("Show overlay", () => pauseOverlay.Show()); diff --git a/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs b/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs new file mode 100644 index 0000000000..0e7b2bcc05 --- /dev/null +++ b/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs @@ -0,0 +1,55 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Graphics.Containers +{ + /// + /// A FillFlowContainer that provides functionality to cycle selection between children + /// The selection wraps around when overflowing past the first or last child. + /// + public class SelectionCycleFillFlowContainer : FillFlowContainer where T : Drawable, ISelectable + { + private int selectedIndex = -1; + + private void setSelected(int value) + { + if (selectedIndex == value) + return; + + // Deselect the previously-selected button + if (selectedIndex != -1) + this[selectedIndex].Selected = false; + + selectedIndex = value; + + // Select the newly-selected button + if (selectedIndex != -1) + this[selectedIndex].Selected = true; + } + + public void SelectNext() + { + if (selectedIndex == -1 || selectedIndex == Count - 1) + setSelected(0); + else + setSelected(selectedIndex + 1); + } + + public void SelectPrevious() + { + if (selectedIndex == -1 || selectedIndex == 0) + setSelected(Count - 1); + else + setSelected(selectedIndex - 1); + } + + public void Deselect() => setSelected(-1); + public void Select(T item) => setSelected(IndexOf(item)); + + public T Selected => (selectedIndex >= 0 && selectedIndex < Count) ? this[selectedIndex] : null; + } +} diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs index 1047aa4255..718a5171c2 100644 --- a/osu.Game/Graphics/UserInterface/DialogButton.cs +++ b/osu.Game/Graphics/UserInterface/DialogButton.cs @@ -2,24 +2,24 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; -using osuTK; -using osuTK.Graphics; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.Backgrounds; -using osu.Game.Graphics.Sprites; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics.Effects; -using osu.Game.Graphics.Containers; using osu.Framework.Input.Events; using osu.Framework.Localisation; +using osu.Game.Graphics.Backgrounds; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osuTK; +using osuTK.Graphics; namespace osu.Game.Graphics.UserInterface { - public class DialogButton : OsuClickableContainer + public class DialogButton : OsuClickableContainer, ISelectable { private const float idle_width = 0.8f; private const float hover_width = 0.9f; @@ -27,7 +27,13 @@ namespace osu.Game.Graphics.UserInterface private const float hover_duration = 500; private const float click_duration = 200; - public readonly BindableBool Selected = new BindableBool(); + public readonly BindableBool SelectedBindable = new BindableBool(); + + public bool Selected + { + get => SelectedBindable.Value; + set => SelectedBindable.Value = value; + } private readonly Container backgroundContainer; private readonly Container colourContainer; @@ -153,7 +159,7 @@ namespace osu.Game.Graphics.UserInterface updateGlow(); - Selected.ValueChanged += selectionChanged; + SelectedBindable.ValueChanged += selectionChanged; } private Color4 buttonColour; @@ -221,7 +227,7 @@ namespace osu.Game.Graphics.UserInterface .OnComplete(_ => { clickAnimating = false; - Selected.TriggerChange(); + SelectedBindable.TriggerChange(); }); return base.OnClick(e); @@ -235,7 +241,7 @@ namespace osu.Game.Graphics.UserInterface protected override void OnMouseUp(MouseUpEvent e) { - if (Selected.Value) + if (Selected) colourContainer.ResizeWidthTo(hover_width, click_duration, Easing.In); base.OnMouseUp(e); } @@ -243,7 +249,7 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnHover(HoverEvent e) { base.OnHover(e); - Selected.Value = true; + Selected = true; return true; } @@ -251,7 +257,7 @@ namespace osu.Game.Graphics.UserInterface protected override void OnHoverLost(HoverLostEvent e) { base.OnHoverLost(e); - Selected.Value = false; + Selected = false; } private void selectionChanged(ValueChangedEvent args) diff --git a/osu.Game/Graphics/UserInterface/ISelectable.cs b/osu.Game/Graphics/UserInterface/ISelectable.cs new file mode 100644 index 0000000000..49c3d725c8 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/ISelectable.cs @@ -0,0 +1,10 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Graphics.UserInterface +{ + public interface ISelectable + { + bool Selected { get; set; } + } +} diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs index 9210001a3a..3e9849a077 100644 --- a/osu.Game/Overlays/Volume/VolumeMeter.cs +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -19,13 +19,14 @@ using osu.Framework.Threading; using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osuTK; using osuTK.Graphics; namespace osu.Game.Overlays.Volume { - public class VolumeMeter : Container, IKeyBindingHandler + public class VolumeMeter : Container, IKeyBindingHandler, ISelectable { private CircularProgress volumeCircle; private CircularProgress volumeCircleGlow; @@ -43,7 +44,13 @@ namespace osu.Game.Overlays.Volume private Sample sample; private double sampleLastPlaybackTime; - public Action RequestFocus; + public BindableBool SelectedBindable = new BindableBool(); + + public bool Selected + { + get => SelectedBindable.Value; + set => SelectedBindable.Value = value; + } public VolumeMeter(string name, float circleSize, Color4 meterColour) { @@ -212,6 +219,8 @@ namespace osu.Game.Overlays.Volume Bindable.BindValueChanged(volume => { this.TransformTo(nameof(DisplayVolume), volume.NewValue, 400, Easing.OutQuint); }, true); bgProgress.Current.Value = 0.75f; + + SelectedBindable.ValueChanged += selectionChanged; } private int? displayVolumeInt; @@ -330,21 +339,9 @@ namespace osu.Game.Overlays.Volume private const float transition_length = 500; - public void Focus() - { - this.ScaleTo(1.04f, transition_length, Easing.OutExpo); - focusGlowContainer.FadeIn(transition_length, Easing.OutExpo); - } - - public void Unfocus() - { - this.ScaleTo(1f, transition_length, Easing.OutExpo); - focusGlowContainer.FadeOut(transition_length, Easing.OutExpo); - } - protected override bool OnHover(HoverEvent e) { - RequestFocus?.Invoke(this); + Selected = true; return false; } @@ -352,6 +349,20 @@ namespace osu.Game.Overlays.Volume { } + private void selectionChanged(ValueChangedEvent selected) + { + if (selected.NewValue) + { + this.ScaleTo(1.04f, transition_length, Easing.OutExpo); + focusGlowContainer.FadeIn(transition_length, Easing.OutExpo); + } + else + { + this.ScaleTo(1f, transition_length, Easing.OutExpo); + focusGlowContainer.FadeOut(transition_length, Easing.OutExpo); + } + } + public bool OnPressed(GlobalAction action) { if (!IsHovered) @@ -360,12 +371,12 @@ namespace osu.Game.Overlays.Volume switch (action) { case GlobalAction.SelectPrevious: - RequestFocus?.Invoke(this); + Selected = true; adjust(1, false); return true; case GlobalAction.SelectNext: - RequestFocus?.Invoke(this); + Selected = true; adjust(-1, false); return true; } diff --git a/osu.Game/Overlays/VolumeOverlay.cs b/osu.Game/Overlays/VolumeOverlay.cs index dca39774f2..0c56e48cd4 100644 --- a/osu.Game/Overlays/VolumeOverlay.cs +++ b/osu.Game/Overlays/VolumeOverlay.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.Threading; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Input.Bindings; using osu.Game.Overlays.Volume; using osuTK; @@ -32,7 +33,7 @@ namespace osu.Game.Overlays public Bindable IsMuted { get; } = new Bindable(); - private FillFlowContainer volumeMeters; + private SelectionCycleFillFlowContainer volumeMeters; [BackgroundDependencyLoader] private void load(AudioManager audio, OsuColour colours) @@ -55,7 +56,7 @@ namespace osu.Game.Overlays Margin = new MarginPadding(10), Current = { BindTarget = IsMuted } }, - volumeMeters = new FillFlowContainer + volumeMeters = new SelectionCycleFillFlowContainer { Direction = FillDirection.Vertical, AutoSizeAxes = Axes.Both, @@ -65,18 +66,9 @@ namespace osu.Game.Overlays Margin = new MarginPadding { Left = offset }, Children = new[] { - volumeMeterEffect = new VolumeMeter("EFFECTS", 125, colours.BlueDarker) - { - RequestFocus = v => focusedMeter.Value = v - }, - volumeMeterMaster = new VolumeMeter("MASTER", 150, colours.PinkDarker) - { - RequestFocus = v => focusedMeter.Value = v - }, - volumeMeterMusic = new VolumeMeter("MUSIC", 125, colours.BlueDarker) - { - RequestFocus = v => focusedMeter.Value = v - }, + volumeMeterEffect = new VolumeMeter("EFFECTS", 125, colours.BlueDarker), + volumeMeterMaster = new VolumeMeter("MASTER", 150, colours.PinkDarker), + volumeMeterMusic = new VolumeMeter("MUSIC", 125, colours.BlueDarker), } } }); @@ -92,23 +84,18 @@ namespace osu.Game.Overlays else audio.RemoveAdjustment(AdjustableProperty.Volume, muteAdjustment); }); - - focusedMeter.BindValueChanged(meter => - { - meter.OldValue?.Unfocus(); - meter.NewValue?.Focus(); - }); } - private readonly Bindable focusedMeter = new Bindable(); - protected override void LoadComplete() { base.LoadComplete(); - volumeMeterMaster.Bindable.ValueChanged += _ => Show(); - volumeMeterEffect.Bindable.ValueChanged += _ => Show(); - volumeMeterMusic.Bindable.ValueChanged += _ => Show(); + foreach (var volumeMeter in volumeMeters) + { + volumeMeter.Bindable.ValueChanged += _ => Show(); + volumeMeter.SelectedBindable.ValueChanged += selected => volumeMeterSelectionChanged(volumeMeter, selected.NewValue); + } + muteButton.Current.ValueChanged += _ => Show(); } @@ -122,28 +109,26 @@ namespace osu.Game.Overlays if (State.Value == Visibility.Hidden) Show(); else - focusedMeter.Value.Decrease(amount, isPrecise); + volumeMeters.Selected?.Decrease(amount, isPrecise); return true; case GlobalAction.IncreaseVolume: if (State.Value == Visibility.Hidden) Show(); else - focusedMeter.Value.Increase(amount, isPrecise); + volumeMeters.Selected?.Increase(amount, isPrecise); return true; case GlobalAction.NextVolumeMeter: - if (State.Value == Visibility.Hidden) - Show(); - else - focusShift(1); + if (State.Value == Visibility.Visible) + volumeMeters.SelectNext(); + Show(); return true; case GlobalAction.PreviousVolumeMeter: - if (State.Value == Visibility.Hidden) - Show(); - else - focusShift(-1); + if (State.Value == Visibility.Visible) + volumeMeters.SelectPrevious(); + Show(); return true; case GlobalAction.ToggleMute: @@ -155,15 +140,12 @@ namespace osu.Game.Overlays return false; } - private void focusShift(int direction = 1) + private void volumeMeterSelectionChanged(VolumeMeter meter, bool isSelected) { - Show(); - - var newIndex = volumeMeters.IndexOf(focusedMeter.Value) + direction; - if (newIndex < 0) - newIndex += volumeMeters.Count; - - focusedMeter.Value = volumeMeters.Children[newIndex % volumeMeters.Count]; + if (!isSelected) + volumeMeters.Deselect(); + else + volumeMeters.Select(meter); } private ScheduledDelegate popOutDelegate; @@ -172,7 +154,7 @@ namespace osu.Game.Overlays { // Focus on the master meter as a default if previously hidden if (State.Value == Visibility.Hidden) - focusedMeter.Value = volumeMeterMaster; + volumeMeters.Select(volumeMeterMaster); if (State.Value == Visibility.Visible) schedulePopOut(); diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index 4a28da0dde..876f33d814 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -2,23 +2,24 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; +using System.Linq; +using Humanizer; +using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osuTK; -using osuTK.Graphics; -using osu.Game.Graphics; -using osu.Framework.Allocation; -using osu.Game.Graphics.UserInterface; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; -using System.Collections.Generic; -using System.Linq; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; -using Humanizer; -using osu.Framework.Graphics.Effects; +using osuTK; +using osuTK.Graphics; namespace osu.Game.Screens.Play { @@ -41,18 +42,18 @@ namespace osu.Game.Screens.Play /// /// Action that is invoked when is triggered. /// - protected virtual Action BackAction => () => InternalButtons.Children.LastOrDefault()?.Click(); + protected virtual Action BackAction => () => InternalButtons.Selected?.Click(); /// /// Action that is invoked when is triggered. /// - protected virtual Action SelectAction => () => InternalButtons.Children.FirstOrDefault(f => f.Selected.Value)?.Click(); + protected virtual Action SelectAction => () => InternalButtons.Selected?.Click(); public abstract string Header { get; } public abstract string Description { get; } - protected ButtonContainer InternalButtons; + protected SelectionCycleFillFlowContainer InternalButtons; public IReadOnlyList Buttons => InternalButtons; private FillFlowContainer retryCounterContainer; @@ -116,7 +117,7 @@ namespace osu.Game.Screens.Play } } }, - InternalButtons = new ButtonContainer + InternalButtons = new SelectionCycleFillFlowContainer { Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, @@ -183,7 +184,7 @@ namespace osu.Game.Screens.Play } }; - button.Selected.ValueChanged += selected => buttonSelectionChanged(button, selected.NewValue); + button.SelectedBindable.ValueChanged += selected => buttonSelectionChanged(button, selected.NewValue); InternalButtons.Add(button); } @@ -255,46 +256,6 @@ namespace osu.Game.Screens.Play }; } - protected class ButtonContainer : FillFlowContainer - { - private int selectedIndex = -1; - - private void setSelected(int value) - { - if (selectedIndex == value) - return; - - // Deselect the previously-selected button - if (selectedIndex != -1) - this[selectedIndex].Selected.Value = false; - - selectedIndex = value; - - // Select the newly-selected button - if (selectedIndex != -1) - this[selectedIndex].Selected.Value = true; - } - - public void SelectNext() - { - if (selectedIndex == -1 || selectedIndex == Count - 1) - setSelected(0); - else - setSelected(selectedIndex + 1); - } - - public void SelectPrevious() - { - if (selectedIndex == -1 || selectedIndex == 0) - setSelected(Count - 1); - else - setSelected(selectedIndex - 1); - } - - public void Deselect() => setSelected(-1); - public void Select(DialogButton button) => setSelected(IndexOf(button)); - } - private class Button : DialogButton { // required to ensure keyboard navigation always starts from an extremity (unless the cursor is moved) @@ -302,7 +263,7 @@ namespace osu.Game.Screens.Play protected override bool OnMouseMove(MouseMoveEvent e) { - Selected.Value = true; + Selected = true; return base.OnMouseMove(e); } } From 8e102280b7e289cb7d564b4ae7b8ba8fddde5cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 5 Jul 2021 20:21:44 +0200 Subject: [PATCH 0171/2442] Revert & disable case with two contradicting inspections Leaving the `false` default value without the suppression triggers `RedundantArgumentDefaultValue`, while removing it triggers `BaseMethodCallWithDefaultParameter`. Disable the former, because a single redundant parameter is less bad than silent breakage if the default value of `propagateChildren` in the base method ever changes. --- osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs index 1f988df947..8943a91076 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/SliderBall.cs @@ -86,7 +86,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default public override void ApplyTransformsAt(double time, bool propagateChildren = false) { // For the same reasons as above w.r.t rewinding, we shouldn't propagate to children here either. - base.ApplyTransformsAt(time); + // ReSharper disable once RedundantArgumentDefaultValue - removing the "redundant" default value triggers BaseMethodCallWithDefaultParameter + base.ApplyTransformsAt(time, false); } private bool tracking; From 686dd2b5ce552a02628844f58a34ecbdddf0d27a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 5 Jul 2021 20:35:14 +0200 Subject: [PATCH 0172/2442] Remove unused constant --- osu.Game/Screens/Play/HUD/LegacyComboCounter.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs index edddc06746..5c5b66d496 100644 --- a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs @@ -23,8 +23,6 @@ namespace osu.Game.Screens.Play.HUD private const double pop_out_duration = 150; - private const Easing pop_out_easing = Easing.None; - private const double fade_out_duration = 100; /// From af02a1efcbf6841b55b9925d80a8058840ae481e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 5 Jul 2021 21:02:38 +0200 Subject: [PATCH 0173/2442] Bump redundant argument default value inspection severity to warning --- osu.sln.DotSettings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index d2c5b1223c..7284ca1a9a 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -130,7 +130,7 @@ HINT WARNING WARNING - HINT + WARNING WARNING WARNING WARNING From 7f2baef998947dd37accb18c2f94b6db5d66f181 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jul 2021 14:24:17 +0900 Subject: [PATCH 0174/2442] Remove all `.resx` files from osu project --- osu.Game/Localisation/ButtonSystem.ja.resx | 38 ---------- osu.Game/Localisation/ButtonSystem.resx | 88 ---------------------- osu.Game/Localisation/Chat.resx | 67 ---------------- osu.Game/Localisation/Common.resx | 64 ---------------- osu.Game/Localisation/Notifications.resx | 67 ---------------- osu.Game/Localisation/NowPlaying.resx | 67 ---------------- osu.Game/Localisation/Settings.resx | 67 ---------------- 7 files changed, 458 deletions(-) delete mode 100644 osu.Game/Localisation/ButtonSystem.ja.resx delete mode 100644 osu.Game/Localisation/ButtonSystem.resx delete mode 100644 osu.Game/Localisation/Chat.resx delete mode 100644 osu.Game/Localisation/Common.resx delete mode 100644 osu.Game/Localisation/Notifications.resx delete mode 100644 osu.Game/Localisation/NowPlaying.resx delete mode 100644 osu.Game/Localisation/Settings.resx diff --git a/osu.Game/Localisation/ButtonSystem.ja.resx b/osu.Game/Localisation/ButtonSystem.ja.resx deleted file mode 100644 index 02f3e7ce2f..0000000000 --- a/osu.Game/Localisation/ButtonSystem.ja.resx +++ /dev/null @@ -1,38 +0,0 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - ソロ - - - プレイリスト - - - 遊ぶ - - - マルチ - - - エディット - - - ブラウズ - - - 閉じる - - - 設定 - - diff --git a/osu.Game/Localisation/ButtonSystem.resx b/osu.Game/Localisation/ButtonSystem.resx deleted file mode 100644 index d72ffff8be..0000000000 --- a/osu.Game/Localisation/ButtonSystem.resx +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - solo - - - multi - - - playlists - - - play - - - edit - - - browse - - - settings - - - back - - - exit - - \ No newline at end of file diff --git a/osu.Game/Localisation/Chat.resx b/osu.Game/Localisation/Chat.resx deleted file mode 100644 index 055e351463..0000000000 --- a/osu.Game/Localisation/Chat.resx +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - chat - - - join the real-time discussion - - \ No newline at end of file diff --git a/osu.Game/Localisation/Common.resx b/osu.Game/Localisation/Common.resx deleted file mode 100644 index 59de16a037..0000000000 --- a/osu.Game/Localisation/Common.resx +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Cancel - - \ No newline at end of file diff --git a/osu.Game/Localisation/Notifications.resx b/osu.Game/Localisation/Notifications.resx deleted file mode 100644 index 08db240ba2..0000000000 --- a/osu.Game/Localisation/Notifications.resx +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - notifications - - - waiting for 'ya - - \ No newline at end of file diff --git a/osu.Game/Localisation/NowPlaying.resx b/osu.Game/Localisation/NowPlaying.resx deleted file mode 100644 index 40fda3e25b..0000000000 --- a/osu.Game/Localisation/NowPlaying.resx +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - now playing - - - manage the currently playing track - - \ No newline at end of file diff --git a/osu.Game/Localisation/Settings.resx b/osu.Game/Localisation/Settings.resx deleted file mode 100644 index 85c224cedf..0000000000 --- a/osu.Game/Localisation/Settings.resx +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - settings - - - change the way osu! behaves - - \ No newline at end of file From 0ecda98b393c46991893db8e4d89f7dcec976bed Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jul 2021 14:28:52 +0900 Subject: [PATCH 0175/2442] Update prefixes to match new resx locations --- osu.Game/Localisation/ButtonSystemStrings.cs | 4 ++-- osu.Game/Localisation/ChatStrings.cs | 2 +- osu.Game/Localisation/CommonStrings.cs | 2 +- osu.Game/Localisation/NotificationsStrings.cs | 2 +- osu.Game/Localisation/NowPlayingStrings.cs | 2 +- osu.Game/Localisation/SettingsStrings.cs | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Localisation/ButtonSystemStrings.cs b/osu.Game/Localisation/ButtonSystemStrings.cs index 8083f80782..ba4abf63a6 100644 --- a/osu.Game/Localisation/ButtonSystemStrings.cs +++ b/osu.Game/Localisation/ButtonSystemStrings.cs @@ -7,7 +7,7 @@ namespace osu.Game.Localisation { public static class ButtonSystemStrings { - private const string prefix = @"osu.Game.Localisation.ButtonSystem"; + private const string prefix = @"osu.Game.Resources.Localisation.ButtonSystem"; /// /// "solo" @@ -56,4 +56,4 @@ namespace osu.Game.Localisation private static string getKey(string key) => $@"{prefix}:{key}"; } -} \ No newline at end of file +} diff --git a/osu.Game/Localisation/ChatStrings.cs b/osu.Game/Localisation/ChatStrings.cs index 636351470b..7bd284a94e 100644 --- a/osu.Game/Localisation/ChatStrings.cs +++ b/osu.Game/Localisation/ChatStrings.cs @@ -7,7 +7,7 @@ namespace osu.Game.Localisation { public static class ChatStrings { - private const string prefix = @"osu.Game.Localisation.Chat"; + private const string prefix = @"osu.Game.Resources.Localisation.Chat"; /// /// "chat" diff --git a/osu.Game/Localisation/CommonStrings.cs b/osu.Game/Localisation/CommonStrings.cs index ced0d80955..50e01f06fc 100644 --- a/osu.Game/Localisation/CommonStrings.cs +++ b/osu.Game/Localisation/CommonStrings.cs @@ -7,7 +7,7 @@ namespace osu.Game.Localisation { public static class CommonStrings { - private const string prefix = @"osu.Game.Localisation.Common"; + private const string prefix = @"osu.Game.Resources.Localisation.Common"; /// /// "Cancel" diff --git a/osu.Game/Localisation/NotificationsStrings.cs b/osu.Game/Localisation/NotificationsStrings.cs index ba28ef5560..382e0d81f4 100644 --- a/osu.Game/Localisation/NotificationsStrings.cs +++ b/osu.Game/Localisation/NotificationsStrings.cs @@ -7,7 +7,7 @@ namespace osu.Game.Localisation { public static class NotificationsStrings { - private const string prefix = @"osu.Game.Localisation.Notifications"; + private const string prefix = @"osu.Game.Resources.Localisation.Notifications"; /// /// "notifications" diff --git a/osu.Game/Localisation/NowPlayingStrings.cs b/osu.Game/Localisation/NowPlayingStrings.cs index 47646b0f68..f334637338 100644 --- a/osu.Game/Localisation/NowPlayingStrings.cs +++ b/osu.Game/Localisation/NowPlayingStrings.cs @@ -7,7 +7,7 @@ namespace osu.Game.Localisation { public static class NowPlayingStrings { - private const string prefix = @"osu.Game.Localisation.NowPlaying"; + private const string prefix = @"osu.Game.Resources.Localisation.NowPlaying"; /// /// "now playing" diff --git a/osu.Game/Localisation/SettingsStrings.cs b/osu.Game/Localisation/SettingsStrings.cs index f4b417fa28..aa2e2740eb 100644 --- a/osu.Game/Localisation/SettingsStrings.cs +++ b/osu.Game/Localisation/SettingsStrings.cs @@ -7,7 +7,7 @@ namespace osu.Game.Localisation { public static class SettingsStrings { - private const string prefix = @"osu.Game.Localisation.Settings"; + private const string prefix = @"osu.Game.Resources.Localisation.Settings"; /// /// "settings" From 1a6b8b2c7353a5d3a988db49db138900ac271761 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jul 2021 14:53:31 +0900 Subject: [PATCH 0176/2442] Populate `UserScores` as early as possible to avoid weird ordering requirements --- .../HUD/MultiplayerGameplayLeaderboard.cs | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 4783ce2f40..a10c16fcd5 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -53,6 +53,13 @@ namespace osu.Game.Screens.Play.HUD { scoringMode = config.GetBindable(OsuSetting.ScoreDisplayMode); + foreach (var userId in playingUsers) + { + var trackedUser = CreateUserData(userId, scoreProcessor); + trackedUser.ScoringMode.BindTo(scoringMode); + UserScores[userId] = trackedUser; + } + userLookupCache.GetUsersAsync(playingUsers.ToArray()).ContinueWith(users => Schedule(() => { foreach (var user in users.Result) @@ -60,16 +67,13 @@ namespace osu.Game.Screens.Play.HUD if (user == null) continue; - var trackedUser = CreateUserData(user.Id, scoreProcessor); - trackedUser.ScoringMode.BindTo(scoringMode); + var trackedUser = UserScores[user.Id]; var leaderboardScore = AddPlayer(user, user.Id == api.LocalUser.Value.Id); leaderboardScore.Accuracy.BindTo(trackedUser.Accuracy); leaderboardScore.TotalScore.BindTo(trackedUser.Score); leaderboardScore.Combo.BindTo(trackedUser.CurrentCombo); leaderboardScore.HasQuit.BindTo(trackedUser.UserQuit); - - UserScores[user.Id] = trackedUser; } })); } @@ -78,14 +82,6 @@ namespace osu.Game.Screens.Play.HUD { base.LoadComplete(); - // this is *required* to be here due to the spectator leaderboard not correctly populating clocks if done later. - // note that running this here is probably not 100% correct (if a user quits before user population occurs for instance, - // an incorrect state will be reached). - prepareDataStreams(); - } - - private void prepareDataStreams() - { // BindableList handles binding in a really bad way (Clear then AddRange) so we need to do this manually.. foreach (int userId in playingUsers) { @@ -95,6 +91,8 @@ namespace osu.Game.Screens.Play.HUD usersChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new[] { userId })); } + // bind here is to support players leaving the match. + // new players are not supported. playingUsers.BindTo(multiplayerClient.CurrentMatchPlayingUserIds); playingUsers.BindCollectionChanged(usersChanged); From 402b527903e92c0332d86f52b6eb5835682ad0bc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 6 Jul 2021 14:54:56 +0900 Subject: [PATCH 0177/2442] Add .editorconfig for localisation analyser --- osu.Game/.editorconfig | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 osu.Game/.editorconfig diff --git a/osu.Game/.editorconfig b/osu.Game/.editorconfig new file mode 100644 index 0000000000..46a3dafd04 --- /dev/null +++ b/osu.Game/.editorconfig @@ -0,0 +1,2 @@ +[*.cs] +dotnet_diagnostic.OLOC001.prefix_namespace = osu.Game.Resources.Localisation \ No newline at end of file From 0658cfb986b4f8a06fac444075d7fa004be19cf3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jul 2021 14:56:00 +0900 Subject: [PATCH 0178/2442] Throw exceptions rather than silently failing if attempting to add a clock for a non-tracked user --- .../Multiplayer/Spectate/MultiSpectatorLeaderboard.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorLeaderboard.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorLeaderboard.cs index ab3ead68b5..55c4270c70 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorLeaderboard.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorLeaderboard.cs @@ -19,7 +19,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate public void AddClock(int userId, IClock clock) { if (!UserScores.TryGetValue(userId, out var data)) - return; + throw new ArgumentException(@"Provided user is not tracked by this leaderboard", nameof(userId)); ((SpectatingTrackedUserData)data).Clock = clock; } @@ -27,7 +27,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate public void RemoveClock(int userId) { if (!UserScores.TryGetValue(userId, out var data)) - return; + throw new ArgumentException(@"Provided user is not tracked by this leaderboard", nameof(userId)); ((SpectatingTrackedUserData)data).Clock = null; } From 1beb85a26f699345bad516da6ba35054489d3399 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 6 Jul 2021 14:56:43 +0900 Subject: [PATCH 0179/2442] Bump localisation analyser packages --- .config/dotnet-tools.json | 2 +- osu.Game/osu.Game.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index b3f7c67c51..97fcb52ab1 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -27,7 +27,7 @@ ] }, "ppy.localisationanalyser.tools": { - "version": "2021.608.0", + "version": "2021.705.0", "commands": [ "localisation" ] diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 357aa89329..626be76a84 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -31,7 +31,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 7f7cf0b927124ede7673b8e28b2cfc6628ed0386 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jul 2021 15:08:00 +0900 Subject: [PATCH 0180/2442] Fix potential failure during cleanup of files in migration tests --- .../NonVisual/CustomTourneyDirectoryTest.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs b/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs index 61f8511e3c..d2369056e1 100644 --- a/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs +++ b/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs @@ -20,7 +20,7 @@ namespace osu.Game.Tournament.Tests.NonVisual [Test] public void TestDefaultDirectory() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestDefaultDirectory))) { try { @@ -139,8 +139,13 @@ namespace osu.Game.Tournament.Tests.NonVisual } finally { - host.Storage.Delete("tournament.ini"); - host.Storage.DeleteDirectory("tournaments"); + try + { + host.Storage.Delete("tournament.ini"); + host.Storage.DeleteDirectory("tournaments"); + } + catch { } + host.Exit(); } } From ae1b1cbec9333887806a2085c833747a628a5710 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 22 Jun 2021 17:33:32 +0900 Subject: [PATCH 0181/2442] Allow serialization of catch hit objects --- osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs | 15 +++++++++++++-- osu.Game.Rulesets.Catch/Objects/JuiceStream.cs | 5 +++++ .../Objects/PalpableCatchHitObject.cs | 2 ++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index ae45182960..cdd8bfbe50 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using Newtonsoft.Json; using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -20,6 +21,11 @@ namespace osu.Game.Rulesets.Catch.Objects /// /// The horizontal position of the hit object between 0 and . /// + /// + /// Only setter is exposed. + /// Use or to get the horizontal position. + /// + [JsonIgnore] public float X { set => OriginalXBindable.Value = value; @@ -34,6 +40,7 @@ namespace osu.Game.Rulesets.Catch.Objects /// public float XOffset { + get => XOffsetBindable.Value; set => XOffsetBindable.Value = value; } @@ -44,7 +51,11 @@ namespace osu.Game.Rulesets.Catch.Objects /// This value is the original value specified in the beatmap, not affected by the beatmap processing. /// Use for a gameplay. /// - public float OriginalX => OriginalXBindable.Value; + public float OriginalX + { + get => OriginalXBindable.Value; + set => OriginalXBindable.Value = value; + } /// /// The effective horizontal position of the hit object between 0 and . @@ -55,7 +66,7 @@ namespace osu.Game.Rulesets.Catch.Objects /// public float EffectiveX => OriginalXBindable.Value + XOffsetBindable.Value; - public double TimePreempt = 1000; + public double TimePreempt { get; set; } = 1000; public readonly Bindable IndexInBeatmapBindable = new Bindable(); diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index 35fd58826e..3088d024d1 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading; +using Newtonsoft.Json; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -25,7 +26,10 @@ namespace osu.Game.Rulesets.Catch.Objects public int RepeatCount { get; set; } + [JsonIgnore] public double Velocity { get; private set; } + + [JsonIgnore] public double TickDistance { get; private set; } /// @@ -113,6 +117,7 @@ namespace osu.Game.Rulesets.Catch.Objects public float EndX => OriginalX + this.CurvePositionAt(1).X; + [JsonIgnore] public double Duration { get => this.SpanCount() * Path.Distance / Velocity; diff --git a/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs index 0cd3af01df..aa7cabf38b 100644 --- a/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using Newtonsoft.Json; using osu.Framework.Bindables; using osu.Game.Rulesets.Objects.Types; using osuTK.Graphics; @@ -33,6 +34,7 @@ namespace osu.Game.Rulesets.Catch.Objects /// /// The target fruit if we are to initiate a hyperdash. /// + [JsonIgnore] public CatchHitObject HyperDashTarget { get => hyperDashTarget; From 175d666906bd742d7a47df3081c5dd1c31ef1b0a Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 22 Jun 2021 17:35:30 +0900 Subject: [PATCH 0182/2442] Use getters of `OriginalX` and `XOffset` --- osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs | 4 ++-- osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index c1a491d1ce..d35d74d93d 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -33,11 +33,11 @@ namespace osu.Game.Rulesets.Catch.Edit if (hitObject is BananaShower) return; // TODO: confine in bounds - hitObject.OriginalXBindable.Value += deltaX; + hitObject.OriginalX += deltaX; // Move the nested hit objects to give an instant result before nested objects are recreated. foreach (var nested in hitObject.NestedHitObjects.OfType()) - nested.OriginalXBindable.Value += deltaX; + nested.OriginalX += deltaX; }); return true; diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index cdd8bfbe50..0b8c0e28a7 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Catch.Objects /// This value is the original value plus the offset applied by the beatmap processing. /// Use if a value not affected by the offset is desired. /// - public float EffectiveX => OriginalXBindable.Value + XOffsetBindable.Value; + public float EffectiveX => OriginalX + XOffset; public double TimePreempt { get; set; } = 1000; From 93ef783339908aab6e10b0a5dbc530a67c0fa7c1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jul 2021 16:40:23 +0900 Subject: [PATCH 0183/2442] Remove BindableList usage --- .../Skinning/RulesetSkinProvidingContainer.cs | 18 +++-- osu.Game/Skinning/SkinProvidingContainer.cs | 77 ++++++------------- 2 files changed, 37 insertions(+), 58 deletions(-) diff --git a/osu.Game/Skinning/RulesetSkinProvidingContainer.cs b/osu.Game/Skinning/RulesetSkinProvidingContainer.cs index 19efc66814..4dea1ff51e 100644 --- a/osu.Game/Skinning/RulesetSkinProvidingContainer.cs +++ b/osu.Game/Skinning/RulesetSkinProvidingContainer.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; @@ -72,31 +73,36 @@ namespace osu.Game.Skinning protected virtual void UpdateSkinSources() { - SkinSources.Clear(); + ResetSources(); + + var skinSources = new List(); foreach (var skin in parentSource.AllSources) { switch (skin) { case LegacySkin legacySkin: - SkinSources.Add(GetLegacyRulesetTransformedSkin(legacySkin)); + skinSources.Add(GetLegacyRulesetTransformedSkin(legacySkin)); break; default: - SkinSources.Add(skin); + skinSources.Add(skin); break; } } - int lastDefaultSkinIndex = SkinSources.IndexOf(SkinSources.OfType().LastOrDefault()); + int lastDefaultSkinIndex = skinSources.IndexOf(skinSources.OfType().LastOrDefault()); // Ruleset resources should be given the ability to override game-wide defaults // This is achieved by placing them before the last instance of DefaultSkin. // Note that DefaultSkin may not be present in some test scenes. if (lastDefaultSkinIndex >= 0) - SkinSources.Insert(lastDefaultSkinIndex, rulesetResourcesSkin); + skinSources.Insert(lastDefaultSkinIndex, rulesetResourcesSkin); else - SkinSources.Add(rulesetResourcesSkin); + skinSources.Add(rulesetResourcesSkin); + + foreach (var skin in skinSources) + AddSource(skin); } protected ISkin GetLegacyRulesetTransformedSkin(ISkin legacySkin) diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index c83c299723..b1ada54a26 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Collections.Specialized; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; @@ -27,13 +26,13 @@ namespace osu.Game.Skinning /// /// Skins which should be exposed by this container, in order of lookup precedence. /// - protected readonly BindableList SkinSources = new BindableList(); + protected IEnumerable SkinSources => skinSources.Keys; /// /// A dictionary mapping each from the /// to one that performs the "allow lookup" checks before proceeding with a lookup. /// - private readonly Dictionary disableableSkinSources = new Dictionary(); + private readonly Dictionary skinSources = new Dictionary(); [CanBeNull] private ISkinSource fallbackSource; @@ -60,7 +59,7 @@ namespace osu.Game.Skinning : this() { if (skin != null) - SkinSources.Add(skin); + AddSource(skin); } /// @@ -70,61 +69,35 @@ namespace osu.Game.Skinning protected SkinProvidingContainer() { RelativeSizeAxes = Axes.Both; + } - SkinSources.BindCollectionChanged(((_, args) => - { - switch (args.Action) - { - case NotifyCollectionChangedAction.Add: - foreach (var skin in args.NewItems.Cast()) - { - disableableSkinSources.Add(skin, new DisableableSkinSource(skin, this)); + public void AddSource(ISkin skin) + { + skinSources.Add(skin, new DisableableSkinSource(skin, this)); - if (skin is ISkinSource source) - source.SourceChanged += OnSourceChanged; - } + if (skin is ISkinSource source) + source.SourceChanged += OnSourceChanged; + } - break; + public void RemoveSource(ISkin skin) + { + skinSources.Remove(skin); - case NotifyCollectionChangedAction.Reset: - case NotifyCollectionChangedAction.Remove: - foreach (var skin in args.OldItems.Cast()) - { - disableableSkinSources.Remove(skin); + if (skin is ISkinSource source) + source.SourceChanged += OnSourceChanged; + } - if (skin is ISkinSource source) - source.SourceChanged -= OnSourceChanged; - } - - break; - - case NotifyCollectionChangedAction.Replace: - foreach (var skin in args.OldItems.Cast()) - { - disableableSkinSources.Remove(skin); - - if (skin is ISkinSource source) - source.SourceChanged -= OnSourceChanged; - } - - foreach (var skin in args.NewItems.Cast()) - { - disableableSkinSources.Add(skin, new DisableableSkinSource(skin, this)); - - if (skin is ISkinSource source) - source.SourceChanged += OnSourceChanged; - } - - break; - } - }), true); + public void ResetSources() + { + foreach (var skin in AllSources.ToArray()) + RemoveSource(skin); } public ISkin FindProvider(Func lookupFunction) { foreach (var skin in SkinSources) { - if (lookupFunction(disableableSkinSources[skin])) + if (lookupFunction(skinSources[skin])) return skin; } @@ -151,7 +124,7 @@ namespace osu.Game.Skinning foreach (var skin in SkinSources) { Drawable sourceDrawable; - if ((sourceDrawable = disableableSkinSources[skin]?.GetDrawableComponent(component)) != null) + if ((sourceDrawable = skinSources[skin]?.GetDrawableComponent(component)) != null) return sourceDrawable; } @@ -163,7 +136,7 @@ namespace osu.Game.Skinning foreach (var skin in SkinSources) { Texture sourceTexture; - if ((sourceTexture = disableableSkinSources[skin]?.GetTexture(componentName, wrapModeS, wrapModeT)) != null) + if ((sourceTexture = skinSources[skin]?.GetTexture(componentName, wrapModeS, wrapModeT)) != null) return sourceTexture; } @@ -175,7 +148,7 @@ namespace osu.Game.Skinning foreach (var skin in SkinSources) { ISample sourceSample; - if ((sourceSample = disableableSkinSources[skin]?.GetSample(sampleInfo)) != null) + if ((sourceSample = skinSources[skin]?.GetSample(sampleInfo)) != null) return sourceSample; } @@ -187,7 +160,7 @@ namespace osu.Game.Skinning foreach (var skin in SkinSources) { IBindable bindable; - if ((bindable = disableableSkinSources[skin]?.GetConfig(lookup)) != null) + if ((bindable = skinSources[skin]?.GetConfig(lookup)) != null) return bindable; } From 7ef7c5148f21c35426a45f4ae02d6a60755fc584 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 6 Jul 2021 16:19:31 +0900 Subject: [PATCH 0184/2442] Add `ScrollingPath` for visualization of the real path of a `JuiceStream` --- .../Blueprints/Components/ScrollingPath.cs | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Edit/Blueprints/Components/ScrollingPath.cs diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/ScrollingPath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/ScrollingPath.cs new file mode 100644 index 0000000000..337b8de92e --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/ScrollingPath.cs @@ -0,0 +1,79 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Lines; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.UI.Scrolling; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components +{ + public class ScrollingPath : CompositeDrawable + { + private readonly Path drawablePath; + + private readonly List<(double Distance, float X)> vertices = new List<(double, float)>(); + + public ScrollingPath() + { + Anchor = Anchor.BottomLeft; + + InternalChildren = new Drawable[] + { + drawablePath = new SmoothPath + { + PathRadius = 2, + Alpha = 0.5f + }, + }; + } + + public void UpdatePositionFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject hitObject) + { + X = hitObject.OriginalX; + Y = hitObjectContainer.PositionAtTime(hitObject.StartTime); + } + + public void UpdatePathFrom(ScrollingHitObjectContainer hitObjectContainer, JuiceStream hitObject) + { + double distanceToYFactor = -hitObjectContainer.LengthAtTime(hitObject.StartTime, hitObject.StartTime + 1 / hitObject.Velocity); + + computeDistanceXs(hitObject); + drawablePath.Vertices = vertices + .Select(v => new Vector2(v.X, (float)(v.Distance * distanceToYFactor))) + .ToArray(); + drawablePath.OriginPosition = drawablePath.PositionInBoundingBox(Vector2.Zero); + } + + private void computeDistanceXs(JuiceStream hitObject) + { + vertices.Clear(); + + var sliderVertices = new List(); + hitObject.Path.GetPathToProgress(sliderVertices, 0, 1); + + if (sliderVertices.Count == 0) + return; + + double distance = 0; + Vector2 lastPosition = Vector2.Zero; + + for (int repeat = 0; repeat < hitObject.RepeatCount + 1; repeat++) + { + foreach (var position in sliderVertices) + { + distance += Vector2.Distance(lastPosition, position); + lastPosition = position; + + vertices.Add((distance, position.X)); + } + + sliderVertices.Reverse(); + } + } + } +} From 0fa7716ceda7f8de41b1bf744bb5e98de3ff0d49 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 6 Jul 2021 16:46:12 +0900 Subject: [PATCH 0185/2442] Show path of juice stream in selection blueprint --- .../JuiceStreamSelectionBlueprint.cs | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs index d6b8c35a09..9f9d6a0556 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs @@ -3,7 +3,9 @@ using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Caching; using osu.Framework.Graphics.Primitives; +using osu.Game.Rulesets.Catch.Edit.Blueprints.Components; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Objects; using osuTK; @@ -17,9 +19,14 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints private float minNestedX; private float maxNestedX; + private readonly ScrollingPath scrollingPath; + + private readonly Cached pathCache = new Cached(); + public JuiceStreamSelectionBlueprint(JuiceStream hitObject) : base(hitObject) { + InternalChild = scrollingPath = new ScrollingPath(); } [BackgroundDependencyLoader] @@ -29,7 +36,25 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints computeObjectBounds(); } - private void onDefaultsApplied(HitObject _) => computeObjectBounds(); + protected override void Update() + { + base.Update(); + + if (!IsSelected) return; + + scrollingPath.UpdatePositionFrom(HitObjectContainer, HitObject); + + if (pathCache.IsValid) return; + + scrollingPath.UpdatePathFrom(HitObjectContainer, HitObject); + pathCache.Validate(); + } + + private void onDefaultsApplied(HitObject _) + { + computeObjectBounds(); + pathCache.Invalidate(); + } private void computeObjectBounds() { From 935fbe7cc627f78deba63f33f1b0057d0470eb7b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jul 2021 16:41:48 +0900 Subject: [PATCH 0186/2442] Remove double fetch/binding of parent source --- .../Skinning/RulesetSkinProvidingContainer.cs | 17 +++---- osu.Game/Skinning/SkinProvidingContainer.cs | 44 ++++++++++++------- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/osu.Game/Skinning/RulesetSkinProvidingContainer.cs b/osu.Game/Skinning/RulesetSkinProvidingContainer.cs index 4dea1ff51e..d5be5d9394 100644 --- a/osu.Game/Skinning/RulesetSkinProvidingContainer.cs +++ b/osu.Game/Skinning/RulesetSkinProvidingContainer.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; @@ -47,22 +48,19 @@ namespace osu.Game.Skinning }; } - private ISkinSource parentSource; - private ResourceStoreBackedSkin rulesetResourcesSkin; protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { - parentSource = parent.Get(); - parentSource.SourceChanged += OnSourceChanged; - if (Ruleset.CreateResourceStore() is IResourceStore resources) rulesetResourcesSkin = new ResourceStoreBackedSkin(resources, parent.Get(), parent.Get()); + var dependencies = base.CreateChildDependencies(parent); + // ensure sources are populated and ready for use before childrens' asynchronous load flow. UpdateSkinSources(); - return base.CreateChildDependencies(parent); + return dependencies; } protected override void OnSourceChanged() @@ -77,7 +75,9 @@ namespace osu.Game.Skinning var skinSources = new List(); - foreach (var skin in parentSource.AllSources) + Debug.Assert(ParentSource != null); + + foreach (var skin in ParentSource.AllSources) { switch (skin) { @@ -121,9 +121,6 @@ namespace osu.Game.Skinning { base.Dispose(isDisposing); - if (parentSource != null) - parentSource.SourceChanged -= OnSourceChanged; - rulesetResourcesSkin?.Dispose(); } } diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index b1ada54a26..a1e71502b9 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -35,7 +35,7 @@ namespace osu.Game.Skinning private readonly Dictionary skinSources = new Dictionary(); [CanBeNull] - private ISkinSource fallbackSource; + protected ISkinSource ParentSource { get; private set; } /// /// Whether falling back to parent s is allowed in this container. @@ -101,7 +101,10 @@ namespace osu.Game.Skinning return skin; } - return fallbackSource?.FindProvider(lookupFunction); + if (!AllowFallingBackToParent) + return null; + + return ParentSource?.FindProvider(lookupFunction); } public IEnumerable AllSources @@ -111,9 +114,9 @@ namespace osu.Game.Skinning foreach (var skin in SkinSources) yield return skin; - if (fallbackSource != null) + if (AllowFallingBackToParent && ParentSource != null) { - foreach (var skin in fallbackSource.AllSources) + foreach (var skin in ParentSource.AllSources) yield return skin; } } @@ -128,7 +131,10 @@ namespace osu.Game.Skinning return sourceDrawable; } - return fallbackSource?.GetDrawableComponent(component); + if (!AllowFallingBackToParent) + return null; + + return ParentSource?.GetDrawableComponent(component); } public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) @@ -140,7 +146,10 @@ namespace osu.Game.Skinning return sourceTexture; } - return fallbackSource?.GetTexture(componentName, wrapModeS, wrapModeT); + if (!AllowFallingBackToParent) + return null; + + return ParentSource?.GetTexture(componentName, wrapModeS, wrapModeT); } public ISample GetSample(ISampleInfo sampleInfo) @@ -152,7 +161,10 @@ namespace osu.Game.Skinning return sourceSample; } - return fallbackSource?.GetSample(sampleInfo); + if (!AllowFallingBackToParent) + return null; + + return ParentSource?.GetSample(sampleInfo); } public IBindable GetConfig(TLookup lookup) @@ -164,7 +176,10 @@ namespace osu.Game.Skinning return bindable; } - return fallbackSource?.GetConfig(lookup); + if (!AllowFallingBackToParent) + return null; + + return ParentSource?.GetConfig(lookup); } protected virtual void OnSourceChanged() => SourceChanged?.Invoke(); @@ -173,12 +188,9 @@ namespace osu.Game.Skinning { var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - if (AllowFallingBackToParent) - { - fallbackSource = dependencies.Get(); - if (fallbackSource != null) - fallbackSource.SourceChanged += OnSourceChanged; - } + ParentSource = dependencies.Get(); + if (ParentSource != null) + ParentSource.SourceChanged += OnSourceChanged; dependencies.CacheAs(this); @@ -192,8 +204,8 @@ namespace osu.Game.Skinning base.Dispose(isDisposing); - if (fallbackSource != null) - fallbackSource.SourceChanged -= OnSourceChanged; + if (ParentSource != null) + ParentSource.SourceChanged -= OnSourceChanged; foreach (var source in SkinSources.OfType()) source.SourceChanged -= OnSourceChanged; From ec1224218c86587eb62137a96f4cd328ff0dcd35 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jul 2021 16:57:19 +0900 Subject: [PATCH 0187/2442] Localise source changed flow for better clarity --- osu.Game/Skinning/SkinProvidingContainer.cs | 23 +++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index a1e71502b9..5beadd7178 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -76,7 +76,7 @@ namespace osu.Game.Skinning skinSources.Add(skin, new DisableableSkinSource(skin, this)); if (skin is ISkinSource source) - source.SourceChanged += OnSourceChanged; + source.SourceChanged += anySourceChanged; } public void RemoveSource(ISkin skin) @@ -84,7 +84,7 @@ namespace osu.Game.Skinning skinSources.Remove(skin); if (skin is ISkinSource source) - source.SourceChanged += OnSourceChanged; + source.SourceChanged += anySourceChanged; } public void ResetSources() @@ -182,7 +182,10 @@ namespace osu.Game.Skinning return ParentSource?.GetConfig(lookup); } - protected virtual void OnSourceChanged() => SourceChanged?.Invoke(); + /// + /// Invoked when any source has changed (either or + /// + protected virtual void OnSourceChanged() { } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { @@ -190,13 +193,21 @@ namespace osu.Game.Skinning ParentSource = dependencies.Get(); if (ParentSource != null) - ParentSource.SourceChanged += OnSourceChanged; + ParentSource.SourceChanged += anySourceChanged; dependencies.CacheAs(this); return dependencies; } + private void anySourceChanged() + { + // Expose to implementations, giving them a chance to react before notifying external consumers. + OnSourceChanged(); + + SourceChanged?.Invoke(); + } + protected override void Dispose(bool isDisposing) { // Must be done before base.Dispose() @@ -205,10 +216,10 @@ namespace osu.Game.Skinning base.Dispose(isDisposing); if (ParentSource != null) - ParentSource.SourceChanged -= OnSourceChanged; + ParentSource.SourceChanged -= anySourceChanged; foreach (var source in SkinSources.OfType()) - source.SourceChanged -= OnSourceChanged; + source.SourceChanged -= anySourceChanged; } private class DisableableSkinSource : ISkin From b4240d3ca4db7d06599524612486f125f1a01f0f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jul 2021 17:04:59 +0900 Subject: [PATCH 0188/2442] Simplify lookups to avoid a second dictionary fetch --- osu.Game/Skinning/SkinProvidingContainer.cs | 33 ++++++++------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index 5beadd7178..a6debcbf66 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -24,13 +24,7 @@ namespace osu.Game.Skinning public event Action SourceChanged; /// - /// Skins which should be exposed by this container, in order of lookup precedence. - /// - protected IEnumerable SkinSources => skinSources.Keys; - - /// - /// A dictionary mapping each from the - /// to one that performs the "allow lookup" checks before proceeding with a lookup. + /// A dictionary mapping each source to a wrapper which handles lookup allowances. /// private readonly Dictionary skinSources = new Dictionary(); @@ -64,7 +58,6 @@ namespace osu.Game.Skinning /// /// Constructs a new with no sources. - /// Implementations can add or change sources through the list. /// protected SkinProvidingContainer() { @@ -95,9 +88,9 @@ namespace osu.Game.Skinning public ISkin FindProvider(Func lookupFunction) { - foreach (var skin in SkinSources) + foreach (var (skin, lookupWrapper) in skinSources) { - if (lookupFunction(skinSources[skin])) + if (lookupFunction(lookupWrapper)) return skin; } @@ -111,7 +104,7 @@ namespace osu.Game.Skinning { get { - foreach (var skin in SkinSources) + foreach (var skin in skinSources.Keys) yield return skin; if (AllowFallingBackToParent && ParentSource != null) @@ -124,10 +117,10 @@ namespace osu.Game.Skinning public Drawable GetDrawableComponent(ISkinComponent component) { - foreach (var skin in SkinSources) + foreach (var (_, lookupWrapper) in skinSources) { Drawable sourceDrawable; - if ((sourceDrawable = skinSources[skin]?.GetDrawableComponent(component)) != null) + if ((sourceDrawable = lookupWrapper.GetDrawableComponent(component)) != null) return sourceDrawable; } @@ -139,10 +132,10 @@ namespace osu.Game.Skinning public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) { - foreach (var skin in SkinSources) + foreach (var (_, lookupWrapper) in skinSources) { Texture sourceTexture; - if ((sourceTexture = skinSources[skin]?.GetTexture(componentName, wrapModeS, wrapModeT)) != null) + if ((sourceTexture = lookupWrapper.GetTexture(componentName, wrapModeS, wrapModeT)) != null) return sourceTexture; } @@ -154,10 +147,10 @@ namespace osu.Game.Skinning public ISample GetSample(ISampleInfo sampleInfo) { - foreach (var skin in SkinSources) + foreach (var (_, lookupWrapper) in skinSources) { ISample sourceSample; - if ((sourceSample = skinSources[skin]?.GetSample(sampleInfo)) != null) + if ((sourceSample = lookupWrapper.GetSample(sampleInfo)) != null) return sourceSample; } @@ -169,10 +162,10 @@ namespace osu.Game.Skinning public IBindable GetConfig(TLookup lookup) { - foreach (var skin in SkinSources) + foreach (var (_, lookupWrapper) in skinSources) { IBindable bindable; - if ((bindable = skinSources[skin]?.GetConfig(lookup)) != null) + if ((bindable = lookupWrapper.GetConfig(lookup)) != null) return bindable; } @@ -218,7 +211,7 @@ namespace osu.Game.Skinning if (ParentSource != null) ParentSource.SourceChanged -= anySourceChanged; - foreach (var source in SkinSources.OfType()) + foreach (var source in skinSources.Keys.OfType()) source.SourceChanged -= anySourceChanged; } From 1232925f93113846049c7be94a09b8cd14a76299 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jul 2021 17:06:00 +0900 Subject: [PATCH 0189/2442] Make source manipulation methods `protected` --- osu.Game/Skinning/SkinProvidingContainer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index a6debcbf66..730659eacd 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -64,7 +64,7 @@ namespace osu.Game.Skinning RelativeSizeAxes = Axes.Both; } - public void AddSource(ISkin skin) + protected void AddSource(ISkin skin) { skinSources.Add(skin, new DisableableSkinSource(skin, this)); @@ -72,7 +72,7 @@ namespace osu.Game.Skinning source.SourceChanged += anySourceChanged; } - public void RemoveSource(ISkin skin) + protected void RemoveSource(ISkin skin) { skinSources.Remove(skin); @@ -80,7 +80,7 @@ namespace osu.Game.Skinning source.SourceChanged += anySourceChanged; } - public void ResetSources() + protected void ResetSources() { foreach (var skin in AllSources.ToArray()) RemoveSource(skin); From 032c285edefbefa8aa2874bca356f9b295123caf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jul 2021 17:07:25 +0900 Subject: [PATCH 0190/2442] Move private downwards --- osu.Game/Skinning/SkinProvidingContainer.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index 730659eacd..6b6fdce480 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -23,11 +23,6 @@ namespace osu.Game.Skinning { public event Action SourceChanged; - /// - /// A dictionary mapping each source to a wrapper which handles lookup allowances. - /// - private readonly Dictionary skinSources = new Dictionary(); - [CanBeNull] protected ISkinSource ParentSource { get; private set; } @@ -46,6 +41,11 @@ namespace osu.Game.Skinning protected virtual bool AllowColourLookup => true; + /// + /// A dictionary mapping each source to a wrapper which handles lookup allowances. + /// + private readonly Dictionary skinSources = new Dictionary(); + /// /// Constructs a new initialised with a single skin source. /// From 7833a1b09a32b7e7061f44935da8e8107d981489 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 6 Jul 2021 17:15:27 +0900 Subject: [PATCH 0191/2442] Allow `FruitOutline` to be used for nested hit objects --- .../Edit/Blueprints/Components/FruitOutline.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/FruitOutline.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/FruitOutline.cs index 8769acc382..345b59bdcd 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/FruitOutline.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/FruitOutline.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -18,7 +19,6 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components { Anchor = Anchor.BottomLeft; Origin = Anchor.Centre; - Size = new Vector2(2 * CatchHitObject.OBJECT_RADIUS); InternalChild = new BorderPiece(); } @@ -28,10 +28,10 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components Colour = osuColour.Yellow; } - public void UpdateFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject hitObject) + public void UpdateFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject hitObject, [CanBeNull] CatchHitObject parent = null) { - X = hitObject.EffectiveX; - Y = hitObjectContainer.PositionAtTime(hitObject.StartTime); + X = hitObject.EffectiveX - (parent?.OriginalX ?? 0); + Y = hitObjectContainer.PositionAtTime(hitObject.StartTime, parent?.StartTime ?? hitObjectContainer.Time.Current); Scale = new Vector2(hitObject.Scale); } } From 2ba300393495ab865ffe622a86772903ba809d9d Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 6 Jul 2021 17:15:51 +0900 Subject: [PATCH 0192/2442] Add nested fruit outlines to juice stream selection blueprint --- .../Components/NestedOutlineContainer.cs | 53 +++++++++++++++++++ .../JuiceStreamSelectionBlueprint.cs | 12 ++++- 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Catch/Edit/Blueprints/Components/NestedOutlineContainer.cs diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/NestedOutlineContainer.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/NestedOutlineContainer.cs new file mode 100644 index 0000000000..9e1743de98 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/NestedOutlineContainer.cs @@ -0,0 +1,53 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.UI.Scrolling; + +namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components +{ + public class NestedOutlineContainer : CompositeDrawable + { + private readonly Container nestedOutlines; + + private readonly List nestedHitObjects = new List(); + + public NestedOutlineContainer() + { + Anchor = Anchor.BottomLeft; + + InternalChild = nestedOutlines = new Container(); + } + + public void UpdatePositionFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject parentHitObject) + { + X = parentHitObject.OriginalX; + Y = hitObjectContainer.PositionAtTime(parentHitObject.StartTime); + } + + public void UpdateNestedObjectsFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject parentHitObject) + { + nestedHitObjects.Clear(); + nestedHitObjects.AddRange(parentHitObject.NestedHitObjects + .OfType() + .Where(h => !(h is TinyDroplet))); + + while (nestedHitObjects.Count < nestedOutlines.Count) + nestedOutlines.Remove(nestedOutlines[^1]); + + while (nestedOutlines.Count < nestedHitObjects.Count) + nestedOutlines.Add(new FruitOutline()); + + for (int i = 0; i < nestedHitObjects.Count; i++) + { + var hitObject = nestedHitObjects[i]; + nestedOutlines[i].UpdateFrom(hitObjectContainer, hitObject, parentHitObject); + nestedOutlines[i].Scale *= hitObject is Droplet ? 0.5f : 1; + } + } + } +} diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs index 9f9d6a0556..bf7b962e0a 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs @@ -4,6 +4,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Caching; +using osu.Framework.Graphics; using osu.Framework.Graphics.Primitives; using osu.Game.Rulesets.Catch.Edit.Blueprints.Components; using osu.Game.Rulesets.Catch.Objects; @@ -21,12 +22,18 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints private readonly ScrollingPath scrollingPath; + private readonly NestedOutlineContainer nestedOutlineContainer; + private readonly Cached pathCache = new Cached(); public JuiceStreamSelectionBlueprint(JuiceStream hitObject) : base(hitObject) { - InternalChild = scrollingPath = new ScrollingPath(); + InternalChildren = new Drawable[] + { + scrollingPath = new ScrollingPath(), + nestedOutlineContainer = new NestedOutlineContainer() + }; } [BackgroundDependencyLoader] @@ -43,10 +50,13 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints if (!IsSelected) return; scrollingPath.UpdatePositionFrom(HitObjectContainer, HitObject); + nestedOutlineContainer.UpdatePositionFrom(HitObjectContainer, HitObject); if (pathCache.IsValid) return; scrollingPath.UpdatePathFrom(HitObjectContainer, HitObject); + nestedOutlineContainer.UpdateNestedObjectsFrom(HitObjectContainer, HitObject); + pathCache.Validate(); } From cd4885e4502feb0f28b3ea32c26fa13640e4c224 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jul 2021 17:18:45 +0900 Subject: [PATCH 0193/2442] Add xmldoc and remove any question of how the intitial flow is being run --- .../Skinning/RulesetSkinProvidingContainer.cs | 28 ++++++------------- osu.Game/Skinning/SkinProvidingContainer.cs | 21 ++++++++++++-- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/osu.Game/Skinning/RulesetSkinProvidingContainer.cs b/osu.Game/Skinning/RulesetSkinProvidingContainer.cs index d5be5d9394..f5a7788359 100644 --- a/osu.Game/Skinning/RulesetSkinProvidingContainer.cs +++ b/osu.Game/Skinning/RulesetSkinProvidingContainer.cs @@ -55,25 +55,15 @@ namespace osu.Game.Skinning if (Ruleset.CreateResourceStore() is IResourceStore resources) rulesetResourcesSkin = new ResourceStoreBackedSkin(resources, parent.Get(), parent.Get()); - var dependencies = base.CreateChildDependencies(parent); - - // ensure sources are populated and ready for use before childrens' asynchronous load flow. - UpdateSkinSources(); - - return dependencies; + return base.CreateChildDependencies(parent); } protected override void OnSourceChanged() - { - UpdateSkinSources(); - base.OnSourceChanged(); - } - - protected virtual void UpdateSkinSources() { ResetSources(); - var skinSources = new List(); + // Populate a local list first so we can adjust the returned order as we go. + var sources = new List(); Debug.Assert(ParentSource != null); @@ -82,26 +72,26 @@ namespace osu.Game.Skinning switch (skin) { case LegacySkin legacySkin: - skinSources.Add(GetLegacyRulesetTransformedSkin(legacySkin)); + sources.Add(GetLegacyRulesetTransformedSkin(legacySkin)); break; default: - skinSources.Add(skin); + sources.Add(skin); break; } } - int lastDefaultSkinIndex = skinSources.IndexOf(skinSources.OfType().LastOrDefault()); + int lastDefaultSkinIndex = sources.IndexOf(sources.OfType().LastOrDefault()); // Ruleset resources should be given the ability to override game-wide defaults // This is achieved by placing them before the last instance of DefaultSkin. // Note that DefaultSkin may not be present in some test scenes. if (lastDefaultSkinIndex >= 0) - skinSources.Insert(lastDefaultSkinIndex, rulesetResourcesSkin); + sources.Insert(lastDefaultSkinIndex, rulesetResourcesSkin); else - skinSources.Add(rulesetResourcesSkin); + sources.Add(rulesetResourcesSkin); - foreach (var skin in skinSources) + foreach (var skin in sources) AddSource(skin); } diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index 6b6fdce480..6033776979 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -64,6 +64,10 @@ namespace osu.Game.Skinning RelativeSizeAxes = Axes.Both; } + /// + /// Add a new skin to this provider. Will be added to the end of the lookup order precedence. + /// + /// The skin to add. protected void AddSource(ISkin skin) { skinSources.Add(skin, new DisableableSkinSource(skin, this)); @@ -72,14 +76,22 @@ namespace osu.Game.Skinning source.SourceChanged += anySourceChanged; } + /// + /// Remove a skin from this provider. + /// + /// The skin to remove. protected void RemoveSource(ISkin skin) { - skinSources.Remove(skin); + if (!skinSources.Remove(skin)) + return; if (skin is ISkinSource source) - source.SourceChanged += anySourceChanged; + source.SourceChanged -= anySourceChanged; } + /// + /// Clears all skin sources. + /// protected void ResetSources() { foreach (var skin in AllSources.ToArray()) @@ -176,7 +188,8 @@ namespace osu.Game.Skinning } /// - /// Invoked when any source has changed (either or + /// Invoked when any source has changed (either or a source registered via ). + /// This is also invoked once initially during to ensure sources are ready for children consumption. /// protected virtual void OnSourceChanged() { } @@ -190,6 +203,8 @@ namespace osu.Game.Skinning dependencies.CacheAs(this); + anySourceChanged(); + return dependencies; } From d75d67577a1668749ffcb08600ce1440fc503256 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jul 2021 17:37:34 +0900 Subject: [PATCH 0194/2442] Fix regressed tests --- .../Visual/Gameplay/TestSceneSkinnableDrawable.cs | 2 +- osu.Game/Skinning/BeatmapSkinProvidingContainer.cs | 6 +++--- osu.Game/Skinning/SkinProvidingContainer.cs | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs index 02b1959dab..3e8ba69e01 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs @@ -168,7 +168,7 @@ namespace osu.Game.Tests.Visual.Gameplay public void Disable() { allow = false; - OnSourceChanged(); + TriggerSourceChanged(); } public SwitchableSkinProvidingContainer(ISkin skin) diff --git a/osu.Game/Skinning/BeatmapSkinProvidingContainer.cs b/osu.Game/Skinning/BeatmapSkinProvidingContainer.cs index f12f44e347..57c08a903f 100644 --- a/osu.Game/Skinning/BeatmapSkinProvidingContainer.cs +++ b/osu.Game/Skinning/BeatmapSkinProvidingContainer.cs @@ -83,9 +83,9 @@ namespace osu.Game.Skinning [BackgroundDependencyLoader] private void load() { - beatmapSkins.BindValueChanged(_ => OnSourceChanged()); - beatmapColours.BindValueChanged(_ => OnSourceChanged()); - beatmapHitsounds.BindValueChanged(_ => OnSourceChanged()); + beatmapSkins.BindValueChanged(_ => TriggerSourceChanged()); + beatmapColours.BindValueChanged(_ => TriggerSourceChanged()); + beatmapHitsounds.BindValueChanged(_ => TriggerSourceChanged()); } } } diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index 6033776979..d2f38b58a4 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -73,7 +73,7 @@ namespace osu.Game.Skinning skinSources.Add(skin, new DisableableSkinSource(skin, this)); if (skin is ISkinSource source) - source.SourceChanged += anySourceChanged; + source.SourceChanged += TriggerSourceChanged; } /// @@ -86,7 +86,7 @@ namespace osu.Game.Skinning return; if (skin is ISkinSource source) - source.SourceChanged -= anySourceChanged; + source.SourceChanged -= TriggerSourceChanged; } /// @@ -199,16 +199,16 @@ namespace osu.Game.Skinning ParentSource = dependencies.Get(); if (ParentSource != null) - ParentSource.SourceChanged += anySourceChanged; + ParentSource.SourceChanged += TriggerSourceChanged; dependencies.CacheAs(this); - anySourceChanged(); + TriggerSourceChanged(); return dependencies; } - private void anySourceChanged() + protected void TriggerSourceChanged() { // Expose to implementations, giving them a chance to react before notifying external consumers. OnSourceChanged(); @@ -224,10 +224,10 @@ namespace osu.Game.Skinning base.Dispose(isDisposing); if (ParentSource != null) - ParentSource.SourceChanged -= anySourceChanged; + ParentSource.SourceChanged -= TriggerSourceChanged; foreach (var source in skinSources.Keys.OfType()) - source.SourceChanged -= anySourceChanged; + source.SourceChanged -= TriggerSourceChanged; } private class DisableableSkinSource : ISkin From 8b12ec9586019b42b01bd98e861ca0f5f2e31a3c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 6 Jul 2021 17:52:27 +0900 Subject: [PATCH 0195/2442] Fix intermittent ready button test failures --- .../Multiplayer/TestSceneMultiplayerReadyButton.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs index 0e036e8868..c36e1200e6 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs @@ -113,10 +113,10 @@ namespace osu.Game.Tests.Visual.Multiplayer }); addClickButtonStep(); - AddAssert("user is ready", () => Client.Room?.Users[0].State == MultiplayerUserState.Ready); + AddUntilStep("user is ready", () => Client.Room?.Users[0].State == MultiplayerUserState.Ready); addClickButtonStep(); - AddAssert("user is idle", () => Client.Room?.Users[0].State == MultiplayerUserState.Idle); + AddUntilStep("user is idle", () => Client.Room?.Users[0].State == MultiplayerUserState.Idle); } [TestCase(true)] @@ -132,7 +132,7 @@ namespace osu.Game.Tests.Visual.Multiplayer }); addClickButtonStep(); - AddAssert("user is ready", () => Client.Room?.Users[0].State == MultiplayerUserState.Ready); + AddUntilStep("user is ready", () => Client.Room?.Users[0].State == MultiplayerUserState.Ready); verifyGameplayStartFlow(); } @@ -206,8 +206,9 @@ namespace osu.Game.Tests.Visual.Multiplayer private void verifyGameplayStartFlow() { + AddUntilStep("user is ready", () => Client.Room?.Users[0].State == MultiplayerUserState.Ready); addClickButtonStep(); - AddAssert("user waiting for load", () => Client.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad); + AddUntilStep("user waiting for load", () => Client.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad); AddAssert("ready button disabled", () => !button.ChildrenOfType().Single().Enabled.Value); AddStep("transitioned to gameplay", () => readyClickOperation.Dispose()); From b209868d96d277740c8718bb49d3e1110c1d92b5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 6 Jul 2021 17:57:24 +0900 Subject: [PATCH 0196/2442] Fix another potential failure --- .../Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs index c36e1200e6..820b403a10 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs @@ -219,7 +219,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Client.ChangeUserState(Client.Room?.Users[0].UserID ?? 0, MultiplayerUserState.FinishedPlay); }); - AddAssert("ready button enabled", () => button.ChildrenOfType().Single().Enabled.Value); + AddUntilStep("ready button enabled", () => button.ChildrenOfType().Single().Enabled.Value); } } } From 32ef2405c4783f5a2db54f80bdfa5f24782a383c Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Tue, 6 Jul 2021 11:30:56 +0200 Subject: [PATCH 0197/2442] Use null instead of -1 --- .../SelectionCycleFillFlowContainer.cs | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs b/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs index 0e7b2bcc05..19a423c25b 100644 --- a/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs +++ b/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs @@ -13,43 +13,51 @@ namespace osu.Game.Graphics.Containers /// public class SelectionCycleFillFlowContainer : FillFlowContainer where T : Drawable, ISelectable { - private int selectedIndex = -1; + private int? selectedIndex; - private void setSelected(int value) + private void setSelected(int? value) { if (selectedIndex == value) return; // Deselect the previously-selected button - if (selectedIndex != -1) - this[selectedIndex].Selected = false; + if (selectedIndex.HasValue) + this[selectedIndex.Value].Selected = false; selectedIndex = value; // Select the newly-selected button - if (selectedIndex != -1) - this[selectedIndex].Selected = true; + if (selectedIndex.HasValue) + this[selectedIndex.Value].Selected = true; } public void SelectNext() { - if (selectedIndex == -1 || selectedIndex == Count - 1) + if (!selectedIndex.HasValue || selectedIndex == Count - 1) setSelected(0); else - setSelected(selectedIndex + 1); + setSelected(selectedIndex.Value + 1); } public void SelectPrevious() { - if (selectedIndex == -1 || selectedIndex == 0) + if (!selectedIndex.HasValue || selectedIndex == 0) setSelected(Count - 1); else - setSelected(selectedIndex - 1); + setSelected(selectedIndex.Value - 1); } - public void Deselect() => setSelected(-1); - public void Select(T item) => setSelected(IndexOf(item)); + public void Deselect() => setSelected(null); + public void Select(T item) + { + var newIndex = IndexOf(item); - public T Selected => (selectedIndex >= 0 && selectedIndex < Count) ? this[selectedIndex] : null; + if (newIndex < 0) + setSelected(null); + else + setSelected(IndexOf(item)); + } + + public T Selected => (selectedIndex >= 0 && selectedIndex < Count) ? this[selectedIndex.Value] : null; } } From c5a0672277514f27cd2fb84b99669cf9c80f91c4 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Tue, 6 Jul 2021 12:07:25 +0200 Subject: [PATCH 0198/2442] Use IStateful instead of ISelected --- .../SelectionCycleFillFlowContainer.cs | 7 ++-- .../Graphics/UserInterface/DialogButton.cs | 35 ++++++++++++------- .../Graphics/UserInterface/ISelectable.cs | 10 ------ osu.Game/Overlays/Volume/VolumeMeter.cs | 32 +++++++++++------ osu.Game/Overlays/VolumeOverlay.cs | 7 ++-- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 8 ++--- 6 files changed, 56 insertions(+), 43 deletions(-) delete mode 100644 osu.Game/Graphics/UserInterface/ISelectable.cs diff --git a/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs b/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs index 19a423c25b..5849fbbe30 100644 --- a/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs +++ b/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; @@ -11,7 +12,7 @@ namespace osu.Game.Graphics.Containers /// A FillFlowContainer that provides functionality to cycle selection between children /// The selection wraps around when overflowing past the first or last child. /// - public class SelectionCycleFillFlowContainer : FillFlowContainer where T : Drawable, ISelectable + public class SelectionCycleFillFlowContainer : FillFlowContainer where T : Drawable, IStateful { private int? selectedIndex; @@ -22,13 +23,13 @@ namespace osu.Game.Graphics.Containers // Deselect the previously-selected button if (selectedIndex.HasValue) - this[selectedIndex.Value].Selected = false; + this[selectedIndex.Value].State = SelectionState.NotSelected; selectedIndex = value; // Select the newly-selected button if (selectedIndex.HasValue) - this[selectedIndex.Value].Selected = true; + this[selectedIndex.Value].State = SelectionState.Selected; } public void SelectNext() diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs index 718a5171c2..f9fbec2b48 100644 --- a/osu.Game/Graphics/UserInterface/DialogButton.cs +++ b/osu.Game/Graphics/UserInterface/DialogButton.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; +using osu.Framework; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -19,7 +21,7 @@ using osuTK.Graphics; namespace osu.Game.Graphics.UserInterface { - public class DialogButton : OsuClickableContainer, ISelectable + public class DialogButton : OsuClickableContainer, IStateful { private const float idle_width = 0.8f; private const float hover_width = 0.9f; @@ -27,12 +29,21 @@ namespace osu.Game.Graphics.UserInterface private const float hover_duration = 500; private const float click_duration = 200; - public readonly BindableBool SelectedBindable = new BindableBool(); + public event Action StateChanged; - public bool Selected + private SelectionState state; + + public SelectionState State { - get => SelectedBindable.Value; - set => SelectedBindable.Value = value; + get => state; + set + { + if (state == value) + return; + + state = value; + StateChanged?.Invoke(value); + } } private readonly Container backgroundContainer; @@ -159,7 +170,7 @@ namespace osu.Game.Graphics.UserInterface updateGlow(); - SelectedBindable.ValueChanged += selectionChanged; + StateChanged += selectionChanged; } private Color4 buttonColour; @@ -227,7 +238,7 @@ namespace osu.Game.Graphics.UserInterface .OnComplete(_ => { clickAnimating = false; - SelectedBindable.TriggerChange(); + StateChanged?.Invoke(State); }); return base.OnClick(e); @@ -241,7 +252,7 @@ namespace osu.Game.Graphics.UserInterface protected override void OnMouseUp(MouseUpEvent e) { - if (Selected) + if (State == SelectionState.Selected) colourContainer.ResizeWidthTo(hover_width, click_duration, Easing.In); base.OnMouseUp(e); } @@ -249,7 +260,7 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnHover(HoverEvent e) { base.OnHover(e); - Selected = true; + State = SelectionState.Selected; return true; } @@ -257,15 +268,15 @@ namespace osu.Game.Graphics.UserInterface protected override void OnHoverLost(HoverLostEvent e) { base.OnHoverLost(e); - Selected = false; + State = SelectionState.NotSelected; } - private void selectionChanged(ValueChangedEvent args) + private void selectionChanged(SelectionState newState) { if (clickAnimating) return; - if (args.NewValue) + if (newState == SelectionState.Selected) { spriteText.TransformSpacingTo(hoverSpacing, hover_duration, Easing.OutElastic); colourContainer.ResizeWidthTo(hover_width, hover_duration, Easing.OutElastic); diff --git a/osu.Game/Graphics/UserInterface/ISelectable.cs b/osu.Game/Graphics/UserInterface/ISelectable.cs deleted file mode 100644 index 49c3d725c8..0000000000 --- a/osu.Game/Graphics/UserInterface/ISelectable.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Game.Graphics.UserInterface -{ - public interface ISelectable - { - bool Selected { get; set; } - } -} diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs index 3e9849a077..a7c4fb6e7d 100644 --- a/osu.Game/Overlays/Volume/VolumeMeter.cs +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -3,6 +3,7 @@ using System; using System.Globalization; +using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -26,7 +27,7 @@ using osuTK.Graphics; namespace osu.Game.Overlays.Volume { - public class VolumeMeter : Container, IKeyBindingHandler, ISelectable + public class VolumeMeter : Container, IKeyBindingHandler, IStateful { private CircularProgress volumeCircle; private CircularProgress volumeCircleGlow; @@ -44,12 +45,21 @@ namespace osu.Game.Overlays.Volume private Sample sample; private double sampleLastPlaybackTime; - public BindableBool SelectedBindable = new BindableBool(); + public event Action StateChanged; - public bool Selected + private SelectionState state; + + public SelectionState State { - get => SelectedBindable.Value; - set => SelectedBindable.Value = value; + get => state; + set + { + if (state == value) + return; + + state = value; + StateChanged?.Invoke(value); + } } public VolumeMeter(string name, float circleSize, Color4 meterColour) @@ -220,7 +230,7 @@ namespace osu.Game.Overlays.Volume bgProgress.Current.Value = 0.75f; - SelectedBindable.ValueChanged += selectionChanged; + StateChanged += stateChanged; } private int? displayVolumeInt; @@ -341,7 +351,7 @@ namespace osu.Game.Overlays.Volume protected override bool OnHover(HoverEvent e) { - Selected = true; + State = SelectionState.Selected; return false; } @@ -349,9 +359,9 @@ namespace osu.Game.Overlays.Volume { } - private void selectionChanged(ValueChangedEvent selected) + private void stateChanged(SelectionState newState) { - if (selected.NewValue) + if (newState == SelectionState.Selected) { this.ScaleTo(1.04f, transition_length, Easing.OutExpo); focusGlowContainer.FadeIn(transition_length, Easing.OutExpo); @@ -371,12 +381,12 @@ namespace osu.Game.Overlays.Volume switch (action) { case GlobalAction.SelectPrevious: - Selected = true; + State = SelectionState.Selected; adjust(1, false); return true; case GlobalAction.SelectNext: - Selected = true; + State = SelectionState.Selected; adjust(-1, false); return true; } diff --git a/osu.Game/Overlays/VolumeOverlay.cs b/osu.Game/Overlays/VolumeOverlay.cs index 0c56e48cd4..56aa62d6e4 100644 --- a/osu.Game/Overlays/VolumeOverlay.cs +++ b/osu.Game/Overlays/VolumeOverlay.cs @@ -13,6 +13,7 @@ using osu.Framework.Input.Events; using osu.Framework.Threading; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Overlays.Volume; using osuTK; @@ -93,7 +94,7 @@ namespace osu.Game.Overlays foreach (var volumeMeter in volumeMeters) { volumeMeter.Bindable.ValueChanged += _ => Show(); - volumeMeter.SelectedBindable.ValueChanged += selected => volumeMeterSelectionChanged(volumeMeter, selected.NewValue); + volumeMeter.StateChanged += selected => volumeMeterSelectionChanged(volumeMeter, selected); } muteButton.Current.ValueChanged += _ => Show(); @@ -140,9 +141,9 @@ namespace osu.Game.Overlays return false; } - private void volumeMeterSelectionChanged(VolumeMeter meter, bool isSelected) + private void volumeMeterSelectionChanged(VolumeMeter meter, SelectionState state) { - if (!isSelected) + if (state == SelectionState.NotSelected) volumeMeters.Deselect(); else volumeMeters.Select(meter); diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index 876f33d814..32f7a93cef 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -184,7 +184,7 @@ namespace osu.Game.Screens.Play } }; - button.SelectedBindable.ValueChanged += selected => buttonSelectionChanged(button, selected.NewValue); + button.StateChanged += selected => buttonSelectionChanged(button, selected); InternalButtons.Add(button); } @@ -217,9 +217,9 @@ namespace osu.Game.Screens.Play { } - private void buttonSelectionChanged(DialogButton button, bool isSelected) + private void buttonSelectionChanged(DialogButton button, SelectionState state) { - if (!isSelected) + if (state == SelectionState.NotSelected) InternalButtons.Deselect(); else InternalButtons.Select(button); @@ -263,7 +263,7 @@ namespace osu.Game.Screens.Play protected override bool OnMouseMove(MouseMoveEvent e) { - Selected = true; + State = SelectionState.Selected; return base.OnMouseMove(e); } } From 7b21d1ecf9ac251e697d65e626044ed3d5244e2c Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 6 Jul 2021 19:50:32 +0900 Subject: [PATCH 0199/2442] Fix juice stream outline disappears away when start position is outside the screen. --- .../Components/NestedOutlineContainer.cs | 20 +++++++++---------- .../Blueprints/Components/ScrollingPath.cs | 4 ++++ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/NestedOutlineContainer.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/NestedOutlineContainer.cs index 9e1743de98..48d90e8b24 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/NestedOutlineContainer.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/NestedOutlineContainer.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.UI.Scrolling; @@ -12,15 +13,11 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components { public class NestedOutlineContainer : CompositeDrawable { - private readonly Container nestedOutlines; - private readonly List nestedHitObjects = new List(); public NestedOutlineContainer() { Anchor = Anchor.BottomLeft; - - InternalChild = nestedOutlines = new Container(); } public void UpdatePositionFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject parentHitObject) @@ -36,18 +33,21 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components .OfType() .Where(h => !(h is TinyDroplet))); - while (nestedHitObjects.Count < nestedOutlines.Count) - nestedOutlines.Remove(nestedOutlines[^1]); + while (nestedHitObjects.Count < InternalChildren.Count) + RemoveInternal(InternalChildren[^1]); - while (nestedOutlines.Count < nestedHitObjects.Count) - nestedOutlines.Add(new FruitOutline()); + while (InternalChildren.Count < nestedHitObjects.Count) + AddInternal(new FruitOutline()); for (int i = 0; i < nestedHitObjects.Count; i++) { var hitObject = nestedHitObjects[i]; - nestedOutlines[i].UpdateFrom(hitObjectContainer, hitObject, parentHitObject); - nestedOutlines[i].Scale *= hitObject is Droplet ? 0.5f : 1; + var outline = (FruitOutline)InternalChildren[i]; + outline.UpdateFrom(hitObjectContainer, hitObject, parentHitObject); + outline.Scale *= hitObject is Droplet ? 0.5f : 1; } } + + protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false; } } diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/ScrollingPath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/ScrollingPath.cs index 337b8de92e..96111beda4 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/ScrollingPath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/ScrollingPath.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Lines; +using osu.Framework.Graphics.Primitives; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.UI.Scrolling; using osuTK; @@ -75,5 +76,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components sliderVertices.Reverse(); } } + + // Because this has 0x0 size, the contents are otherwise masked away if the start position is outside the screen. + protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false; } } From 06d2c6f0a1916dd30c673004713d4515ab2f4944 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jul 2021 19:51:57 +0900 Subject: [PATCH 0200/2442] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 9067ae1cd9..cfc28ffb21 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 626be76a84..646d21dfee 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index e339e49187..f36f7881cd 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From 07d54d261a0359b7a2df15e685ee0874b2cbb4bf Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Tue, 6 Jul 2021 13:24:18 +0200 Subject: [PATCH 0201/2442] Let selection container handle manual selection changes --- .../SelectionCycleFillFlowContainer.cs | 31 +++++++++++++++++++ osu.Game/Overlays/VolumeOverlay.cs | 12 ------- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 12 ------- 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs b/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs index 5849fbbe30..b48a697903 100644 --- a/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs +++ b/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.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; +using System.Collections.Generic; using osu.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -60,5 +62,34 @@ namespace osu.Game.Graphics.Containers } public T Selected => (selectedIndex >= 0 && selectedIndex < Count) ? this[selectedIndex.Value] : null; + + private readonly Dictionary> handlerMap = new Dictionary>(); + + public override void Add(T drawable) + { + // This event is used to update selection state when modified within the drawable itself. + // It is added to a dictionary so that we can unsubscribe if the drawable is removed from this container + drawable.StateChanged += handlerMap[drawable] = state => selectionChanged(drawable, state); + + base.Add(drawable); + } + + public override bool Remove(T drawable) + { + if (!base.Remove(drawable)) + return false; + + drawable.StateChanged -= handlerMap[drawable]; + handlerMap.Remove(drawable); + return true; + } + + private void selectionChanged(T drawable, SelectionState state) + { + if (state == SelectionState.NotSelected) + Deselect(); + else + Select(drawable); + } } } diff --git a/osu.Game/Overlays/VolumeOverlay.cs b/osu.Game/Overlays/VolumeOverlay.cs index 56aa62d6e4..a96949e96f 100644 --- a/osu.Game/Overlays/VolumeOverlay.cs +++ b/osu.Game/Overlays/VolumeOverlay.cs @@ -13,7 +13,6 @@ using osu.Framework.Input.Events; using osu.Framework.Threading; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Overlays.Volume; using osuTK; @@ -92,10 +91,7 @@ namespace osu.Game.Overlays base.LoadComplete(); foreach (var volumeMeter in volumeMeters) - { volumeMeter.Bindable.ValueChanged += _ => Show(); - volumeMeter.StateChanged += selected => volumeMeterSelectionChanged(volumeMeter, selected); - } muteButton.Current.ValueChanged += _ => Show(); } @@ -141,14 +137,6 @@ namespace osu.Game.Overlays return false; } - private void volumeMeterSelectionChanged(VolumeMeter meter, SelectionState state) - { - if (state == SelectionState.NotSelected) - volumeMeters.Deselect(); - else - volumeMeters.Select(meter); - } - private ScheduledDelegate popOutDelegate; public override void Show() diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index 32f7a93cef..c71dd2ce65 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using Humanizer; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; @@ -184,8 +183,6 @@ namespace osu.Game.Screens.Play } }; - button.StateChanged += selected => buttonSelectionChanged(button, selected); - InternalButtons.Add(button); } @@ -216,15 +213,6 @@ namespace osu.Game.Screens.Play public void OnReleased(GlobalAction action) { } - - private void buttonSelectionChanged(DialogButton button, SelectionState state) - { - if (state == SelectionState.NotSelected) - InternalButtons.Deselect(); - else - InternalButtons.Select(button); - } - private void updateRetryCount() { // "You've retried 1,065 times in this session" From 6bc00208251091726aa3285cc6c6863b886942ed Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 6 Jul 2021 20:28:49 +0900 Subject: [PATCH 0202/2442] Fix intermittent spectate button test failures --- .../Multiplayer/TestSceneMultiplayerSpectateButton.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs index 4966dfbe50..3d08d5da9e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs @@ -122,10 +122,10 @@ namespace osu.Game.Tests.Visual.Multiplayer public void TestToggleWhenIdle(MultiplayerUserState initialState) { addClickSpectateButtonStep(); - AddAssert("user is spectating", () => Client.Room?.Users[0].State == MultiplayerUserState.Spectating); + AddUntilStep("user is spectating", () => Client.Room?.Users[0].State == MultiplayerUserState.Spectating); addClickSpectateButtonStep(); - AddAssert("user is idle", () => Client.Room?.Users[0].State == MultiplayerUserState.Idle); + AddUntilStep("user is idle", () => Client.Room?.Users[0].State == MultiplayerUserState.Idle); } [TestCase(MultiplayerRoomState.Closed)] @@ -174,9 +174,9 @@ namespace osu.Game.Tests.Visual.Multiplayer }); private void assertSpectateButtonEnablement(bool shouldBeEnabled) - => AddAssert($"spectate button {(shouldBeEnabled ? "is" : "is not")} enabled", () => spectateButton.ChildrenOfType().Single().Enabled.Value == shouldBeEnabled); + => AddUntilStep($"spectate button {(shouldBeEnabled ? "is" : "is not")} enabled", () => spectateButton.ChildrenOfType().Single().Enabled.Value == shouldBeEnabled); private void assertReadyButtonEnablement(bool shouldBeEnabled) - => AddAssert($"ready button {(shouldBeEnabled ? "is" : "is not")} enabled", () => readyButton.ChildrenOfType().Single().Enabled.Value == shouldBeEnabled); + => AddUntilStep($"ready button {(shouldBeEnabled ? "is" : "is not")} enabled", () => readyButton.ChildrenOfType().Single().Enabled.Value == shouldBeEnabled); } } From ffe18ebe516e20ea367e836908d51d435d455f65 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Tue, 6 Jul 2021 14:11:46 +0200 Subject: [PATCH 0203/2442] Resolve build errors --- .../Gameplay/TestSceneGameplayMenuOverlay.cs | 34 +++++++++---------- .../SelectionCycleFillFlowContainer.cs | 12 +++++-- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 1 + 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs index 0aafbda951..d6a50fc346 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs @@ -87,7 +87,7 @@ namespace osu.Game.Tests.Visual.Gameplay showOverlay(); AddStep("Up arrow", () => InputManager.Key(Key.Up)); - AddAssert("Last button selected", () => pauseOverlay.Buttons.Last().Selected); + AddAssert("Last button selected", () => pauseOverlay.Buttons.Last().State == SelectionState.Selected); } /// @@ -99,7 +99,7 @@ namespace osu.Game.Tests.Visual.Gameplay showOverlay(); AddStep("Down arrow", () => InputManager.Key(Key.Down)); - AddAssert("First button selected", () => getButton(0).Selected); + AddAssert("First button selected", () => getButton(0).State == SelectionState.Selected); } /// @@ -111,11 +111,11 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Show overlay", () => failOverlay.Show()); AddStep("Up arrow", () => InputManager.Key(Key.Up)); - AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected); + AddAssert("Last button selected", () => failOverlay.Buttons.Last().State == SelectionState.Selected); AddStep("Up arrow", () => InputManager.Key(Key.Up)); - AddAssert("First button selected", () => failOverlay.Buttons.First().Selected); + AddAssert("First button selected", () => failOverlay.Buttons.First().State == SelectionState.Selected); AddStep("Up arrow", () => InputManager.Key(Key.Up)); - AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected); + AddAssert("Last button selected", () => failOverlay.Buttons.Last().State == SelectionState.Selected); } /// @@ -127,11 +127,11 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Show overlay", () => failOverlay.Show()); AddStep("Down arrow", () => InputManager.Key(Key.Down)); - AddAssert("First button selected", () => failOverlay.Buttons.First().Selected); + AddAssert("First button selected", () => failOverlay.Buttons.First().State == SelectionState.Selected); AddStep("Down arrow", () => InputManager.Key(Key.Down)); - AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected); + AddAssert("Last button selected", () => failOverlay.Buttons.Last().State == SelectionState.Selected); AddStep("Down arrow", () => InputManager.Key(Key.Down)); - AddAssert("First button selected", () => failOverlay.Buttons.First().Selected); + AddAssert("First button selected", () => failOverlay.Buttons.First().State == SelectionState.Selected); } /// @@ -145,7 +145,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Hover first button", () => InputManager.MoveMouseTo(failOverlay.Buttons.First())); AddStep("Hide overlay", () => failOverlay.Hide()); - AddAssert("Overlay state is reset", () => !failOverlay.Buttons.Any(b => b.Selected)); + AddAssert("Overlay state is reset", () => !failOverlay.Buttons.Any(b => b.State == SelectionState.Selected)); } /// @@ -162,11 +162,11 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Hide overlay", () => pauseOverlay.Hide()); showOverlay(); - AddAssert("First button not selected", () => !getButton(0).Selected); + AddAssert("First button not selected", () => getButton(0).State == SelectionState.NotSelected); AddStep("Move slightly", () => InputManager.MoveMouseTo(InputManager.CurrentState.Mouse.Position + new Vector2(1))); - AddAssert("First button selected", () => getButton(0).Selected); + AddAssert("First button selected", () => getButton(0).State == SelectionState.Selected); } /// @@ -179,8 +179,8 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Down arrow", () => InputManager.Key(Key.Down)); AddStep("Hover second button", () => InputManager.MoveMouseTo(getButton(1))); - AddAssert("First button not selected", () => !getButton(0).Selected); - AddAssert("Second button selected", () => getButton(1).Selected); + AddAssert("First button not selected", () => getButton(0).State == SelectionState.NotSelected); + AddAssert("Second button selected", () => getButton(1).State == SelectionState.Selected); } /// @@ -196,8 +196,8 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Hover second button", () => InputManager.MoveMouseTo(getButton(1))); AddStep("Up arrow", () => InputManager.Key(Key.Up)); - AddAssert("Second button not selected", () => !getButton(1).Selected); - AddAssert("First button selected", () => getButton(0).Selected); + AddAssert("Second button not selected", () => getButton(1).State == SelectionState.NotSelected); + AddAssert("First button selected", () => getButton(0).State == SelectionState.Selected); } /// @@ -211,7 +211,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Hover second button", () => InputManager.MoveMouseTo(getButton(1))); AddStep("Unhover second button", () => InputManager.MoveMouseTo(Vector2.Zero)); AddStep("Down arrow", () => InputManager.Key(Key.Down)); - AddAssert("First button selected", () => getButton(0).Selected); // Initial state condition + AddAssert("First button selected", () => getButton(0).State == SelectionState.Selected); // Initial state condition } /// @@ -282,7 +282,7 @@ namespace osu.Game.Tests.Visual.Gameplay showOverlay(); AddAssert("No button selected", - () => pauseOverlay.Buttons.All(button => !button.Selected)); + () => pauseOverlay.Buttons.All(button => button.State == SelectionState.NotSelected)); } private void showOverlay() => AddStep("Show overlay", () => pauseOverlay.Show()); diff --git a/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs b/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs index b48a697903..3dbe1f8f47 100644 --- a/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs +++ b/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs @@ -69,7 +69,9 @@ namespace osu.Game.Graphics.Containers { // This event is used to update selection state when modified within the drawable itself. // It is added to a dictionary so that we can unsubscribe if the drawable is removed from this container - drawable.StateChanged += handlerMap[drawable] = state => selectionChanged(drawable, state); + handlerMap[drawable] = state => selectionChanged(drawable, state); + + drawable.StateChanged += handlerMap[drawable]; base.Add(drawable); } @@ -79,8 +81,12 @@ namespace osu.Game.Graphics.Containers if (!base.Remove(drawable)) return false; - drawable.StateChanged -= handlerMap[drawable]; - handlerMap.Remove(drawable); + if (handlerMap.TryGetValue(drawable, out var action)) + { + drawable.StateChanged -= action; + handlerMap.Remove(drawable); + } + return true; } diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index c71dd2ce65..6fcc70e5f2 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -213,6 +213,7 @@ namespace osu.Game.Screens.Play public void OnReleased(GlobalAction action) { } + private void updateRetryCount() { // "You've retried 1,065 times in this session" From 4b1b5a88fe0dbef52f1297a06f584468fba3ad54 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Tue, 6 Jul 2021 14:39:53 +0200 Subject: [PATCH 0204/2442] Add null check to supress quality errors --- .../SelectionCycleFillFlowContainer.cs | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs b/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs index 3dbe1f8f47..4f20c0039b 100644 --- a/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs +++ b/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs @@ -18,6 +18,8 @@ namespace osu.Game.Graphics.Containers { private int? selectedIndex; + public T Selected => (selectedIndex >= 0 && selectedIndex < Count) ? this[selectedIndex.Value] : null; + private void setSelected(int? value) { if (selectedIndex == value) @@ -51,6 +53,7 @@ namespace osu.Game.Graphics.Containers } public void Deselect() => setSelected(null); + public void Select(T item) { var newIndex = IndexOf(item); @@ -61,19 +64,20 @@ namespace osu.Game.Graphics.Containers setSelected(IndexOf(item)); } - public T Selected => (selectedIndex >= 0 && selectedIndex < Count) ? this[selectedIndex.Value] : null; - private readonly Dictionary> handlerMap = new Dictionary>(); public override void Add(T drawable) { - // This event is used to update selection state when modified within the drawable itself. - // It is added to a dictionary so that we can unsubscribe if the drawable is removed from this container - handlerMap[drawable] = state => selectionChanged(drawable, state); - - drawable.StateChanged += handlerMap[drawable]; - base.Add(drawable); + + if (drawable != null) + { + // This event is used to update selection state when modified within the drawable itself. + // It is added to a dictionary so that we can unsubscribe if the drawable is removed from this container + handlerMap[drawable] = state => selectionChanged(drawable, state); + + drawable.StateChanged += handlerMap[drawable]; + } } public override bool Remove(T drawable) @@ -81,7 +85,7 @@ namespace osu.Game.Graphics.Containers if (!base.Remove(drawable)) return false; - if (handlerMap.TryGetValue(drawable, out var action)) + if (drawable != null && handlerMap.TryGetValue(drawable, out var action)) { drawable.StateChanged -= action; handlerMap.Remove(drawable); From 4451598bcfe8f5b62b3f19c0b108290bfd448d09 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Tue, 6 Jul 2021 15:17:19 +0200 Subject: [PATCH 0205/2442] Fix remaining quality complaints --- osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs | 2 +- osu.Game/Graphics/UserInterface/DialogButton.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs index d6a50fc346..ed40a83831 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs @@ -145,7 +145,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Hover first button", () => InputManager.MoveMouseTo(failOverlay.Buttons.First())); AddStep("Hide overlay", () => failOverlay.Hide()); - AddAssert("Overlay state is reset", () => !failOverlay.Buttons.Any(b => b.State == SelectionState.Selected)); + AddAssert("Overlay state is reset", () => failOverlay.Buttons.All(b => b.State == SelectionState.NotSelected)); } /// diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs index f9fbec2b48..2d75dad828 100644 --- a/osu.Game/Graphics/UserInterface/DialogButton.cs +++ b/osu.Game/Graphics/UserInterface/DialogButton.cs @@ -3,7 +3,6 @@ using System; using osu.Framework; -using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; From 255f7b7b532a25276272331a4e224c9389a6931a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 6 Jul 2021 16:32:02 +0300 Subject: [PATCH 0206/2442] Add failing test scene --- .../Skins/TestSceneSkinProvidingContainer.cs | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 osu.Game.Tests/Skins/TestSceneSkinProvidingContainer.cs diff --git a/osu.Game.Tests/Skins/TestSceneSkinProvidingContainer.cs b/osu.Game.Tests/Skins/TestSceneSkinProvidingContainer.cs new file mode 100644 index 0000000000..cfc4ccd208 --- /dev/null +++ b/osu.Game.Tests/Skins/TestSceneSkinProvidingContainer.cs @@ -0,0 +1,92 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.OpenGL.Textures; +using osu.Framework.Graphics.Textures; +using osu.Game.Audio; +using osu.Game.Skinning; +using osu.Game.Tests.Visual; + +namespace osu.Game.Tests.Skins +{ + public class TestSceneSkinProvidingContainer : OsuTestScene + { + /// + /// Ensures that the first inserted skin after resetting (via source change) + /// is always prioritised over others when providing the same resource. + /// + [Test] + public void TestPriorityPreservation() + { + TestSkinProvidingContainer provider = null; + TestSkin mostPrioritisedSource = null; + + AddStep("setup sources", () => + { + var sources = new List(); + for (int i = 0; i < 10; i++) + sources.Add(new TestSkin()); + + mostPrioritisedSource = sources.First(); + + Child = provider = new TestSkinProvidingContainer(sources); + }); + + AddAssert("texture provided by expected skin", () => + { + return provider.FindProvider(s => s.GetTexture(TestSkin.TEXTURE_NAME) != null) == mostPrioritisedSource; + }); + + AddStep("trigger source change", () => provider.TriggerSourceChanged()); + + AddAssert("texture still provided by expected skin", () => + { + return provider.FindProvider(s => s.GetTexture(TestSkin.TEXTURE_NAME) != null) == mostPrioritisedSource; + }); + } + + private class TestSkinProvidingContainer : SkinProvidingContainer + { + private readonly IEnumerable sources; + + public TestSkinProvidingContainer(IEnumerable sources) + { + this.sources = sources; + } + + public new void TriggerSourceChanged() => base.TriggerSourceChanged(); + + protected override void OnSourceChanged() + { + ResetSources(); + sources.ForEach(AddSource); + } + } + + private class TestSkin : ISkin + { + public const string TEXTURE_NAME = "virtual-texture"; + + public Drawable GetDrawableComponent(ISkinComponent component) => throw new System.NotImplementedException(); + + public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) + { + if (componentName == TEXTURE_NAME) + return Texture.WhitePixel; + + return null; + } + + public ISample GetSample(ISampleInfo sampleInfo) => throw new System.NotImplementedException(); + + public IBindable GetConfig(TLookup lookup) => throw new System.NotImplementedException(); + } + } +} From 523546d8b636239a6e6213c0104439eefc9ecbb7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Jul 2021 22:51:56 +0900 Subject: [PATCH 0207/2442] Use List to guarantee lookup order --- osu.Game/Skinning/SkinProvidingContainer.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index d2f38b58a4..a628e97578 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -44,7 +44,7 @@ namespace osu.Game.Skinning /// /// A dictionary mapping each source to a wrapper which handles lookup allowances. /// - private readonly Dictionary skinSources = new Dictionary(); + private readonly List<(ISkin skin, DisableableSkinSource wrapped)> skinSources = new List<(ISkin, DisableableSkinSource)>(); /// /// Constructs a new initialised with a single skin source. @@ -70,7 +70,7 @@ namespace osu.Game.Skinning /// The skin to add. protected void AddSource(ISkin skin) { - skinSources.Add(skin, new DisableableSkinSource(skin, this)); + skinSources.Add((skin, new DisableableSkinSource(skin, this))); if (skin is ISkinSource source) source.SourceChanged += TriggerSourceChanged; @@ -82,7 +82,7 @@ namespace osu.Game.Skinning /// The skin to remove. protected void RemoveSource(ISkin skin) { - if (!skinSources.Remove(skin)) + if (skinSources.RemoveAll(s => s.skin == skin) == 0) return; if (skin is ISkinSource source) @@ -116,8 +116,8 @@ namespace osu.Game.Skinning { get { - foreach (var skin in skinSources.Keys) - yield return skin; + foreach (var i in skinSources) + yield return i.skin; if (AllowFallingBackToParent && ParentSource != null) { @@ -226,8 +226,11 @@ namespace osu.Game.Skinning if (ParentSource != null) ParentSource.SourceChanged -= TriggerSourceChanged; - foreach (var source in skinSources.Keys.OfType()) - source.SourceChanged -= TriggerSourceChanged; + foreach (var i in skinSources) + { + if (i.skin is ISkinSource source) + source.SourceChanged -= TriggerSourceChanged; + } } private class DisableableSkinSource : ISkin From f45418dde7b50ee63bd7c12d686cde40022c0617 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 6 Jul 2021 00:15:17 +0200 Subject: [PATCH 0208/2442] Replace game-side directory/file selector with framework extensions --- .../Settings/TestSceneDirectorySelector.cs | 2 +- .../Visual/Settings/TestSceneFileSelector.cs | 4 +- .../Screens/Setup/StablePathSelectScreen.cs | 4 +- .../UserInterfaceV2/DirectorySelector.cs | 297 ------------------ .../Graphics/UserInterfaceV2/FileSelector.cs | 94 ------ .../UserInterfaceV2/OsuDirectorySelector.cs | 140 +++++++++ .../UserInterfaceV2/OsuFileSelector.cs | 90 ++++++ .../Maintenance/DirectorySelectScreen.cs | 4 +- .../Edit/Setup/FileChooserLabelledTextBox.cs | 4 +- osu.Game/Screens/Import/FileImportScreen.cs | 4 +- 10 files changed, 241 insertions(+), 402 deletions(-) delete mode 100644 osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs delete mode 100644 osu.Game/Graphics/UserInterfaceV2/FileSelector.cs create mode 100644 osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelector.cs create mode 100644 osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs diff --git a/osu.Game.Tests/Visual/Settings/TestSceneDirectorySelector.cs b/osu.Game.Tests/Visual/Settings/TestSceneDirectorySelector.cs index 082d85603e..227bce0c60 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneDirectorySelector.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneDirectorySelector.cs @@ -12,7 +12,7 @@ namespace osu.Game.Tests.Visual.Settings [BackgroundDependencyLoader] private void load() { - Add(new DirectorySelector { RelativeSizeAxes = Axes.Both }); + Add(new OsuDirectorySelector { RelativeSizeAxes = Axes.Both }); } } } diff --git a/osu.Game.Tests/Visual/Settings/TestSceneFileSelector.cs b/osu.Game.Tests/Visual/Settings/TestSceneFileSelector.cs index 311e4c3362..84a0fc6e4c 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneFileSelector.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneFileSelector.cs @@ -12,13 +12,13 @@ namespace osu.Game.Tests.Visual.Settings [Test] public void TestAllFiles() { - AddStep("create", () => Child = new FileSelector { RelativeSizeAxes = Axes.Both }); + AddStep("create", () => Child = new OsuFileSelector { RelativeSizeAxes = Axes.Both }); } [Test] public void TestJpgFilesOnly() { - AddStep("create", () => Child = new FileSelector(validFileExtensions: new[] { ".jpg" }) { RelativeSizeAxes = Axes.Both }); + AddStep("create", () => Child = new OsuFileSelector(validFileExtensions: new[] { ".jpg" }) { RelativeSizeAxes = Axes.Both }); } } } diff --git a/osu.Game.Tournament/Screens/Setup/StablePathSelectScreen.cs b/osu.Game.Tournament/Screens/Setup/StablePathSelectScreen.cs index 03f79b644f..3752d9d3be 100644 --- a/osu.Game.Tournament/Screens/Setup/StablePathSelectScreen.cs +++ b/osu.Game.Tournament/Screens/Setup/StablePathSelectScreen.cs @@ -30,7 +30,7 @@ namespace osu.Game.Tournament.Screens.Setup [Resolved] private MatchIPCInfo ipc { get; set; } - private DirectorySelector directorySelector; + private OsuDirectorySelector directorySelector; private DialogOverlay overlay; [BackgroundDependencyLoader(true)] @@ -79,7 +79,7 @@ namespace osu.Game.Tournament.Screens.Setup }, new Drawable[] { - directorySelector = new DirectorySelector(initialPath) + directorySelector = new OsuDirectorySelector(initialPath) { RelativeSizeAxes = Axes.Both, } diff --git a/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs b/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs deleted file mode 100644 index a1cd074619..0000000000 --- a/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs +++ /dev/null @@ -1,297 +0,0 @@ -// 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.IO; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.Events; -using osu.Framework.Platform; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osuTK; -using osuTK.Graphics; - -namespace osu.Game.Graphics.UserInterfaceV2 -{ - public class DirectorySelector : CompositeDrawable - { - private FillFlowContainer directoryFlow; - - [Resolved] - private GameHost host { get; set; } - - [Cached] - public readonly Bindable CurrentPath = new Bindable(); - - public DirectorySelector(string initialPath = null) - { - CurrentPath.Value = new DirectoryInfo(initialPath ?? Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)); - } - - [BackgroundDependencyLoader] - private void load() - { - Padding = new MarginPadding(10); - - InternalChild = new GridContainer - { - RelativeSizeAxes = Axes.Both, - RowDimensions = new[] - { - new Dimension(GridSizeMode.Absolute, 50), - new Dimension(), - }, - Content = new[] - { - new Drawable[] - { - new CurrentDirectoryDisplay - { - RelativeSizeAxes = Axes.Both, - }, - }, - new Drawable[] - { - new OsuScrollContainer - { - RelativeSizeAxes = Axes.Both, - Child = directoryFlow = new FillFlowContainer - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Direction = FillDirection.Vertical, - Spacing = new Vector2(2), - } - } - } - } - }; - - CurrentPath.BindValueChanged(updateDisplay, true); - } - - private void updateDisplay(ValueChangedEvent directory) - { - directoryFlow.Clear(); - - try - { - if (directory.NewValue == null) - { - var drives = DriveInfo.GetDrives(); - - foreach (var drive in drives) - directoryFlow.Add(new DirectoryPiece(drive.RootDirectory)); - } - else - { - directoryFlow.Add(new ParentDirectoryPiece(CurrentPath.Value.Parent)); - - directoryFlow.AddRange(GetEntriesForPath(CurrentPath.Value)); - } - } - catch (Exception) - { - CurrentPath.Value = directory.OldValue; - this.FlashColour(Color4.Red, 300); - } - } - - protected virtual IEnumerable GetEntriesForPath(DirectoryInfo path) - { - foreach (var dir in path.GetDirectories().OrderBy(d => d.Name)) - { - if ((dir.Attributes & FileAttributes.Hidden) == 0) - yield return new DirectoryPiece(dir); - } - } - - private class CurrentDirectoryDisplay : CompositeDrawable - { - [Resolved] - private Bindable currentDirectory { get; set; } - - private FillFlowContainer flow; - - [BackgroundDependencyLoader] - private void load() - { - InternalChildren = new Drawable[] - { - flow = new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.X, - Spacing = new Vector2(5), - Height = DisplayPiece.HEIGHT, - Direction = FillDirection.Horizontal, - }, - }; - - currentDirectory.BindValueChanged(updateDisplay, true); - } - - private void updateDisplay(ValueChangedEvent dir) - { - flow.Clear(); - - List pathPieces = new List(); - - DirectoryInfo ptr = dir.NewValue; - - while (ptr != null) - { - pathPieces.Insert(0, new CurrentDisplayPiece(ptr)); - ptr = ptr.Parent; - } - - flow.ChildrenEnumerable = new Drawable[] - { - new OsuSpriteText { Text = "Current Directory: ", Font = OsuFont.Default.With(size: DisplayPiece.HEIGHT), }, - new ComputerPiece(), - }.Concat(pathPieces); - } - - private class ComputerPiece : CurrentDisplayPiece - { - protected override IconUsage? Icon => null; - - public ComputerPiece() - : base(null, "Computer") - { - } - } - - private class CurrentDisplayPiece : DirectoryPiece - { - public CurrentDisplayPiece(DirectoryInfo directory, string displayName = null) - : base(directory, displayName) - { - } - - [BackgroundDependencyLoader] - private void load() - { - Flow.Add(new SpriteIcon - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Icon = FontAwesome.Solid.ChevronRight, - Size = new Vector2(FONT_SIZE / 2) - }); - } - - protected override IconUsage? Icon => Directory.Name.Contains(Path.DirectorySeparatorChar) ? base.Icon : null; - } - } - - private class ParentDirectoryPiece : DirectoryPiece - { - protected override IconUsage? Icon => FontAwesome.Solid.Folder; - - public ParentDirectoryPiece(DirectoryInfo directory) - : base(directory, "..") - { - } - } - - protected class DirectoryPiece : DisplayPiece - { - protected readonly DirectoryInfo Directory; - - [Resolved] - private Bindable currentDirectory { get; set; } - - public DirectoryPiece(DirectoryInfo directory, string displayName = null) - : base(displayName) - { - Directory = directory; - } - - protected override bool OnClick(ClickEvent e) - { - currentDirectory.Value = Directory; - return true; - } - - protected override string FallbackName => Directory.Name; - - protected override IconUsage? Icon => Directory.Name.Contains(Path.DirectorySeparatorChar) - ? FontAwesome.Solid.Database - : FontAwesome.Regular.Folder; - } - - protected abstract class DisplayPiece : CompositeDrawable - { - public const float HEIGHT = 20; - - protected const float FONT_SIZE = 16; - - private readonly string displayName; - - protected FillFlowContainer Flow; - - protected DisplayPiece(string displayName = null) - { - this.displayName = displayName; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - AutoSizeAxes = Axes.Both; - - Masking = true; - CornerRadius = 5; - - InternalChildren = new Drawable[] - { - new Box - { - Colour = colours.GreySeafoamDarker, - RelativeSizeAxes = Axes.Both, - }, - Flow = new FillFlowContainer - { - AutoSizeAxes = Axes.X, - Height = 20, - Margin = new MarginPadding { Vertical = 2, Horizontal = 5 }, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(5), - } - }; - - if (Icon.HasValue) - { - Flow.Add(new SpriteIcon - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Icon = Icon.Value, - Size = new Vector2(FONT_SIZE) - }); - } - - Flow.Add(new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Text = displayName ?? FallbackName, - Font = OsuFont.Default.With(size: FONT_SIZE) - }); - } - - protected abstract string FallbackName { get; } - - protected abstract IconUsage? Icon { get; } - } - } -} diff --git a/osu.Game/Graphics/UserInterfaceV2/FileSelector.cs b/osu.Game/Graphics/UserInterfaceV2/FileSelector.cs deleted file mode 100644 index e10b8f7033..0000000000 --- a/osu.Game/Graphics/UserInterfaceV2/FileSelector.cs +++ /dev/null @@ -1,94 +0,0 @@ -// 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.IO; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.Events; - -namespace osu.Game.Graphics.UserInterfaceV2 -{ - public class FileSelector : DirectorySelector - { - private readonly string[] validFileExtensions; - - [Cached] - public readonly Bindable CurrentFile = new Bindable(); - - public FileSelector(string initialPath = null, string[] validFileExtensions = null) - : base(initialPath) - { - this.validFileExtensions = validFileExtensions ?? Array.Empty(); - } - - protected override IEnumerable GetEntriesForPath(DirectoryInfo path) - { - foreach (var dir in base.GetEntriesForPath(path)) - yield return dir; - - IEnumerable files = path.GetFiles(); - - if (validFileExtensions.Length > 0) - files = files.Where(f => validFileExtensions.Contains(f.Extension)); - - foreach (var file in files.OrderBy(d => d.Name)) - { - if ((file.Attributes & FileAttributes.Hidden) == 0) - yield return new FilePiece(file); - } - } - - protected class FilePiece : DisplayPiece - { - private readonly FileInfo file; - - [Resolved] - private Bindable currentFile { get; set; } - - public FilePiece(FileInfo file) - { - this.file = file; - } - - protected override bool OnClick(ClickEvent e) - { - currentFile.Value = file; - return true; - } - - protected override string FallbackName => file.Name; - - protected override IconUsage? Icon - { - get - { - switch (file.Extension) - { - case ".ogg": - case ".mp3": - case ".wav": - return FontAwesome.Regular.FileAudio; - - case ".jpg": - case ".jpeg": - case ".png": - return FontAwesome.Regular.FileImage; - - case ".mp4": - case ".avi": - case ".mov": - case ".flv": - return FontAwesome.Regular.FileVideo; - - default: - return FontAwesome.Regular.File; - } - } - } - } - } -} diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelector.cs b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelector.cs new file mode 100644 index 0000000000..9bc66f6c9f --- /dev/null +++ b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelector.cs @@ -0,0 +1,140 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.IO; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osuTK; + +namespace osu.Game.Graphics.UserInterfaceV2 +{ + public class OsuDirectorySelector : DirectorySelector + { + public const float ITEM_HEIGHT = 20; + + public OsuDirectorySelector(string initialPath = null) + : base(initialPath) + { + } + + [BackgroundDependencyLoader] + private void load() + { + Padding = new MarginPadding(10); + } + + protected override ScrollContainer CreateScrollContainer() => new OsuScrollContainer(); + + protected override DirectorySelectorBreadcrumbDisplay CreateBreadcrumb() => new OsuDirectorySelectorBreadcrumbDisplay(); + + protected override DirectorySelectorDirectory CreateParentDirectoryItem(DirectoryInfo directory) => new OsuDirectorySelectorParentDirectory(directory); + + protected override DirectorySelectorDirectory CreateDirectoryItem(DirectoryInfo directory, string displayName = null) => new OsuDirectorySelectorDirectory(directory, displayName); + + protected override void NotifySelectionError() => this.FlashColour(Colour4.Red, 300); + + internal class OsuDirectorySelectorBreadcrumbDisplay : DirectorySelectorBreadcrumbDisplay + { + protected override DirectorySelectorDirectory CreateRootDirectoryItem() => new OsuBreadcrumbDisplayComputer(); + protected override DirectorySelectorDirectory CreateDirectoryItem(DirectoryInfo directory, string displayName = null) => new OsuBreadcrumbDisplayDirectory(directory, displayName); + + [BackgroundDependencyLoader] + private void load() + { + Height = 50; + } + + private class OsuBreadcrumbDisplayComputer : OsuBreadcrumbDisplayDirectory + { + protected override IconUsage? Icon => null; + + public OsuBreadcrumbDisplayComputer() + : base(null, "Computer") + { + } + } + + private class OsuBreadcrumbDisplayDirectory : OsuDirectorySelectorDirectory + { + public OsuBreadcrumbDisplayDirectory(DirectoryInfo directory, string displayName = null) + : base(directory, displayName) + { + } + + [BackgroundDependencyLoader] + private void load() + { + Flow.Add(new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Icon = FontAwesome.Solid.ChevronRight, + Size = new Vector2(FONT_SIZE / 2) + }); + } + + protected override IconUsage? Icon => Directory.Name.Contains(Path.DirectorySeparatorChar) ? base.Icon : null; + } + } + + internal class OsuDirectorySelectorParentDirectory : OsuDirectorySelectorDirectory + { + protected override IconUsage? Icon => FontAwesome.Solid.Folder; + + public OsuDirectorySelectorParentDirectory(DirectoryInfo directory) + : base(directory, "..") + { + } + } + + internal class OsuDirectorySelectorDirectory : DirectorySelectorDirectory + { + public OsuDirectorySelectorDirectory(DirectoryInfo directory, string displayName = null) + : base(directory, displayName) + { + } + + [BackgroundDependencyLoader] + private void load() + { + Flow.AutoSizeAxes = Axes.X; + Flow.Height = ITEM_HEIGHT; + + AddInternal(new OsuDirectorySelectorItemBackground + { + Depth = 1 + }); + } + + protected override SpriteText CreateSpriteText() => new OsuSpriteText(); + + protected override IconUsage? Icon => Directory.Name.Contains(Path.DirectorySeparatorChar) + ? FontAwesome.Solid.Database + : FontAwesome.Regular.Folder; + } + + internal class OsuDirectorySelectorItemBackground : CompositeDrawable + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + RelativeSizeAxes = Axes.Both; + + Masking = true; + CornerRadius = 5; + + InternalChild = new Box + { + Colour = colours.GreySeafoamDarker, + RelativeSizeAxes = Axes.Both, + }; + } + } + } +} diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs b/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs new file mode 100644 index 0000000000..c50178100e --- /dev/null +++ b/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs @@ -0,0 +1,90 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.IO; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Graphics.UserInterfaceV2 +{ + public class OsuFileSelector : FileSelector + { + public OsuFileSelector(string initialPath = null, string[] validFileExtensions = null) + : base(initialPath, validFileExtensions) + { + } + + [BackgroundDependencyLoader] + private void load() + { + Padding = new MarginPadding(10); + } + + protected override ScrollContainer CreateScrollContainer() => new OsuScrollContainer(); + + protected override DirectorySelectorBreadcrumbDisplay CreateBreadcrumb() => new OsuDirectorySelector.OsuDirectorySelectorBreadcrumbDisplay(); + + protected override DirectorySelectorDirectory CreateParentDirectoryItem(DirectoryInfo directory) => new OsuDirectorySelector.OsuDirectorySelectorParentDirectory(directory); + + protected override DirectorySelectorDirectory CreateDirectoryItem(DirectoryInfo directory, string displayName = null) => new OsuDirectorySelector.OsuDirectorySelectorDirectory(directory, displayName); + + protected override DirectoryListingFile CreateFileItem(FileInfo file) => new OsuDirectoryListingFile(file); + + protected override void NotifySelectionError() => this.FlashColour(Colour4.Red, 300); + + protected class OsuDirectoryListingFile : DirectoryListingFile + { + public OsuDirectoryListingFile(FileInfo file) + : base(file) + { + } + + [BackgroundDependencyLoader] + private void load() + { + Flow.AutoSizeAxes = Axes.X; + Flow.Height = OsuDirectorySelector.ITEM_HEIGHT; + + AddInternal(new OsuDirectorySelector.OsuDirectorySelectorItemBackground + { + Depth = 1 + }); + } + + protected override IconUsage? Icon + { + get + { + switch (File.Extension) + { + case @".ogg": + case @".mp3": + case @".wav": + return FontAwesome.Regular.FileAudio; + + case @".jpg": + case @".jpeg": + case @".png": + return FontAwesome.Regular.FileImage; + + case @".mp4": + case @".avi": + case @".mov": + case @".flv": + return FontAwesome.Regular.FileVideo; + + default: + return FontAwesome.Regular.File; + } + } + } + + protected override SpriteText CreateSpriteText() => new OsuSpriteText(); + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs index 349a112477..5392ba5d93 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { private TriangleButton selectionButton; - private DirectorySelector directorySelector; + private OsuDirectorySelector directorySelector; /// /// Text to display in the header to inform the user of what they are selecting. @@ -91,7 +91,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance }, new Drawable[] { - directorySelector = new DirectorySelector + directorySelector = new OsuDirectorySelector { RelativeSizeAxes = Axes.Both, } diff --git a/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs b/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs index a33a70af65..69c27702f8 100644 --- a/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs +++ b/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs @@ -56,9 +56,9 @@ namespace osu.Game.Screens.Edit.Setup public void DisplayFileChooser() { - FileSelector fileSelector; + OsuFileSelector fileSelector; - Target.Child = fileSelector = new FileSelector(currentFile.Value?.DirectoryName, handledExtensions) + Target.Child = fileSelector = new OsuFileSelector(currentFile.Value?.DirectoryName, handledExtensions) { RelativeSizeAxes = Axes.X, Height = 400, diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index ee8ef6926d..7e1d55b3e2 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Import { public override bool HideOverlaysOnEnter => true; - private FileSelector fileSelector; + private OsuFileSelector fileSelector; private Container contentContainer; private TextFlowContainer currentFileText; @@ -57,7 +57,7 @@ namespace osu.Game.Screens.Import Colour = colours.GreySeafoamDark, RelativeSizeAxes = Axes.Both, }, - fileSelector = new FileSelector(validFileExtensions: game.HandledExtensions.ToArray()) + fileSelector = new OsuFileSelector(validFileExtensions: game.HandledExtensions.ToArray()) { RelativeSizeAxes = Axes.Both, Width = 0.65f From e94e283ee4083c82652302f04817346b39b085d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 6 Jul 2021 00:20:58 +0200 Subject: [PATCH 0209/2442] Move shared inner classes to separate files --- .../UserInterfaceV2/OsuDirectorySelector.cs | 102 ------------------ .../OsuDirectorySelectorBreadcrumbDisplay.cs | 64 +++++++++++ .../OsuDirectorySelectorDirectory.cs | 58 ++++++++++ .../OsuDirectorySelectorParentDirectory.cs | 18 ++++ .../UserInterfaceV2/OsuFileSelector.cs | 8 +- 5 files changed, 144 insertions(+), 106 deletions(-) create mode 100644 osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorBreadcrumbDisplay.cs create mode 100644 osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs create mode 100644 osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorParentDirectory.cs diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelector.cs b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelector.cs index 9bc66f6c9f..1ce4d97fdf 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelector.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelector.cs @@ -5,12 +5,8 @@ using System.IO; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osuTK; namespace osu.Game.Graphics.UserInterfaceV2 { @@ -38,103 +34,5 @@ namespace osu.Game.Graphics.UserInterfaceV2 protected override DirectorySelectorDirectory CreateDirectoryItem(DirectoryInfo directory, string displayName = null) => new OsuDirectorySelectorDirectory(directory, displayName); protected override void NotifySelectionError() => this.FlashColour(Colour4.Red, 300); - - internal class OsuDirectorySelectorBreadcrumbDisplay : DirectorySelectorBreadcrumbDisplay - { - protected override DirectorySelectorDirectory CreateRootDirectoryItem() => new OsuBreadcrumbDisplayComputer(); - protected override DirectorySelectorDirectory CreateDirectoryItem(DirectoryInfo directory, string displayName = null) => new OsuBreadcrumbDisplayDirectory(directory, displayName); - - [BackgroundDependencyLoader] - private void load() - { - Height = 50; - } - - private class OsuBreadcrumbDisplayComputer : OsuBreadcrumbDisplayDirectory - { - protected override IconUsage? Icon => null; - - public OsuBreadcrumbDisplayComputer() - : base(null, "Computer") - { - } - } - - private class OsuBreadcrumbDisplayDirectory : OsuDirectorySelectorDirectory - { - public OsuBreadcrumbDisplayDirectory(DirectoryInfo directory, string displayName = null) - : base(directory, displayName) - { - } - - [BackgroundDependencyLoader] - private void load() - { - Flow.Add(new SpriteIcon - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Icon = FontAwesome.Solid.ChevronRight, - Size = new Vector2(FONT_SIZE / 2) - }); - } - - protected override IconUsage? Icon => Directory.Name.Contains(Path.DirectorySeparatorChar) ? base.Icon : null; - } - } - - internal class OsuDirectorySelectorParentDirectory : OsuDirectorySelectorDirectory - { - protected override IconUsage? Icon => FontAwesome.Solid.Folder; - - public OsuDirectorySelectorParentDirectory(DirectoryInfo directory) - : base(directory, "..") - { - } - } - - internal class OsuDirectorySelectorDirectory : DirectorySelectorDirectory - { - public OsuDirectorySelectorDirectory(DirectoryInfo directory, string displayName = null) - : base(directory, displayName) - { - } - - [BackgroundDependencyLoader] - private void load() - { - Flow.AutoSizeAxes = Axes.X; - Flow.Height = ITEM_HEIGHT; - - AddInternal(new OsuDirectorySelectorItemBackground - { - Depth = 1 - }); - } - - protected override SpriteText CreateSpriteText() => new OsuSpriteText(); - - protected override IconUsage? Icon => Directory.Name.Contains(Path.DirectorySeparatorChar) - ? FontAwesome.Solid.Database - : FontAwesome.Regular.Folder; - } - - internal class OsuDirectorySelectorItemBackground : CompositeDrawable - { - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - RelativeSizeAxes = Axes.Both; - - Masking = true; - CornerRadius = 5; - - InternalChild = new Box - { - Colour = colours.GreySeafoamDarker, - RelativeSizeAxes = Axes.Both, - }; - } - } } } diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorBreadcrumbDisplay.cs b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorBreadcrumbDisplay.cs new file mode 100644 index 0000000000..cb5ff242a1 --- /dev/null +++ b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorBreadcrumbDisplay.cs @@ -0,0 +1,64 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.IO; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics.Sprites; +using osuTK; + +namespace osu.Game.Graphics.UserInterfaceV2 +{ + internal class OsuDirectorySelectorBreadcrumbDisplay : DirectorySelectorBreadcrumbDisplay + { + protected override Drawable CreateCaption() => new OsuSpriteText + { + Text = "Current Directory: ", + Font = OsuFont.Default.With(size: OsuDirectorySelector.ITEM_HEIGHT), + }; + + protected override DirectorySelectorDirectory CreateRootDirectoryItem() => new OsuBreadcrumbDisplayComputer(); + + protected override DirectorySelectorDirectory CreateDirectoryItem(DirectoryInfo directory, string displayName = null) => new OsuBreadcrumbDisplayDirectory(directory, displayName); + + [BackgroundDependencyLoader] + private void load() + { + Height = 50; + } + + private class OsuBreadcrumbDisplayComputer : OsuBreadcrumbDisplayDirectory + { + protected override IconUsage? Icon => null; + + public OsuBreadcrumbDisplayComputer() + : base(null, "Computer") + { + } + } + + private class OsuBreadcrumbDisplayDirectory : OsuDirectorySelectorDirectory + { + public OsuBreadcrumbDisplayDirectory(DirectoryInfo directory, string displayName = null) + : base(directory, displayName) + { + } + + [BackgroundDependencyLoader] + private void load() + { + Flow.Add(new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Icon = FontAwesome.Solid.ChevronRight, + Size = new Vector2(FONT_SIZE / 2) + }); + } + + protected override IconUsage? Icon => Directory.Name.Contains(Path.DirectorySeparatorChar) ? base.Icon : null; + } + } +} diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs new file mode 100644 index 0000000000..8a420cdcfb --- /dev/null +++ b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs @@ -0,0 +1,58 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.IO; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Graphics.UserInterfaceV2 +{ + internal class OsuDirectorySelectorDirectory : DirectorySelectorDirectory + { + public OsuDirectorySelectorDirectory(DirectoryInfo directory, string displayName = null) + : base(directory, displayName) + { + } + + [BackgroundDependencyLoader] + private void load() + { + Flow.AutoSizeAxes = Axes.X; + Flow.Height = OsuDirectorySelector.ITEM_HEIGHT; + + AddInternal(new Background + { + Depth = 1 + }); + } + + protected override SpriteText CreateSpriteText() => new OsuSpriteText(); + + protected override IconUsage? Icon => Directory.Name.Contains(Path.DirectorySeparatorChar) + ? FontAwesome.Solid.Database + : FontAwesome.Regular.Folder; + + internal class Background : CompositeDrawable + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + RelativeSizeAxes = Axes.Both; + + Masking = true; + CornerRadius = 5; + + InternalChild = new Box + { + Colour = colours.GreySeafoamDarker, + RelativeSizeAxes = Axes.Both, + }; + } + } + } +} diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorParentDirectory.cs b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorParentDirectory.cs new file mode 100644 index 0000000000..481d811adb --- /dev/null +++ b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorParentDirectory.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.IO; +using osu.Framework.Graphics.Sprites; + +namespace osu.Game.Graphics.UserInterfaceV2 +{ + internal class OsuDirectorySelectorParentDirectory : OsuDirectorySelectorDirectory + { + protected override IconUsage? Icon => FontAwesome.Solid.Folder; + + public OsuDirectorySelectorParentDirectory(DirectoryInfo directory) + : base(directory, "..") + { + } + } +} diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs b/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs index c50178100e..b9fb642cbe 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs @@ -27,11 +27,11 @@ namespace osu.Game.Graphics.UserInterfaceV2 protected override ScrollContainer CreateScrollContainer() => new OsuScrollContainer(); - protected override DirectorySelectorBreadcrumbDisplay CreateBreadcrumb() => new OsuDirectorySelector.OsuDirectorySelectorBreadcrumbDisplay(); + protected override DirectorySelectorBreadcrumbDisplay CreateBreadcrumb() => new OsuDirectorySelectorBreadcrumbDisplay(); - protected override DirectorySelectorDirectory CreateParentDirectoryItem(DirectoryInfo directory) => new OsuDirectorySelector.OsuDirectorySelectorParentDirectory(directory); + protected override DirectorySelectorDirectory CreateParentDirectoryItem(DirectoryInfo directory) => new OsuDirectorySelectorParentDirectory(directory); - protected override DirectorySelectorDirectory CreateDirectoryItem(DirectoryInfo directory, string displayName = null) => new OsuDirectorySelector.OsuDirectorySelectorDirectory(directory, displayName); + protected override DirectorySelectorDirectory CreateDirectoryItem(DirectoryInfo directory, string displayName = null) => new OsuDirectorySelectorDirectory(directory, displayName); protected override DirectoryListingFile CreateFileItem(FileInfo file) => new OsuDirectoryListingFile(file); @@ -50,7 +50,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 Flow.AutoSizeAxes = Axes.X; Flow.Height = OsuDirectorySelector.ITEM_HEIGHT; - AddInternal(new OsuDirectorySelector.OsuDirectorySelectorItemBackground + AddInternal(new OsuDirectorySelectorDirectory.Background { Depth = 1 }); From ddb1da5a6611b11769675f7558343c2b441b0e8d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jul 2021 13:48:35 +0900 Subject: [PATCH 0210/2442] Tidy up class (although it's not in a good state logically) --- .../SelectionCycleFillFlowContainer.cs | 54 ++++++++++--------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs b/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs index 4f20c0039b..656f489772 100644 --- a/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs +++ b/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using osu.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -16,25 +17,11 @@ namespace osu.Game.Graphics.Containers /// public class SelectionCycleFillFlowContainer : FillFlowContainer where T : Drawable, IStateful { - private int? selectedIndex; - public T Selected => (selectedIndex >= 0 && selectedIndex < Count) ? this[selectedIndex.Value] : null; - private void setSelected(int? value) - { - if (selectedIndex == value) - return; + private int? selectedIndex; - // Deselect the previously-selected button - if (selectedIndex.HasValue) - this[selectedIndex.Value].State = SelectionState.NotSelected; - - selectedIndex = value; - - // Select the newly-selected button - if (selectedIndex.HasValue) - this[selectedIndex.Value].State = SelectionState.Selected; - } + private readonly Dictionary> handlerMap = new Dictionary>(); public void SelectNext() { @@ -64,20 +51,17 @@ namespace osu.Game.Graphics.Containers setSelected(IndexOf(item)); } - private readonly Dictionary> handlerMap = new Dictionary>(); - public override void Add(T drawable) { base.Add(drawable); - if (drawable != null) - { - // This event is used to update selection state when modified within the drawable itself. - // It is added to a dictionary so that we can unsubscribe if the drawable is removed from this container - handlerMap[drawable] = state => selectionChanged(drawable, state); + Debug.Assert(drawable != null); - drawable.StateChanged += handlerMap[drawable]; - } + // This event is used to update selection state when modified within the drawable itself. + // It is added to a dictionary so that we can unsubscribe if the drawable is removed from this container + handlerMap[drawable] = state => selectionChanged(drawable, state); + + drawable.StateChanged += handlerMap[drawable]; } public override bool Remove(T drawable) @@ -85,7 +69,9 @@ namespace osu.Game.Graphics.Containers if (!base.Remove(drawable)) return false; - if (drawable != null && handlerMap.TryGetValue(drawable, out var action)) + Debug.Assert(drawable != null); + + if (handlerMap.TryGetValue(drawable, out var action)) { drawable.StateChanged -= action; handlerMap.Remove(drawable); @@ -94,6 +80,22 @@ namespace osu.Game.Graphics.Containers return true; } + private void setSelected(int? value) + { + if (selectedIndex == value) + return; + + // Deselect the previously-selected button + if (selectedIndex.HasValue) + this[selectedIndex.Value].State = SelectionState.NotSelected; + + selectedIndex = value; + + // Select the newly-selected button + if (selectedIndex.HasValue) + this[selectedIndex.Value].State = SelectionState.Selected; + } + private void selectionChanged(T drawable, SelectionState state) { if (state == SelectionState.NotSelected) From eb8b14a931cffd9fb99858aecfc06ffaaef30ee8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jul 2021 13:51:51 +0900 Subject: [PATCH 0211/2442] Reorder methods to make more sense --- osu.Game/Skinning/SkinProvidingContainer.cs | 84 ++++++++++----------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index a628e97578..f386900f64 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -64,38 +64,19 @@ namespace osu.Game.Skinning RelativeSizeAxes = Axes.Both; } - /// - /// Add a new skin to this provider. Will be added to the end of the lookup order precedence. - /// - /// The skin to add. - protected void AddSource(ISkin skin) + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { - skinSources.Add((skin, new DisableableSkinSource(skin, this))); + var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - if (skin is ISkinSource source) - source.SourceChanged += TriggerSourceChanged; - } + ParentSource = dependencies.Get(); + if (ParentSource != null) + ParentSource.SourceChanged += TriggerSourceChanged; - /// - /// Remove a skin from this provider. - /// - /// The skin to remove. - protected void RemoveSource(ISkin skin) - { - if (skinSources.RemoveAll(s => s.skin == skin) == 0) - return; + dependencies.CacheAs(this); - if (skin is ISkinSource source) - source.SourceChanged -= TriggerSourceChanged; - } + TriggerSourceChanged(); - /// - /// Clears all skin sources. - /// - protected void ResetSources() - { - foreach (var skin in AllSources.ToArray()) - RemoveSource(skin); + return dependencies; } public ISkin FindProvider(Func lookupFunction) @@ -187,27 +168,46 @@ namespace osu.Game.Skinning return ParentSource?.GetConfig(lookup); } + /// + /// Add a new skin to this provider. Will be added to the end of the lookup order precedence. + /// + /// The skin to add. + protected void AddSource(ISkin skin) + { + skinSources.Add((skin, new DisableableSkinSource(skin, this))); + + if (skin is ISkinSource source) + source.SourceChanged += TriggerSourceChanged; + } + + /// + /// Remove a skin from this provider. + /// + /// The skin to remove. + protected void RemoveSource(ISkin skin) + { + if (skinSources.RemoveAll(s => s.skin == skin) == 0) + return; + + if (skin is ISkinSource source) + source.SourceChanged -= TriggerSourceChanged; + } + + /// + /// Clears all skin sources. + /// + protected void ResetSources() + { + foreach (var skin in AllSources.ToArray()) + RemoveSource(skin); + } + /// /// Invoked when any source has changed (either or a source registered via ). /// This is also invoked once initially during to ensure sources are ready for children consumption. /// protected virtual void OnSourceChanged() { } - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) - { - var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - - ParentSource = dependencies.Get(); - if (ParentSource != null) - ParentSource.SourceChanged += TriggerSourceChanged; - - dependencies.CacheAs(this); - - TriggerSourceChanged(); - - return dependencies; - } - protected void TriggerSourceChanged() { // Expose to implementations, giving them a chance to react before notifying external consumers. From 35d4b12a4f7bb0bb42b388d6c70cdb2c3d58c4b1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jul 2021 13:52:52 +0900 Subject: [PATCH 0212/2442] Remove single local usage of `AllSources` --- osu.Game/Skinning/SkinProvidingContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index f386900f64..d8f931da5e 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -198,8 +198,8 @@ namespace osu.Game.Skinning /// protected void ResetSources() { - foreach (var skin in AllSources.ToArray()) - RemoveSource(skin); + foreach (var i in skinSources) + RemoveSource(i.skin); } /// From ca791c2afa550c44b85de3380fa6dfaf6e41f4c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jul 2021 13:53:00 +0900 Subject: [PATCH 0213/2442] Remove unused using statement --- osu.Game/Skinning/SkinProvidingContainer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index d8f931da5e..d1a0187f7b 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio.Sample; From c18b8ca86c5095fcec39eae075eec175f6648eff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jul 2021 14:08:29 +0900 Subject: [PATCH 0214/2442] Add missing `ToArray()` call --- osu.Game/Skinning/SkinProvidingContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index d1a0187f7b..7c26fdaf03 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -197,7 +197,7 @@ namespace osu.Game.Skinning /// protected void ResetSources() { - foreach (var i in skinSources) + foreach (var i in skinSources.ToArray()) RemoveSource(i.skin); } From b08fece2d4126ae061e5f5d68fba259015450443 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jul 2021 14:30:47 +0900 Subject: [PATCH 0215/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index cfc28ffb21..9280eaf97c 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 646d21dfee..8a3c69e40c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index f36f7881cd..2eea646c61 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 33615646bd6ff48c2894d73c34a8e66fcb8eef84 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 6 Jul 2021 18:10:40 +0900 Subject: [PATCH 0216/2442] Rename DrawableRoom test scene --- .../{TestSceneRoomStatus.cs => TestSceneDrawableRoom.cs} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename osu.Game.Tests/Visual/Multiplayer/{TestSceneRoomStatus.cs => TestSceneDrawableRoom.cs} (95%) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs similarity index 95% rename from osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs rename to osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index cec40635f3..1231e0f7d0 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -10,9 +10,9 @@ using osu.Game.Screens.OnlinePlay.Lounge.Components; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneRoomStatus : OsuTestScene + public class TestSceneDrawableRoom : OsuTestScene { - public TestSceneRoomStatus() + public TestSceneDrawableRoom() { Child = new FillFlowContainer { From 1a832a4e6bb3dfd64587a7872fa841318a20006f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 6 Jul 2021 18:23:37 +0900 Subject: [PATCH 0217/2442] Add clickability to test --- .../Multiplayer/TestSceneDrawableRoom.cs | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index 1231e0f7d0..532ce6790f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.Rooms; using osu.Game.Online.Rooms.RoomStatuses; using osu.Game.Screens.OnlinePlay.Lounge.Components; @@ -20,32 +21,44 @@ namespace osu.Game.Tests.Visual.Multiplayer Width = 0.5f, Children = new Drawable[] { - new DrawableRoom(new Room + createDrawableRoom(new Room { Name = { Value = "Open - ending in 1 day" }, Status = { Value = new RoomStatusOpen() }, EndDate = { Value = DateTimeOffset.Now.AddDays(1) } - }) { MatchingFilter = true }, - new DrawableRoom(new Room + }), + createDrawableRoom(new Room { Name = { Value = "Playing - ending in 1 day" }, Status = { Value = new RoomStatusPlaying() }, EndDate = { Value = DateTimeOffset.Now.AddDays(1) } - }) { MatchingFilter = true }, - new DrawableRoom(new Room + }), + createDrawableRoom(new Room { Name = { Value = "Ended" }, Status = { Value = new RoomStatusEnded() }, EndDate = { Value = DateTimeOffset.Now } - }) { MatchingFilter = true }, - new DrawableRoom(new Room + }), + createDrawableRoom(new Room { Name = { Value = "Open" }, Status = { Value = new RoomStatusOpen() }, Category = { Value = RoomCategory.Realtime } - }) { MatchingFilter = true }, + }), } }; } + + private DrawableRoom createDrawableRoom(Room room) + { + var drawableRoom = new DrawableRoom(room) + { + MatchingFilter = true + }; + + drawableRoom.Action = () => drawableRoom.State = drawableRoom.State == SelectionState.Selected ? SelectionState.NotSelected : SelectionState.Selected; + + return drawableRoom; + } } } From c50e3fd31778c3ac476742d6e5897d586980c42b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 6 Jul 2021 18:24:06 +0900 Subject: [PATCH 0218/2442] Clean up selection box construction --- .../Lounge/Components/DrawableRoom.cs | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 35782c6104..14fd715b69 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -38,7 +38,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components public event Action StateChanged; - private readonly Box selectionBox; + private Box selectionBox; [Resolved(canBeNull: true)] private OnlinePlayScreen parentScreen { get; set; } @@ -55,14 +55,18 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components get => state; set { - if (value == state) return; + if (value == state) + return; state = value; - if (state == SelectionState.Selected) - selectionBox.FadeIn(transition_duration); - else - selectionBox.FadeOut(transition_duration); + if (selectionBox != null) + { + if (state == SelectionState.Selected) + selectionBox.FadeIn(transition_duration); + else + selectionBox.FadeOut(transition_duration); + } StateChanged?.Invoke(State); } @@ -99,13 +103,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Height = height + SELECTION_BORDER_WIDTH * 2; CornerRadius = corner_radius + SELECTION_BORDER_WIDTH / 2; Masking = true; - - // create selectionBox here so State can be set before being loaded - selectionBox = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0f, - }; } [BackgroundDependencyLoader] @@ -118,7 +115,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components new StatusColouredContainer(transition_duration) { RelativeSizeAxes = Axes.Both, - Child = selectionBox + Child = selectionBox = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = state == SelectionState.Selected ? 1 : 0, + } }, new Container { From 14b6949456b2f7d7278839c9e40bb7a8b49956c0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 6 Jul 2021 18:50:05 +0900 Subject: [PATCH 0219/2442] Shorten room status messages --- osu.Game/Online/Rooms/RoomStatuses/RoomStatusEnded.cs | 2 +- osu.Game/Online/Rooms/RoomStatuses/RoomStatusOpen.cs | 2 +- osu.Game/Online/Rooms/RoomStatuses/RoomStatusPlaying.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Rooms/RoomStatuses/RoomStatusEnded.cs b/osu.Game/Online/Rooms/RoomStatuses/RoomStatusEnded.cs index c852f86f6b..01f3ae368b 100644 --- a/osu.Game/Online/Rooms/RoomStatuses/RoomStatusEnded.cs +++ b/osu.Game/Online/Rooms/RoomStatuses/RoomStatusEnded.cs @@ -8,7 +8,7 @@ namespace osu.Game.Online.Rooms.RoomStatuses { public class RoomStatusEnded : RoomStatus { - public override string Message => @"Ended"; + public override string Message => "Ended"; public override Color4 GetAppropriateColour(OsuColour colours) => colours.YellowDarker; } } diff --git a/osu.Game/Online/Rooms/RoomStatuses/RoomStatusOpen.cs b/osu.Game/Online/Rooms/RoomStatuses/RoomStatusOpen.cs index 4f7f0d6f5d..686d4f4033 100644 --- a/osu.Game/Online/Rooms/RoomStatuses/RoomStatusOpen.cs +++ b/osu.Game/Online/Rooms/RoomStatuses/RoomStatusOpen.cs @@ -8,7 +8,7 @@ namespace osu.Game.Online.Rooms.RoomStatuses { public class RoomStatusOpen : RoomStatus { - public override string Message => @"Welcoming Players"; + public override string Message => "Open"; public override Color4 GetAppropriateColour(OsuColour colours) => colours.GreenLight; } } diff --git a/osu.Game/Online/Rooms/RoomStatuses/RoomStatusPlaying.cs b/osu.Game/Online/Rooms/RoomStatuses/RoomStatusPlaying.cs index f04f1b23af..83f1acf52a 100644 --- a/osu.Game/Online/Rooms/RoomStatuses/RoomStatusPlaying.cs +++ b/osu.Game/Online/Rooms/RoomStatuses/RoomStatusPlaying.cs @@ -8,7 +8,7 @@ namespace osu.Game.Online.Rooms.RoomStatuses { public class RoomStatusPlaying : RoomStatus { - public override string Message => @"Now Playing"; + public override string Message => "Playing"; public override Color4 GetAppropriateColour(OsuColour colours) => colours.Purple; } } From 2ddfa15a80d0c6ea11c2fdb0d8499557e21565f8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 6 Jul 2021 18:50:27 +0900 Subject: [PATCH 0220/2442] Redesign RoomStatusInfo --- .../OnlinePlay/Components/RoomStatusInfo.cs | 66 ++++++++++++------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomStatusInfo.cs b/osu.Game/Screens/OnlinePlay/Components/RoomStatusInfo.cs index bcc256bcff..900dea1e8c 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomStatusInfo.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomStatusInfo.cs @@ -4,12 +4,16 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; using osu.Game.Online.Rooms; using osu.Game.Online.Rooms.RoomStatuses; +using osuTK; +using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay.Components { @@ -23,26 +27,28 @@ namespace osu.Game.Screens.OnlinePlay.Components [BackgroundDependencyLoader] private void load() { - StatusPart statusPart; + StatusPill statusPill; EndDatePart endDatePart; InternalChild = new FillFlowContainer { AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(2), Children = new Drawable[] { - statusPart = new StatusPart + statusPill = new StatusPill(), + endDatePart = new EndDatePart { - Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 14) - }, - endDatePart = new EndDatePart { Font = OsuFont.GetFont(size: 14) } + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 12) + } } }; - statusPart.EndDate.BindTo(EndDate); - statusPart.Status.BindTo(Status); - statusPart.Availability.BindTo(Availability); + statusPill.EndDate.BindTo(EndDate); + statusPill.Status.BindTo(Status); endDatePart.EndDate.BindTo(EndDate); } @@ -80,37 +86,49 @@ namespace osu.Game.Screens.OnlinePlay.Components } } - private class StatusPart : EndDatePart + private class StatusPill : CompositeDrawable { + public readonly IBindable EndDate = new Bindable(); public readonly IBindable Status = new Bindable(); - public readonly IBindable Availability = new Bindable(); [Resolved] private OsuColour colours { get; set; } - public StatusPart() + private Drawable background; + private SpriteText statusText; + + [BackgroundDependencyLoader] + private void load() { - EndDate.BindValueChanged(_ => Format()); - Status.BindValueChanged(_ => Format()); - Availability.BindValueChanged(_ => Format()); + Size = new Vector2(60, 16); + + InternalChildren = new[] + { + background = new Circle { RelativeSizeAxes = Axes.Both }, + statusText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 12), + Colour = Color4.Black + } + }; } protected override void LoadComplete() { base.LoadComplete(); - Text = Format(); + EndDate.BindValueChanged(_ => updateDisplay()); + Status.BindValueChanged(_ => updateDisplay(), true); } - protected override string Format() + private void updateDisplay() { - if (!IsLoaded) - return string.Empty; + RoomStatus status = EndDate.Value < DateTimeOffset.Now ? new RoomStatusEnded() : Status.Value ?? new RoomStatusOpen(); - RoomStatus status = Date < DateTimeOffset.Now ? new RoomStatusEnded() : Status.Value ?? new RoomStatusOpen(); - - this.FadeColour(status.GetAppropriateColour(colours), 100); - return $"{Availability.Value.GetDescription()}, {status.Message}"; + background.FadeColour(status.GetAppropriateColour(colours), 100); + statusText.Text = status.Message; } } } From 8929aa0ca70ca397e5f05c3a0157e68af3136c68 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 6 Jul 2021 20:04:32 +0900 Subject: [PATCH 0221/2442] Initial redesign of DrawableRoom --- .../Multiplayer/TestSceneDrawableRoom.cs | 14 ++- .../Lounge/Components/DrawableRoom.cs | 104 ++++++++---------- 2 files changed, 57 insertions(+), 61 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index 532ce6790f..7fef5aba4d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -8,6 +8,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online.Rooms; using osu.Game.Online.Rooms.RoomStatuses; using osu.Game.Screens.OnlinePlay.Lounge.Components; +using osuTK; namespace osu.Game.Tests.Visual.Multiplayer { @@ -17,31 +18,34 @@ namespace osu.Game.Tests.Visual.Multiplayer { Child = new FillFlowContainer { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Width = 0.5f, + Size = new Vector2(0.9f), + Spacing = new Vector2(10), Children = new Drawable[] { createDrawableRoom(new Room { - Name = { Value = "Open - ending in 1 day" }, + Name = { Value = "Room name: Open - ending in 1 day" }, Status = { Value = new RoomStatusOpen() }, EndDate = { Value = DateTimeOffset.Now.AddDays(1) } }), createDrawableRoom(new Room { - Name = { Value = "Playing - ending in 1 day" }, + Name = { Value = "Room name: Playing - ending in 1 day" }, Status = { Value = new RoomStatusPlaying() }, EndDate = { Value = DateTimeOffset.Now.AddDays(1) } }), createDrawableRoom(new Room { - Name = { Value = "Ended" }, + Name = { Value = "Room name: Ended" }, Status = { Value = new RoomStatusEnded() }, EndDate = { Value = DateTimeOffset.Now } }), createDrawableRoom(new Room { - Name = { Value = "Open" }, + Name = { Value = "Room name: Open" }, Status = { Value = new RoomStatusOpen() }, Category = { Value = RoomCategory.Realtime } }), diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 14fd715b69..062b83b66c 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -8,6 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Effects; @@ -29,16 +30,13 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components public class DrawableRoom : OsuClickableContainer, IStateful, IFilterable, IHasContextMenu { public const float SELECTION_BORDER_WIDTH = 4; - private const float corner_radius = 5; + private const float corner_radius = 10; private const float transition_duration = 60; - private const float content_padding = 10; - private const float height = 110; - private const float side_strip_width = 5; - private const float cover_width = 145; + private const float height = 100; public event Action StateChanged; - private Box selectionBox; + private Drawable selectionBox; [Resolved(canBeNull: true)] private OnlinePlayScreen parentScreen { get; set; } @@ -108,23 +106,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components [BackgroundDependencyLoader] private void load(OsuColour colours) { - float stripWidth = side_strip_width * (Room.Category.Value == RoomCategory.Spotlight ? 2 : 1); - Children = new Drawable[] { - new StatusColouredContainer(transition_duration) - { - RelativeSizeAxes = Axes.Both, - Child = selectionBox = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = state == SelectionState.Selected ? 1 : 0, - } - }, new Container { + Name = @"Room content", RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding(SELECTION_BORDER_WIDTH), Child = new Container { RelativeSizeAxes = Axes.Both, @@ -141,69 +128,74 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"212121"), - }, - new StatusColouredContainer(transition_duration) - { - RelativeSizeAxes = Axes.Y, - Width = stripWidth, - Child = new Box { RelativeSizeAxes = Axes.Both } + Colour = Color4Extensions.FromHex(@"#27302E"), }, new Container { - RelativeSizeAxes = Axes.Y, - Width = cover_width, - Masking = true, - Margin = new MarginPadding { Left = stripWidth }, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fill, Child = new OnlinePlayBackgroundSprite(BeatmapSetCoverType.List) { RelativeSizeAxes = Axes.Both } }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientHorizontal(Color4Extensions.FromHex(@"#27302E"), Color4Extensions.FromHex(@"#27302E").Opacity(0.3f)) + }, new Container { + Name = @"Left details", RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { - Vertical = content_padding, - Left = stripWidth + cover_width + content_padding, - Right = content_padding, + Left = 20, + Vertical = 5 }, Children = new Drawable[] { - new FillFlowContainer + new RoomStatusInfo(), + new RoomName { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(5f), - Children = new Drawable[] - { - new RoomName { Font = OsuFont.GetFont(size: 18) }, - new ParticipantInfo(), - }, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Font = OsuFont.GetFont(size: 28) }, new FillFlowContainer { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5), + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, Children = new Drawable[] { - new RoomStatusInfo(), - new BeatmapTitle { TextSize = 14 }, - }, - }, - new ModeTypeInfo - { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - }, - }, + new StarRatingRangeDisplay { Scale = new Vector2(0.85f) } + } + } + } }, }, }, }, + new StatusColouredContainer(transition_duration) + { + RelativeSizeAxes = Axes.Both, + Child = selectionBox = new Container + { + RelativeSizeAxes = Axes.Both, + Alpha = state == SelectionState.Selected ? 1 : 0, + Masking = true, + CornerRadius = corner_radius, + BorderThickness = SELECTION_BORDER_WIDTH, + BorderColour = Color4.White, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + } + }, }; } From 35672f372a11fd69aa9e3ee67af5b7296131f32e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jul 2021 14:58:01 +0900 Subject: [PATCH 0222/2442] Shorten test beatmap to avoid timeouts in score submission test --- .../Gameplay/TestScenePlayerScoreSubmission.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs index d9c0544d3c..c3a46ec4ac 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs @@ -4,14 +4,18 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Screens; +using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.Rooms; using osu.Game.Online.Solo; using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Ranking; +using osu.Game.Tests.Beatmaps; +using osuTK; namespace osu.Game.Tests.Visual.Gameplay { @@ -25,6 +29,15 @@ namespace osu.Game.Tests.Visual.Gameplay protected override bool HasCustomSteps => true; + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) + { + var beatmap = (TestBeatmap)base.CreateBeatmap(ruleset); + + beatmap.HitObjects = beatmap.HitObjects.Take(10).ToList(); + + return beatmap; + } + protected override TestPlayer CreatePlayer(Ruleset ruleset) => new TestPlayer(false); [Test] From 28adb43a4a6151cc669d8512830e472cf51dc369 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 7 Jul 2021 09:26:17 +0300 Subject: [PATCH 0223/2442] Add detailed explaination for the reason of using old binding method --- osu.Game/Overlays/RestoreDefaultValueButton.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Overlays/RestoreDefaultValueButton.cs b/osu.Game/Overlays/RestoreDefaultValueButton.cs index 0790929ab2..44adb70108 100644 --- a/osu.Game/Overlays/RestoreDefaultValueButton.cs +++ b/osu.Game/Overlays/RestoreDefaultValueButton.cs @@ -23,6 +23,12 @@ namespace osu.Game.Overlays // this is done to ensure a click on this button doesn't trigger focus on a parent element which contains the button. public override bool AcceptsFocus => true; + // this is intentionally not using BindableWithCurrent as this needs a BindableNumber instance for an accurate IsDefault value. + // + // this also cannot use IBindableWithCurrent.Create() due to BindableNumberWithCurrent + // directly casting given bindables to BindableNumber, which is not necessarily the case. + // + // therefore rely on the old method of taking each current bindable instance for now, until things are settled framework-side. private Bindable current; public Bindable Current From faf95c7161b168c734a7d64d42e88b28566180be Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 7 Jul 2021 15:35:14 +0900 Subject: [PATCH 0224/2442] Remove unused usings --- .../Visual/Gameplay/TestScenePlayerScoreSubmission.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs index c3a46ec4ac..f9ccb10778 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs @@ -11,11 +11,9 @@ using osu.Game.Online.Solo; using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Judgements; -using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Ranking; using osu.Game.Tests.Beatmaps; -using osuTK; namespace osu.Game.Tests.Visual.Gameplay { From 115376c53825adb05cddd3af0d056163e04fc003 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 7 Jul 2021 16:10:24 +0900 Subject: [PATCH 0225/2442] Add playfield border to catch editor --- .../Edit/CatchHitObjectComposer.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index d9712bc8e9..d360274aa6 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Edit; @@ -20,6 +22,16 @@ namespace osu.Game.Rulesets.Catch.Edit { } + [BackgroundDependencyLoader] + private void load() + { + LayerBelowRuleset.Add(new PlayfieldBorder + { + RelativeSizeAxes = Axes.Both, + PlayfieldBorderStyle = { Value = PlayfieldBorderStyle.Corners } + }); + } + protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) => new DrawableCatchEditorRuleset(ruleset, beatmap, mods); From 7d76fcf2b64766c14dce7c54c6af03ab0cdb4b37 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 7 Jul 2021 16:13:34 +0900 Subject: [PATCH 0226/2442] Fix hit object placement not receiving input when outside playfield The input area is vertical infinite, but horizontally restricted to the playfield due to `CatchPlayfield`'s `ReceivePositionalInputAt` override. --- .../Edit/Blueprints/CatchPlacementBlueprint.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/CatchPlacementBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/CatchPlacementBlueprint.cs index 69054e2c81..5a32d241ad 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/CatchPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/CatchPlacementBlueprint.cs @@ -6,6 +6,7 @@ using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; +using osuTK; namespace osu.Game.Rulesets.Catch.Edit.Blueprints { @@ -23,5 +24,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints : base(new THitObject()) { } + + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; } } From 09925dffef48d0e20232cbd38d260792a4420246 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jul 2021 16:30:23 +0900 Subject: [PATCH 0227/2442] Add missing `HeadlessTest` flag on new test scene --- osu.Game.Tests/Skins/TestSceneSkinProvidingContainer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Skins/TestSceneSkinProvidingContainer.cs b/osu.Game.Tests/Skins/TestSceneSkinProvidingContainer.cs index cfc4ccd208..ab47067411 100644 --- a/osu.Game.Tests/Skins/TestSceneSkinProvidingContainer.cs +++ b/osu.Game.Tests/Skins/TestSceneSkinProvidingContainer.cs @@ -10,12 +10,14 @@ using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; +using osu.Framework.Testing; using osu.Game.Audio; using osu.Game.Skinning; using osu.Game.Tests.Visual; namespace osu.Game.Tests.Skins { + [HeadlessTest] public class TestSceneSkinProvidingContainer : OsuTestScene { /// From 8b1876bc2a3341e4151123f9e56e1e8eec6e63a9 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Wed, 7 Jul 2021 11:43:54 +0200 Subject: [PATCH 0228/2442] Disallow removing items from SelectionCycleFillFlowContainer --- .../SelectionCycleFillFlowContainer.cs | 23 ++----------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs b/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs index 656f489772..cef903f63e 100644 --- a/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs +++ b/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using System.Diagnostics; using osu.Framework; using osu.Framework.Graphics; @@ -21,8 +20,6 @@ namespace osu.Game.Graphics.Containers private int? selectedIndex; - private readonly Dictionary> handlerMap = new Dictionary>(); - public void SelectNext() { if (!selectedIndex.HasValue || selectedIndex == Count - 1) @@ -57,28 +54,12 @@ namespace osu.Game.Graphics.Containers Debug.Assert(drawable != null); - // This event is used to update selection state when modified within the drawable itself. - // It is added to a dictionary so that we can unsubscribe if the drawable is removed from this container - handlerMap[drawable] = state => selectionChanged(drawable, state); - - drawable.StateChanged += handlerMap[drawable]; + drawable.StateChanged += state => selectionChanged(drawable, state); } public override bool Remove(T drawable) - { - if (!base.Remove(drawable)) - return false; + => throw new NotSupportedException($"Cannot remove drawables from {nameof(SelectionCycleFillFlowContainer)}"); - Debug.Assert(drawable != null); - - if (handlerMap.TryGetValue(drawable, out var action)) - { - drawable.StateChanged -= action; - handlerMap.Remove(drawable); - } - - return true; - } private void setSelected(int? value) { From f53f6690e3982fecc1b4b5dcdfea4ef808b0b80c Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Wed, 7 Jul 2021 12:01:47 +0200 Subject: [PATCH 0229/2442] Remove extra blank line --- osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs b/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs index cef903f63e..90b2d20e4d 100644 --- a/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs +++ b/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs @@ -60,7 +60,6 @@ namespace osu.Game.Graphics.Containers public override bool Remove(T drawable) => throw new NotSupportedException($"Cannot remove drawables from {nameof(SelectionCycleFillFlowContainer)}"); - private void setSelected(int? value) { if (selectedIndex == value) From 4d7c7441016aff5dc5f8e2b6f7476716e234a36c Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Wed, 7 Jul 2021 12:59:31 +0200 Subject: [PATCH 0230/2442] Fix failing test --- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index 6fcc70e5f2..2608c93fa1 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Humanizer; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; @@ -41,7 +42,7 @@ namespace osu.Game.Screens.Play /// /// Action that is invoked when is triggered. /// - protected virtual Action BackAction => () => InternalButtons.Selected?.Click(); + protected virtual Action BackAction => () => InternalButtons.Children.LastOrDefault()?.Click(); /// /// Action that is invoked when is triggered. From 83283a706eca6bc8c39d8b775ac8ce5d2a8db13e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jul 2021 20:51:13 +0900 Subject: [PATCH 0231/2442] Add test scene --- .../UserInterface/TestSceneVolumeOverlay.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneVolumeOverlay.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneVolumeOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneVolumeOverlay.cs new file mode 100644 index 0000000000..64708c4858 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneVolumeOverlay.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. + +using osu.Framework.Graphics; +using osu.Game.Overlays; +using osu.Game.Overlays.Volume; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneVolumeOverlay : OsuTestScene + { + private VolumeOverlay volume; + + protected override void LoadComplete() + { + base.LoadComplete(); + + AddRange(new Drawable[] + { + volume = new VolumeOverlay(), + new VolumeControlReceptor + { + RelativeSizeAxes = Axes.Both, + ActionRequested = action => volume.Adjust(action), + ScrollActionRequested = (action, amount, isPrecise) => volume.Adjust(action, amount, isPrecise), + }, + }); + + AddStep("show controls", () => volume.Show()); + } + } +} From f1aa99e1033c8115259f4b9dcbc7c9ecf49932b0 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 7 Jul 2021 21:01:55 +0900 Subject: [PATCH 0232/2442] Fix catch selection blueprint not displayed after copy-pasted --- .../Edit/Blueprints/CatchSelectionBlueprint.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/CatchSelectionBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/CatchSelectionBlueprint.cs index 298f9474b0..720d730858 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/CatchSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/CatchSelectionBlueprint.cs @@ -13,6 +13,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints public abstract class CatchSelectionBlueprint : HitObjectSelectionBlueprint where THitObject : CatchHitObject { + protected override bool AlwaysShowWhenSelected => true; + public override Vector2 ScreenSpaceSelectionPoint { get From cbe4114e90d8a16f960e6a3583139bbf7072ffc8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jul 2021 21:02:48 +0900 Subject: [PATCH 0233/2442] Adjust visuals and make base opacity 100% --- osu.Game/Overlays/Volume/VolumeMeter.cs | 75 ++++++++++++------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs index a7c4fb6e7d..82ab45428a 100644 --- a/osu.Game/Overlays/Volume/VolumeMeter.cs +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -40,7 +40,7 @@ namespace osu.Game.Overlays.Volume private OsuSpriteText text; private BufferedContainer maxGlow; - private Container focusGlowContainer; + private Container selectedGlowContainer; private Sample sample; private double sampleLastPlaybackTime; @@ -59,6 +59,8 @@ namespace osu.Game.Overlays.Volume state = value; StateChanged?.Invoke(value); + + updateSelectedState(); } } @@ -94,28 +96,8 @@ namespace osu.Game.Overlays.Volume Size = new Vector2(circleSize), Children = new Drawable[] { - focusGlowContainer = new CircularContainer - { - Masking = true, - RelativeSizeAxes = Axes.Both, - Alpha = 0, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true, - }, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = meterColour.Opacity(0.5f), - Radius = 5, - Hollow = true, - } - }, new BufferedContainer { - Alpha = 0.9f, RelativeSizeAxes = Axes.Both, Children = new Drawable[] { @@ -187,6 +169,24 @@ namespace osu.Game.Overlays.Volume }, }, }, + selectedGlowContainer = new CircularContainer + { + Masking = true, + RelativeSizeAxes = Axes.Both, + Alpha = 0, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true, + }, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = meterColour.Opacity(0.1f), + Radius = 10, + } + }, maxGlow = (text = new OsuSpriteText { Anchor = Anchor.Centre, @@ -211,7 +211,6 @@ namespace osu.Game.Overlays.Volume { new Box { - Alpha = 0.9f, RelativeSizeAxes = Axes.Both, Colour = backgroundColour, }, @@ -229,8 +228,6 @@ namespace osu.Game.Overlays.Volume Bindable.BindValueChanged(volume => { this.TransformTo(nameof(DisplayVolume), volume.NewValue, 400, Easing.OutQuint); }, true); bgProgress.Current.Value = 0.75f; - - StateChanged += stateChanged; } private int? displayVolumeInt; @@ -359,20 +356,6 @@ namespace osu.Game.Overlays.Volume { } - private void stateChanged(SelectionState newState) - { - if (newState == SelectionState.Selected) - { - this.ScaleTo(1.04f, transition_length, Easing.OutExpo); - focusGlowContainer.FadeIn(transition_length, Easing.OutExpo); - } - else - { - this.ScaleTo(1f, transition_length, Easing.OutExpo); - focusGlowContainer.FadeOut(transition_length, Easing.OutExpo); - } - } - public bool OnPressed(GlobalAction action) { if (!IsHovered) @@ -397,5 +380,21 @@ namespace osu.Game.Overlays.Volume public void OnReleased(GlobalAction action) { } + + private void updateSelectedState() + { + switch (state) + { + case SelectionState.Selected: + this.ScaleTo(1.04f, transition_length, Easing.OutExpo); + selectedGlowContainer.FadeIn(transition_length, Easing.OutExpo); + break; + + case SelectionState.NotSelected: + this.ScaleTo(1f, transition_length, Easing.OutExpo); + selectedGlowContainer.FadeOut(transition_length, Easing.OutExpo); + break; + } + } } } From 7d405f04fbff8d449864958e2ddb4b3c65511ce1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jul 2021 21:17:31 +0900 Subject: [PATCH 0234/2442] Fix selected volume control not updating correctly on mouse move --- osu.Game/Overlays/Volume/VolumeMeter.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs index 82ab45428a..eed6f5c2f3 100644 --- a/osu.Game/Overlays/Volume/VolumeMeter.cs +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -64,6 +64,8 @@ namespace osu.Game.Overlays.Volume } } + private const float transition_length = 500; + public VolumeMeter(string name, float circleSize, Color4 meterColour) { this.circleSize = circleSize; @@ -344,12 +346,10 @@ namespace osu.Game.Overlays.Volume return true; } - private const float transition_length = 500; - - protected override bool OnHover(HoverEvent e) + protected override bool OnMouseMove(MouseMoveEvent e) { State = SelectionState.Selected; - return false; + return base.OnMouseMove(e); } protected override void OnHoverLost(HoverLostEvent e) From ddca132ab594c7c325e4f609656be2bc73352338 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 7 Jul 2021 21:38:38 +0900 Subject: [PATCH 0235/2442] Add difficulty adjustment mod tests --- .../Mods/ModDifficultyAdjustTest.cs | 168 ++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 osu.Game.Tests/Mods/ModDifficultyAdjustTest.cs diff --git a/osu.Game.Tests/Mods/ModDifficultyAdjustTest.cs b/osu.Game.Tests/Mods/ModDifficultyAdjustTest.cs new file mode 100644 index 0000000000..fcbdcbe724 --- /dev/null +++ b/osu.Game.Tests/Mods/ModDifficultyAdjustTest.cs @@ -0,0 +1,168 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Online.API; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Tests.Mods +{ + [TestFixture] + [Ignore("Currently broken, pending fixes/reworking of ModDifficultyAdjust.")] + public class ModDifficultyAdjustTest + { + // Todo: This shouldn't exist, but is currently required for the mod to accept a new BeatmapDifficulty object... + private int currentId; + + private TestModDifficultyAdjust testMod; + + [SetUp] + public void Setup() + { + currentId = 0; + testMod = new TestModDifficultyAdjust(); + + // Todo: This shouldn't be a thing, but is currently required because this causes the mod to keep track of the bindables internally... + applyDifficulty(new BeatmapDifficulty + { + DrainRate = -1, + OverallDifficulty = -1 + }); + } + + [Test] + public void TestUnchangedSettingsFollowAppliedDifficulty() + { + var result = applyDifficulty(new BeatmapDifficulty + { + DrainRate = 10, + OverallDifficulty = 10 + }); + + Assert.That(result.DrainRate, Is.EqualTo(10)); + Assert.That(result.OverallDifficulty, Is.EqualTo(10)); + + result = applyDifficulty(new BeatmapDifficulty + { + DrainRate = 1, + OverallDifficulty = 1 + }); + + Assert.That(result.DrainRate, Is.EqualTo(1)); + Assert.That(result.OverallDifficulty, Is.EqualTo(1)); + } + + [Test] + public void TestChangedSettingsOverrideAppliedDifficulty() + { + testMod.OverallDifficulty.Value = 4; + + var result = applyDifficulty(new BeatmapDifficulty + { + DrainRate = 10, + OverallDifficulty = 10 + }); + + Assert.That(result.DrainRate, Is.EqualTo(10)); + Assert.That(result.OverallDifficulty, Is.EqualTo(4)); + + result = applyDifficulty(new BeatmapDifficulty + { + DrainRate = 1, + OverallDifficulty = 1 + }); + + Assert.That(result.DrainRate, Is.EqualTo(1)); + Assert.That(result.OverallDifficulty, Is.EqualTo(4)); + } + + [Test] + public void TestChangedSettingsRetainedWhenSameValueIsApplied() + { + testMod.OverallDifficulty.Value = 4; + + // Apply and de-apply the same value as the mod. + applyDifficulty(new BeatmapDifficulty { OverallDifficulty = 4 }); + var result = applyDifficulty(new BeatmapDifficulty { OverallDifficulty = 10 }); + + Assert.That(result.OverallDifficulty, Is.EqualTo(4)); + } + + [Test] + public void TestChangedSettingSerialisedWhenSameValueIsApplied() + { + applyDifficulty(new BeatmapDifficulty { OverallDifficulty = 4 }); + testMod.OverallDifficulty.Value = 4; + + var result = (TestModDifficultyAdjust)new APIMod(testMod).ToMod(new TestRuleset()); + + Assert.That(result.OverallDifficulty.Value, Is.EqualTo(4)); + } + + [Test] + public void TestChangedSettingsRevertedToDefault() + { + applyDifficulty(new BeatmapDifficulty + { + DrainRate = 10, + OverallDifficulty = 10 + }); + + testMod.OverallDifficulty.Value = 4; + testMod.ResetSettingsToDefaults(); + + Assert.That(testMod.DrainRate.Value, Is.EqualTo(10)); + Assert.That(testMod.OverallDifficulty.Value, Is.EqualTo(10)); + } + + /// + /// Applies a to the mod and returns a new + /// representing the result if the mod were applied to a fresh instance. + /// + private BeatmapDifficulty applyDifficulty(BeatmapDifficulty difficulty) + { + difficulty.ID = ++currentId; + testMod.ReadFromDifficulty(difficulty); + + var newDifficulty = new BeatmapDifficulty(); + testMod.ApplyToDifficulty(newDifficulty); + return newDifficulty; + } + + private class TestModDifficultyAdjust : ModDifficultyAdjust + { + } + + private class TestRuleset : Ruleset + { + public override IEnumerable GetModsFor(ModType type) + { + if (type == ModType.DifficultyIncrease) + yield return new TestModDifficultyAdjust(); + } + + public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) + { + throw new System.NotImplementedException(); + } + + public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) + { + throw new System.NotImplementedException(); + } + + public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) + { + throw new System.NotImplementedException(); + } + + public override string Description => string.Empty; + public override string ShortName => string.Empty; + } + } +} From 341cb09c6eb5399b3ba113cf92ef2c620e4dc43a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Jul 2021 12:06:52 +0900 Subject: [PATCH 0236/2442] Update terminology in README At some point we'll want to replace the link to the outdated blog post (or just remove it?) with the gantt or otherwise. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2213b42121..e95c12cfdc 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ A free-to-win rhythm game. Rhythm is just a *click* away! -The future of [osu!](https://osu.ppy.sh) and the beginning of an open era! Commonly known by the codename *osu!lazer*. Pew pew. +The future of [osu!](https://osu.ppy.sh) and the beginning of an open era! Currently known by and released under the codename "*lazer*". As in sharper than cutting-edge. ## Status @@ -23,7 +23,7 @@ We are accepting bug reports (please report with as much detail as possible and - Detailed release changelogs are available on the [official osu! site](https://osu.ppy.sh/home/changelog/lazer). - You can learn more about our approach to [project management](https://github.com/ppy/osu/wiki/Project-management). -- Read peppy's [latest blog post](https://blog.ppy.sh/a-definitive-lazer-faq/) exploring where lazer is currently and the roadmap going forward. +- Read peppy's [latest blog post](https://blog.ppy.sh/a-definitive-lazer-faq/) exploring where the project is currently and the roadmap going forward. ## Running osu! From 663ffae42f9b8e97037e7f4c3ba06b8acb68d600 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 7 Jul 2021 21:02:11 +0900 Subject: [PATCH 0237/2442] Fix hit object selection blueprint potential null reference --- osu.Game/Rulesets/Edit/HitObjectSelectionBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectSelectionBlueprint.cs b/osu.Game/Rulesets/Edit/HitObjectSelectionBlueprint.cs index 56434b1d82..77dc55c6ef 100644 --- a/osu.Game/Rulesets/Edit/HitObjectSelectionBlueprint.cs +++ b/osu.Game/Rulesets/Edit/HitObjectSelectionBlueprint.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Edit /// protected virtual bool AlwaysShowWhenSelected => false; - protected override bool ShouldBeAlive => (DrawableObject.IsAlive && DrawableObject.IsPresent) || (AlwaysShowWhenSelected && State == SelectionState.Selected); + protected override bool ShouldBeAlive => (DrawableObject?.IsAlive == true && DrawableObject.IsPresent) || (AlwaysShowWhenSelected && State == SelectionState.Selected); protected HitObjectSelectionBlueprint(HitObject hitObject) : base(hitObject) From 8d94e8f5346b7bf17ee8cf4936ce177533e041b8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Jul 2021 14:28:05 +0900 Subject: [PATCH 0238/2442] Enable tests and update expectations --- osu.Game.Tests/Mods/ModDifficultyAdjustTest.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Mods/ModDifficultyAdjustTest.cs b/osu.Game.Tests/Mods/ModDifficultyAdjustTest.cs index fcbdcbe724..692f4ec5b7 100644 --- a/osu.Game.Tests/Mods/ModDifficultyAdjustTest.cs +++ b/osu.Game.Tests/Mods/ModDifficultyAdjustTest.cs @@ -13,7 +13,6 @@ using osu.Game.Rulesets.UI; namespace osu.Game.Tests.Mods { [TestFixture] - [Ignore("Currently broken, pending fixes/reworking of ModDifficultyAdjust.")] public class ModDifficultyAdjustTest { // Todo: This shouldn't exist, but is currently required for the mod to accept a new BeatmapDifficulty object... @@ -116,8 +115,16 @@ namespace osu.Game.Tests.Mods testMod.OverallDifficulty.Value = 4; testMod.ResetSettingsToDefaults(); - Assert.That(testMod.DrainRate.Value, Is.EqualTo(10)); - Assert.That(testMod.OverallDifficulty.Value, Is.EqualTo(10)); + Assert.That(testMod.DrainRate.Value, Is.Null); + Assert.That(testMod.OverallDifficulty.Value, Is.Null); + + var applied = applyDifficulty(new BeatmapDifficulty + { + DrainRate = 10, + OverallDifficulty = 10 + }); + + Assert.That(applied.OverallDifficulty, Is.EqualTo(10)); } /// @@ -126,10 +133,12 @@ namespace osu.Game.Tests.Mods /// private BeatmapDifficulty applyDifficulty(BeatmapDifficulty difficulty) { + // ensure that ReadFromDifficulty doesn't pollute the values. + var newDifficulty = difficulty.Clone(); + difficulty.ID = ++currentId; testMod.ReadFromDifficulty(difficulty); - var newDifficulty = new BeatmapDifficulty(); testMod.ApplyToDifficulty(newDifficulty); return newDifficulty; } From 0e4f4a6fde3ca36abd4978b01e9806d0a3d0a6a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Jul 2021 14:28:13 +0900 Subject: [PATCH 0239/2442] Initial storage changes --- .../Mods/CatchModDifficultyAdjust.cs | 29 +++--- .../Mods/OsuModDifficultyAdjust.cs | 29 +++--- .../Mods/TaikoModDifficultyAdjust.cs | 8 +- osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs | 97 +++---------------- 4 files changed, 40 insertions(+), 123 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs index bd7a1df2e4..aaa426cd03 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs @@ -13,23 +13,23 @@ namespace osu.Game.Rulesets.Catch.Mods public class CatchModDifficultyAdjust : ModDifficultyAdjust, IApplicableToBeatmapProcessor { [SettingSource("Circle Size", "Override a beatmap's set CS.", FIRST_SETTING_ORDER - 1)] - public BindableNumber CircleSize { get; } = new BindableFloatWithLimitExtension + public Bindable CircleSize { get; } = new Bindable { + /* Precision = 0.1f, MinValue = 1, MaxValue = 10, - Default = 5, - Value = 5, + */ }; [SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1)] - public BindableNumber ApproachRate { get; } = new BindableFloatWithLimitExtension + public Bindable ApproachRate { get; } = new Bindable { + /* Precision = 0.1f, MinValue = 1, MaxValue = 10, - Default = 5, - Value = 5, + */ }; [SettingSource("Spicy Patterns", "Adjust the patterns as if Hard Rock is enabled.")] @@ -39,8 +39,9 @@ namespace osu.Game.Rulesets.Catch.Mods { base.ApplyLimits(extended); - CircleSize.MaxValue = extended ? 11 : 10; - ApproachRate.MaxValue = extended ? 11 : 10; + // TODO: reimplement + // CircleSize.MaxValue = extended ? 11 : 10; + // ApproachRate.MaxValue = extended ? 11 : 10; } public override string SettingDescription @@ -61,20 +62,12 @@ namespace osu.Game.Rulesets.Catch.Mods } } - protected override void TransferSettings(BeatmapDifficulty difficulty) - { - base.TransferSettings(difficulty); - - TransferSetting(CircleSize, difficulty.CircleSize); - TransferSetting(ApproachRate, difficulty.ApproachRate); - } - protected override void ApplySettings(BeatmapDifficulty difficulty) { base.ApplySettings(difficulty); - ApplySetting(CircleSize, cs => difficulty.CircleSize = cs); - ApplySetting(ApproachRate, ar => difficulty.ApproachRate = ar); + if (CircleSize.Value != null) difficulty.CircleSize = CircleSize.Value.Value; + if (ApproachRate.Value != null) difficulty.ApproachRate = ApproachRate.Value.Value; } public void ApplyToBeatmapProcessor(IBeatmapProcessor beatmapProcessor) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs index 1cb25edecf..54e30c56a3 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs @@ -12,31 +12,32 @@ namespace osu.Game.Rulesets.Osu.Mods public class OsuModDifficultyAdjust : ModDifficultyAdjust { [SettingSource("Circle Size", "Override a beatmap's set CS.", FIRST_SETTING_ORDER - 1)] - public BindableNumber CircleSize { get; } = new BindableFloatWithLimitExtension + public Bindable CircleSize { get; } = new Bindable { + /* Precision = 0.1f, MinValue = 0, MaxValue = 10, - Default = 5, - Value = 5, + */ }; [SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1)] - public BindableNumber ApproachRate { get; } = new BindableFloatWithLimitExtension + public Bindable ApproachRate { get; } = new Bindable { + /* Precision = 0.1f, MinValue = 0, MaxValue = 10, - Default = 5, - Value = 5, + */ }; protected override void ApplyLimits(bool extended) { base.ApplyLimits(extended); - CircleSize.MaxValue = extended ? 11 : 10; - ApproachRate.MaxValue = extended ? 11 : 10; + // TODO: reimplement + // CircleSize.MaxValue = extended ? 11 : 10; + // ApproachRate.MaxValue = extended ? 11 : 10; } public override string SettingDescription @@ -55,20 +56,12 @@ namespace osu.Game.Rulesets.Osu.Mods } } - protected override void TransferSettings(BeatmapDifficulty difficulty) - { - base.TransferSettings(difficulty); - - TransferSetting(CircleSize, difficulty.CircleSize); - TransferSetting(ApproachRate, difficulty.ApproachRate); - } - protected override void ApplySettings(BeatmapDifficulty difficulty) { base.ApplySettings(difficulty); - ApplySetting(CircleSize, cs => difficulty.CircleSize = cs); - ApplySetting(ApproachRate, ar => difficulty.ApproachRate = ar); + if (CircleSize.Value != null) difficulty.CircleSize = CircleSize.Value.Value; + if (ApproachRate.Value != null) difficulty.ApproachRate = ApproachRate.Value.Value; } } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs index 4006652bd5..902ccdc14e 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs @@ -12,13 +12,13 @@ namespace osu.Game.Rulesets.Taiko.Mods public class TaikoModDifficultyAdjust : ModDifficultyAdjust { [SettingSource("Scroll Speed", "Adjust a beatmap's set scroll speed", LAST_SETTING_ORDER + 1)] - public BindableNumber ScrollSpeed { get; } = new BindableFloat + public Bindable ScrollSpeed { get; } = new Bindable { + /* Precision = 0.05f, MinValue = 0.25f, MaxValue = 4, - Default = 1, - Value = 1, + */ }; public override string SettingDescription @@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Taiko.Mods { base.ApplySettings(difficulty); - ApplySetting(ScrollSpeed, scroll => difficulty.SliderMultiplier *= scroll); + if (ScrollSpeed.Value != null) difficulty.SliderMultiplier = ScrollSpeed.Value.Value; } } } diff --git a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs index b70eee4e1d..e98bd14720 100644 --- a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs @@ -1,13 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Beatmaps; +using System; +using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; -using System; -using System.Collections.Generic; +using osu.Game.Beatmaps; using osu.Game.Configuration; -using System.Linq; namespace osu.Game.Rulesets.Mods { @@ -34,23 +33,23 @@ namespace osu.Game.Rulesets.Mods protected const int LAST_SETTING_ORDER = 2; [SettingSource("HP Drain", "Override a beatmap's set HP.", FIRST_SETTING_ORDER)] - public BindableNumber DrainRate { get; } = new BindableFloatWithLimitExtension + public Bindable DrainRate { get; } = new Bindable { + /* Precision = 0.1f, MinValue = 0, MaxValue = 10, - Default = 5, - Value = 5, + */ }; [SettingSource("Accuracy", "Override a beatmap's set OD.", LAST_SETTING_ORDER)] - public BindableNumber OverallDifficulty { get; } = new BindableFloatWithLimitExtension + public Bindable OverallDifficulty { get; } = new Bindable { + /* Precision = 0.1f, MinValue = 0, MaxValue = 10, - Default = 5, - Value = 5, + */ }; [SettingSource("Extended Limits", "Adjust difficulty beyond sane limits.")] @@ -67,8 +66,9 @@ namespace osu.Game.Rulesets.Mods /// Whether limits should extend beyond sane ranges. protected virtual void ApplyLimits(bool extended) { - DrainRate.MaxValue = extended ? 11 : 10; - OverallDifficulty.MaxValue = extended ? 11 : 10; + // TODO: reimplement + // DrainRate.MaxValue = extended ? 11 : 10; + // OverallDifficulty.MaxValue = extended ? 11 : 10; } public override string SettingDescription @@ -86,89 +86,20 @@ namespace osu.Game.Rulesets.Mods } } - private BeatmapDifficulty difficulty; - public void ReadFromDifficulty(BeatmapDifficulty difficulty) { - if (this.difficulty == null || this.difficulty.ID != difficulty.ID) - { - TransferSettings(difficulty); - this.difficulty = difficulty; - } } public void ApplyToDifficulty(BeatmapDifficulty difficulty) => ApplySettings(difficulty); - /// - /// Transfer initial settings from the beatmap to settings. - /// - /// The beatmap's initial values. - protected virtual void TransferSettings(BeatmapDifficulty difficulty) - { - TransferSetting(DrainRate, difficulty.DrainRate); - TransferSetting(OverallDifficulty, difficulty.OverallDifficulty); - } - - private readonly Dictionary userChangedSettings = new Dictionary(); - - /// - /// Transfer a setting from to a configuration bindable. - /// Only performs the transfer if the user is not currently overriding. - /// - protected void TransferSetting(BindableNumber bindable, T beatmapDefault) - where T : struct, IComparable, IConvertible, IEquatable - { - bindable.UnbindEvents(); - - userChangedSettings.TryAdd(bindable, false); - - bindable.Default = beatmapDefault; - - // users generally choose a difficulty setting and want it to stick across multiple beatmap changes. - // we only want to value transfer if the user hasn't changed the value previously. - if (!userChangedSettings[bindable]) - bindable.Value = beatmapDefault; - - bindable.ValueChanged += _ => userChangedSettings[bindable] = !bindable.IsDefault; - } - - internal override void CopyAdjustedSetting(IBindable target, object source) - { - // if the value is non-bindable, it's presumably coming from an external source (like the API) - therefore presume it is not default. - // if the value is bindable, defer to the source's IsDefault to be able to tell. - userChangedSettings[target] = !(source is IBindable bindableSource) || !bindableSource.IsDefault; - base.CopyAdjustedSetting(target, source); - } - - /// - /// Applies a setting from a configuration bindable using , if it has been changed by the user. - /// - protected void ApplySetting(BindableNumber setting, Action applyFunc) - where T : struct, IComparable, IConvertible, IEquatable - { - if (userChangedSettings.TryGetValue(setting, out bool userChangedSetting) && userChangedSetting) - applyFunc.Invoke(setting.Value); - } - /// /// Apply all custom settings to the provided beatmap. /// /// The beatmap to have settings applied. protected virtual void ApplySettings(BeatmapDifficulty difficulty) { - ApplySetting(DrainRate, dr => difficulty.DrainRate = dr); - ApplySetting(OverallDifficulty, od => difficulty.OverallDifficulty = od); - } - - public override void ResetSettingsToDefaults() - { - base.ResetSettingsToDefaults(); - - if (difficulty != null) - { - // base implementation potentially overwrite modified defaults that came from a beatmap selection. - TransferSettings(difficulty); - } + if (DrainRate.Value != null) difficulty.DrainRate = DrainRate.Value.Value; + if (OverallDifficulty.Value != null) difficulty.OverallDifficulty = OverallDifficulty.Value.Value; } /// From d540156e94c3254ee43a9aa28dfc1f13a01852ce Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Jul 2021 14:29:30 +0900 Subject: [PATCH 0240/2442] Remove now unnecessary `BeatmapDifficulty.ID` --- osu.Game.Tests/Mods/ModDifficultyAdjustTest.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/osu.Game.Tests/Mods/ModDifficultyAdjustTest.cs b/osu.Game.Tests/Mods/ModDifficultyAdjustTest.cs index 692f4ec5b7..84cf796835 100644 --- a/osu.Game.Tests/Mods/ModDifficultyAdjustTest.cs +++ b/osu.Game.Tests/Mods/ModDifficultyAdjustTest.cs @@ -15,23 +15,12 @@ namespace osu.Game.Tests.Mods [TestFixture] public class ModDifficultyAdjustTest { - // Todo: This shouldn't exist, but is currently required for the mod to accept a new BeatmapDifficulty object... - private int currentId; - private TestModDifficultyAdjust testMod; [SetUp] public void Setup() { - currentId = 0; testMod = new TestModDifficultyAdjust(); - - // Todo: This shouldn't be a thing, but is currently required because this causes the mod to keep track of the bindables internally... - applyDifficulty(new BeatmapDifficulty - { - DrainRate = -1, - OverallDifficulty = -1 - }); } [Test] @@ -136,7 +125,6 @@ namespace osu.Game.Tests.Mods // ensure that ReadFromDifficulty doesn't pollute the values. var newDifficulty = difficulty.Clone(); - difficulty.ID = ++currentId; testMod.ReadFromDifficulty(difficulty); testMod.ApplyToDifficulty(newDifficulty); From bd4b3f5268936c8a440073679cda9645c77c524c Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 8 Jul 2021 15:42:29 +0900 Subject: [PATCH 0241/2442] Add catch selection blueprint visual test scene (without tests) --- .../Editor/CatchEditorTestSceneContainer.cs | 66 +++++++++++++++++++ .../CatchSelectionBlueprintTestScene.cs | 24 +++++++ .../TestSceneJuiceStreamSelectionBlueprint.cs | 38 +++++++++++ .../Visual/SelectionBlueprintTestScene.cs | 3 +- 4 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Catch.Tests/Editor/CatchEditorTestSceneContainer.cs create mode 100644 osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs create mode 100644 osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/CatchEditorTestSceneContainer.cs b/osu.Game.Rulesets.Catch.Tests/Editor/CatchEditorTestSceneContainer.cs new file mode 100644 index 0000000000..158c8edba5 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Editor/CatchEditorTestSceneContainer.cs @@ -0,0 +1,66 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Timing; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Edit; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.UI; +using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Catch.Tests.Editor +{ + public class CatchEditorTestSceneContainer : Container + { + [Cached(typeof(Playfield))] + public readonly ScrollingPlayfield Playfield; + + protected override Container Content { get; } + + public CatchEditorTestSceneContainer() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + Width = CatchPlayfield.WIDTH; + Height = 1000; + Padding = new MarginPadding + { + Bottom = 100 + }; + + InternalChildren = new Drawable[] + { + new ScrollingTestContainer(ScrollingDirection.Down) + { + TimeRange = 1000, + RelativeSizeAxes = Axes.Both, + Child = Playfield = new TestCatchPlayfield + { + RelativeSizeAxes = Axes.Both + } + }, + new PlayfieldBorder + { + PlayfieldBorderStyle = { Value = PlayfieldBorderStyle.Full }, + Clock = new FramedClock(new StopwatchClock(true)) + }, + Content = new Container + { + RelativeSizeAxes = Axes.Both + } + }; + } + + private class TestCatchPlayfield : CatchEditorPlayfield + { + public TestCatchPlayfield() + : base(new BeatmapDifficulty { CircleSize = 0 }) + { + } + } + } +} diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs b/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs new file mode 100644 index 0000000000..dcdc32145b --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Catch.Tests.Editor +{ + public abstract class CatchSelectionBlueprintTestScene : SelectionBlueprintTestScene + { + protected ScrollingHitObjectContainer HitObjectContainer => contentContainer.Playfield.HitObjectContainer; + + protected override Container Content => contentContainer; + + private readonly CatchEditorTestSceneContainer contentContainer; + + protected CatchSelectionBlueprintTestScene() + { + base.Content.Add(contentContainer = new CatchEditorTestSceneContainer()); + } + } +} diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs new file mode 100644 index 0000000000..1b96175020 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs @@ -0,0 +1,38 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Catch.Edit.Blueprints; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Tests.Editor +{ + public class TestSceneJuiceStreamSelectionBlueprint : CatchSelectionBlueprintTestScene + { + public TestSceneJuiceStreamSelectionBlueprint() + { + var hitObject = new JuiceStream + { + OriginalX = 100, + StartTime = 100, + Path = new SliderPath(PathType.PerfectCurve, new[] + { + Vector2.Zero, + new Vector2(200, 100), + new Vector2(0, 200), + }), + }; + var controlPoint = new ControlPointInfo(); + controlPoint.Add(0, new TimingControlPoint + { + BeatLength = 100 + }); + hitObject.ApplyDefaults(controlPoint, new BeatmapDifficulty { CircleSize = 0 }); + AddBlueprint(new JuiceStreamSelectionBlueprint(hitObject)); + } + } +} diff --git a/osu.Game/Tests/Visual/SelectionBlueprintTestScene.cs b/osu.Game/Tests/Visual/SelectionBlueprintTestScene.cs index dc12a4999d..c3fb3bfc17 100644 --- a/osu.Game/Tests/Visual/SelectionBlueprintTestScene.cs +++ b/osu.Game/Tests/Visual/SelectionBlueprintTestScene.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using JetBrains.Annotations; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Timing; @@ -23,7 +24,7 @@ namespace osu.Game.Tests.Visual }); } - protected void AddBlueprint(HitObjectSelectionBlueprint blueprint, DrawableHitObject drawableObject) + protected void AddBlueprint(HitObjectSelectionBlueprint blueprint, [CanBeNull] DrawableHitObject drawableObject = null) { Add(blueprint.With(d => { From 8da1335e5fe30a39fcc2b80ec5cc49eb51efed96 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 8 Jul 2021 15:51:46 +0900 Subject: [PATCH 0242/2442] Add catch placement blueprint visual test scenes (without tests) --- .../CatchPlacementBlueprintTestScene.cs | 45 +++++++++++++++++++ ...TestSceneBananaShowerPlacementBlueprint.cs | 28 ++++++++++++ .../TestSceneFruitPlacementBlueprint.cs | 19 ++++++++ .../Visual/PlacementBlueprintTestScene.cs | 2 +- 4 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Catch.Tests/Editor/CatchPlacementBlueprintTestScene.cs create mode 100644 osu.Game.Rulesets.Catch.Tests/Editor/TestSceneBananaShowerPlacementBlueprint.cs create mode 100644 osu.Game.Rulesets.Catch.Tests/Editor/TestSceneFruitPlacementBlueprint.cs diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/CatchPlacementBlueprintTestScene.cs b/osu.Game.Rulesets.Catch.Tests/Editor/CatchPlacementBlueprintTestScene.cs new file mode 100644 index 0000000000..12a8a97338 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Editor/CatchPlacementBlueprintTestScene.cs @@ -0,0 +1,45 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Timing; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Catch.Tests.Editor +{ + public abstract class CatchPlacementBlueprintTestScene : PlacementBlueprintTestScene + { + protected new ScrollingHitObjectContainer HitObjectContainer => contentContainer.Playfield.HitObjectContainer; + + protected override Container Content => contentContainer; + + private readonly CatchEditorTestSceneContainer contentContainer; + + protected CatchPlacementBlueprintTestScene() + { + base.Content.Add(contentContainer = new CatchEditorTestSceneContainer + { + Clock = new FramedClock(new ManualClock()) + }); + } + + // Unused because AddHitObject is overriden + protected override Container CreateHitObjectContainer() => new Container(); + + protected override void AddHitObject(DrawableHitObject hitObject) + { + contentContainer.Playfield.HitObjectContainer.Add(hitObject); + } + + protected override SnapResult SnapForBlueprint(PlacementBlueprint blueprint) + { + var result = base.SnapForBlueprint(blueprint); + result.Time = HitObjectContainer.TimeAtScreenSpacePosition(result.ScreenSpacePosition); + return result; + } + } +} diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneBananaShowerPlacementBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneBananaShowerPlacementBlueprint.cs new file mode 100644 index 0000000000..dd8a7490b5 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneBananaShowerPlacementBlueprint.cs @@ -0,0 +1,28 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Catch.Edit.Blueprints; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.Objects.Drawables; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; + +namespace osu.Game.Rulesets.Catch.Tests.Editor +{ + public class TestSceneBananaShowerPlacementBlueprint : CatchPlacementBlueprintTestScene + { + protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableBananaShower((BananaShower)hitObject); + + protected override PlacementBlueprint CreateBlueprint() => new BananaShowerPlacementBlueprint(); + + protected override void AddHitObject(DrawableHitObject hitObject) + { + // Create nested bananas (but positions are not randomized because beatmap processing is not done). + hitObject.HitObject.ApplyDefaults(new ControlPointInfo(), Beatmap.Value.BeatmapInfo.BaseDifficulty); + + base.AddHitObject(hitObject); + } + } +} diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneFruitPlacementBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneFruitPlacementBlueprint.cs new file mode 100644 index 0000000000..c1ce27f204 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneFruitPlacementBlueprint.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Catch.Edit.Blueprints; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.Objects.Drawables; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; + +namespace osu.Game.Rulesets.Catch.Tests.Editor +{ + public class TestSceneFruitPlacementBlueprint : CatchPlacementBlueprintTestScene + { + protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableFruit((Fruit)hitObject); + + protected override PlacementBlueprint CreateBlueprint() => new FruitPlacementBlueprint(); + } +} diff --git a/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs b/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs index 2dc77fa72a..130a39c33a 100644 --- a/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs +++ b/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs @@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual protected PlacementBlueprintTestScene() { - Add(HitObjectContainer = CreateHitObjectContainer().With(c => c.Clock = new FramedClock(new StopwatchClock()))); + base.Content.Add(HitObjectContainer = CreateHitObjectContainer().With(c => c.Clock = new FramedClock(new StopwatchClock()))); } [BackgroundDependencyLoader] From ae67409f41aae01d8a8408c2e0763f0668a03e1f Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 8 Jul 2021 16:12:02 +0900 Subject: [PATCH 0243/2442] Add a test of fruit placement blueprint --- .../CatchPlacementBlueprintTestScene.cs | 37 ++++++++++++++++++- .../TestSceneFruitPlacementBlueprint.cs | 25 +++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/CatchPlacementBlueprintTestScene.cs b/osu.Game.Rulesets.Catch.Tests/Editor/CatchPlacementBlueprintTestScene.cs index 12a8a97338..c99e6626e7 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/CatchPlacementBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/CatchPlacementBlueprintTestScene.cs @@ -1,18 +1,30 @@ // 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 NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; using osu.Framework.Timing; +using osu.Game.Rulesets.Catch.Edit.Blueprints.Components; +using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Tests.Visual; +using osuTK; +using osuTK.Input; namespace osu.Game.Rulesets.Catch.Tests.Editor { public abstract class CatchPlacementBlueprintTestScene : PlacementBlueprintTestScene { + protected const double TIME_SNAP = 100; + + protected DrawableCatchHitObject LastObject; + protected new ScrollingHitObjectContainer HitObjectContainer => contentContainer.Playfield.HitObjectContainer; protected override Container Content => contentContainer; @@ -27,18 +39,41 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor }); } + [SetUp] + public void Setup() => Schedule(() => + { + HitObjectContainer.Clear(); + ResetPlacement(); + LastObject = null; + }); + + protected void AddMoveStep(double time, float x) => AddStep($"move to time={time}, x={x}", () => + { + float y = HitObjectContainer.PositionAtTime(time); + Vector2 pos = HitObjectContainer.ToScreenSpace(new Vector2(x, y + HitObjectContainer.DrawHeight)); + InputManager.MoveMouseTo(pos); + }); + + protected void AddClickStep(MouseButton button) => AddStep($"click {button}", () => + { + InputManager.Click(button); + }); + + protected IEnumerable FruitOutlines => Content.ChildrenOfType(); + // Unused because AddHitObject is overriden protected override Container CreateHitObjectContainer() => new Container(); protected override void AddHitObject(DrawableHitObject hitObject) { + LastObject = (DrawableCatchHitObject)hitObject; contentContainer.Playfield.HitObjectContainer.Add(hitObject); } protected override SnapResult SnapForBlueprint(PlacementBlueprint blueprint) { var result = base.SnapForBlueprint(blueprint); - result.Time = HitObjectContainer.TimeAtScreenSpacePosition(result.ScreenSpacePosition); + result.Time = Math.Round(HitObjectContainer.TimeAtScreenSpacePosition(result.ScreenSpacePosition) / TIME_SNAP) * TIME_SNAP; return result; } } diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneFruitPlacementBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneFruitPlacementBlueprint.cs index c1ce27f204..4b1c45ae2f 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneFruitPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneFruitPlacementBlueprint.cs @@ -1,12 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; +using NUnit.Framework; +using osu.Framework.Utils; using osu.Game.Rulesets.Catch.Edit.Blueprints; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawables; +using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; +using osuTK.Input; namespace osu.Game.Rulesets.Catch.Tests.Editor { @@ -15,5 +20,25 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableFruit((Fruit)hitObject); protected override PlacementBlueprint CreateBlueprint() => new FruitPlacementBlueprint(); + + [Test] + public void TestFruitPlacementPosition() + { + const double time = 300; + const float x = CatchPlayfield.CENTER_X; + + AddMoveStep(time, x); + AddClickStep(MouseButton.Left); + + AddAssert("outline position is correct", () => + { + var outline = FruitOutlines.Single(); + return Precision.AlmostEquals(outline.X, x) && + Precision.AlmostEquals(outline.Y, HitObjectContainer.PositionAtTime(time)); + }); + + AddAssert("fruit time is correct", () => Precision.AlmostEquals(LastObject.StartTimeBindable.Value, time)); + AddAssert("fruit position is correct", () => Precision.AlmostEquals(LastObject.X, x)); + } } } From 68116aa042a9062547b1fff4a8c9d81fd0e05464 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 8 Jul 2021 16:17:09 +0900 Subject: [PATCH 0244/2442] Fix placement blueprint animation is not running in test scene --- .../Editor/CatchPlacementBlueprintTestScene.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/CatchPlacementBlueprintTestScene.cs b/osu.Game.Rulesets.Catch.Tests/Editor/CatchPlacementBlueprintTestScene.cs index c99e6626e7..1d30ae34cd 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/CatchPlacementBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/CatchPlacementBlueprintTestScene.cs @@ -33,10 +33,9 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor protected CatchPlacementBlueprintTestScene() { - base.Content.Add(contentContainer = new CatchEditorTestSceneContainer - { - Clock = new FramedClock(new ManualClock()) - }); + base.Content.Add(contentContainer = new CatchEditorTestSceneContainer()); + + contentContainer.Playfield.Clock = new FramedClock(new ManualClock()); } [SetUp] From 4ac7d629d7fcd739e2bfac5175113e515ad33157 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 8 Jul 2021 16:36:41 +0900 Subject: [PATCH 0245/2442] Expose current placement blueprint --- osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs b/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs index 130a39c33a..42cf826bd4 100644 --- a/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs +++ b/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs @@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual public abstract class PlacementBlueprintTestScene : OsuManualInputManagerTestScene, IPlacementHandler { protected readonly Container HitObjectContainer; - private PlacementBlueprint currentBlueprint; + protected PlacementBlueprint CurrentBlueprint { get; private set; } protected PlacementBlueprintTestScene() { @@ -63,9 +63,9 @@ namespace osu.Game.Tests.Visual protected void ResetPlacement() { - if (currentBlueprint != null) - Remove(currentBlueprint); - Add(currentBlueprint = CreateBlueprint()); + if (CurrentBlueprint != null) + Remove(CurrentBlueprint); + Add(CurrentBlueprint = CreateBlueprint()); } public void Delete(HitObject hitObject) @@ -76,7 +76,7 @@ namespace osu.Game.Tests.Visual { base.Update(); - currentBlueprint.UpdateTimeAndPosition(SnapForBlueprint(currentBlueprint)); + CurrentBlueprint.UpdateTimeAndPosition(SnapForBlueprint(CurrentBlueprint)); } protected virtual SnapResult SnapForBlueprint(PlacementBlueprint blueprint) => From 8ac3015f14dcba91ddb413687e191ac3da7346f5 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 8 Jul 2021 16:36:44 +0900 Subject: [PATCH 0246/2442] Add tests of banana shower placement blueprint --- ...TestSceneBananaShowerPlacementBlueprint.cs | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneBananaShowerPlacementBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneBananaShowerPlacementBlueprint.cs index dd8a7490b5..3f9db5cbf6 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneBananaShowerPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneBananaShowerPlacementBlueprint.cs @@ -1,13 +1,19 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; +using NUnit.Framework; +using osu.Framework.Testing; +using osu.Framework.Utils; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Catch.Edit.Blueprints; +using osu.Game.Rulesets.Catch.Edit.Blueprints.Components; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; +using osuTK.Input; namespace osu.Game.Rulesets.Catch.Tests.Editor { @@ -24,5 +30,58 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor base.AddHitObject(hitObject); } + + [Test] + public void TestBasicPlacement() + { + const double start_time = 100; + const double end_time = 500; + + AddMoveStep(start_time, 0); + AddClickStep(MouseButton.Left); + AddMoveStep(end_time, 0); + AddClickStep(MouseButton.Right); + AddAssert("banana shower is placed", () => LastObject is DrawableBananaShower); + AddAssert("start time is correct", () => Precision.AlmostEquals(LastObject.HitObject.StartTime, start_time)); + AddAssert("end time is correct", () => Precision.AlmostEquals(LastObject.HitObject.GetEndTime(), end_time)); + } + + [Test] + public void TestReversePlacement() + { + const double start_time = 100; + const double end_time = 500; + + AddMoveStep(end_time, 0); + AddClickStep(MouseButton.Left); + AddMoveStep(start_time, 0); + AddClickStep(MouseButton.Right); + AddAssert("start time is correct", () => Precision.AlmostEquals(LastObject.HitObject.StartTime, start_time)); + AddAssert("end time is correct", () => Precision.AlmostEquals(LastObject.HitObject.GetEndTime(), end_time)); + } + + [Test] + public void TestFinishWithZeroDuration() + { + AddMoveStep(100, 0); + AddClickStep(MouseButton.Left); + AddClickStep(MouseButton.Right); + AddAssert("banana shower is not placed", () => LastObject == null); + AddAssert("state is waiting", () => CurrentBlueprint?.PlacementActive == PlacementBlueprint.PlacementState.Waiting); + } + + [Test] + public void TestOpacity() + { + AddMoveStep(100, 0); + AddClickStep(MouseButton.Left); + AddAssert("outline is semitransparent", () => timeSpanOutline.Alpha < 1); + AddMoveStep(200, 0); + AddAssert("outline is opaque", () => Precision.AlmostEquals(timeSpanOutline.Alpha, 1)); + AddMoveStep(100, 0); + AddAssert("outline is semitransparent", () => timeSpanOutline.Alpha < 1); + } + + private TimeSpanOutline timeSpanOutline => Content.ChildrenOfType().Single(); } } From 25b94061fd8b414deaefb1db961d0655e8b350dd Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 8 Jul 2021 16:40:18 +0900 Subject: [PATCH 0247/2442] Fix assert step not waiting for transformation --- .../Editor/TestSceneBananaShowerPlacementBlueprint.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneBananaShowerPlacementBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneBananaShowerPlacementBlueprint.cs index 3f9db5cbf6..e3811b7669 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneBananaShowerPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneBananaShowerPlacementBlueprint.cs @@ -75,11 +75,11 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor { AddMoveStep(100, 0); AddClickStep(MouseButton.Left); - AddAssert("outline is semitransparent", () => timeSpanOutline.Alpha < 1); + AddUntilStep("outline is semitransparent", () => Precision.DefinitelyBigger(1, timeSpanOutline.Alpha)); AddMoveStep(200, 0); - AddAssert("outline is opaque", () => Precision.AlmostEquals(timeSpanOutline.Alpha, 1)); + AddUntilStep("outline is opaque", () => Precision.AlmostEquals(timeSpanOutline.Alpha, 1)); AddMoveStep(100, 0); - AddAssert("outline is semitransparent", () => timeSpanOutline.Alpha < 1); + AddUntilStep("outline is semitransparent", () => Precision.DefinitelyBigger(1, timeSpanOutline.Alpha)); } private TimeSpanOutline timeSpanOutline => Content.ChildrenOfType().Single(); From fcee69ffe6c479f1ca226612b56cf4391d0f908b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Jul 2021 15:52:49 +0900 Subject: [PATCH 0248/2442] Fix `ShowsDefaultIndicator` not actually being consumed --- osu.Game/Overlays/Settings/SettingsItem.cs | 23 +++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 15a0a42d31..c60ad020f0 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -101,10 +101,10 @@ namespace osu.Game.Overlays.Settings public event Action SettingChanged; + private readonly RestoreDefaultValueButton restoreDefaultButton; + protected SettingsItem() { - RestoreDefaultValueButton restoreDefaultButton; - RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Padding = new MarginPadding { Right = SettingsPanel.CONTENT_MARGINS }; @@ -126,14 +126,19 @@ namespace osu.Game.Overlays.Settings // all bindable logic is in constructor intentionally to support "CreateSettingsControls" being used in a context it is // never loaded, but requires bindable storage. - if (controlWithCurrent != null) - { - controlWithCurrent.Current.ValueChanged += _ => SettingChanged?.Invoke(); - controlWithCurrent.Current.DisabledChanged += _ => updateDisabled(); + if (controlWithCurrent == null) + throw new ArgumentException(@$"Control created via {nameof(CreateControl)} must implement {nameof(IHasCurrentValue)}"); - if (ShowsDefaultIndicator) - restoreDefaultButton.Current = controlWithCurrent.Current; - } + controlWithCurrent.Current.ValueChanged += _ => SettingChanged?.Invoke(); + controlWithCurrent.Current.DisabledChanged += _ => updateDisabled(); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + if (ShowsDefaultIndicator) + restoreDefaultButton.Current = controlWithCurrent.Current; } private void updateDisabled() From c4313d6e96b687befbbc9462c4320c3185e24dbf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Jul 2021 15:53:49 +0900 Subject: [PATCH 0249/2442] Initial implementation of new flow (only working for approach rate) --- .../Mods/OsuModDifficultyAdjust.cs | 4 +- .../TestSceneModDifficultyAdjustSettings.cs | 74 ++++++++++++++ .../Mods/ApproachRateSettingsControl.cs | 20 ++++ .../Mods/DifficultyAdjustSettingsControl.cs | 97 +++++++++++++++++++ osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs | 4 +- 5 files changed, 195 insertions(+), 4 deletions(-) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs create mode 100644 osu.Game/Rulesets/Mods/ApproachRateSettingsControl.cs create mode 100644 osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs index 54e30c56a3..82c4a6fd56 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModDifficultyAdjust : ModDifficultyAdjust { - [SettingSource("Circle Size", "Override a beatmap's set CS.", FIRST_SETTING_ORDER - 1)] + [SettingSource("Circle Size", "Override a beatmap's set CS.", FIRST_SETTING_ORDER - 1, SettingControlType = typeof(DifficultyAdjustSettingsControl))] public Bindable CircleSize { get; } = new Bindable { /* @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Mods */ }; - [SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1)] + [SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1, SettingControlType = typeof(ApproachRateSettingsControl))] public Bindable ApproachRate { get; } = new Bindable { /* diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs new file mode 100644 index 0000000000..b1ad92273c --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs @@ -0,0 +1,74 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Configuration; +using osu.Game.Rulesets.Osu.Mods; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneModDifficultyAdjustSettings : OsuManualInputManagerTestScene + { + [SetUpSteps] + public void SetUpSteps() + { + AddStep("create difficulty adjust", () => + { + var modDifficultyAdjust = new OsuModDifficultyAdjust(); + + Child = new Container + { + Size = new Vector2(300), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Children = new Drawable[] + { + new Box + { + Colour = Color4.Black, + RelativeSizeAxes = Axes.Both, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ChildrenEnumerable = modDifficultyAdjust.CreateSettingsControls(), + }, + } + }; + }); + + setBeatmapWithDifficultyParameters(5); + setBeatmapWithDifficultyParameters(8); + } + + [Test] + public void TestBasic() + { + } + + private void setBeatmapWithDifficultyParameters(float value) + { + AddStep($"set beatmap with all {value}", () => Beatmap.Value = CreateWorkingBeatmap(new Beatmap() + { + BeatmapInfo = new BeatmapInfo + { + BaseDifficulty = new BeatmapDifficulty + { + OverallDifficulty = value, + CircleSize = value, + DrainRate = value, + ApproachRate = value, + } + } + })); + } + } +} diff --git a/osu.Game/Rulesets/Mods/ApproachRateSettingsControl.cs b/osu.Game/Rulesets/Mods/ApproachRateSettingsControl.cs new file mode 100644 index 0000000000..15a94cb271 --- /dev/null +++ b/osu.Game/Rulesets/Mods/ApproachRateSettingsControl.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Beatmaps; + +namespace osu.Game.Rulesets.Mods +{ + public class ApproachRateSettingsControl : DifficultyAdjustSettingsControl + { + public ApproachRateSettingsControl() + { + CurrentNumber.Precision = 0.1f; + + CurrentNumber.MinValue = 0; + CurrentNumber.MaxValue = 10; + } + + protected override float UpdateFromDifficulty(BeatmapDifficulty difficulty) => difficulty.ApproachRate; + } +} diff --git a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs new file mode 100644 index 0000000000..2b437370ea --- /dev/null +++ b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs @@ -0,0 +1,97 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Beatmaps; +using osu.Game.Overlays.Settings; + +namespace osu.Game.Rulesets.Mods +{ + // TODO: make abstract once we finish making each implementation. + public class DifficultyAdjustSettingsControl : SettingsItem + { + [Resolved] + private IBindable beatmap { get; set; } + + protected readonly BindableNumber CurrentNumber = new BindableNumber + { + // TODO: these need to be pulled out of the main bindable. + MinValue = 0, + MaxValue = 10, + }; + + protected override Drawable CreateControl() => new ControlDrawable(CurrentNumber); + + private bool isInternalChange; + + protected override void LoadComplete() + { + base.LoadComplete(); + + beatmap.BindValueChanged(b => + { + updateFromDifficulty(); + }, true); + + Current.BindValueChanged(current => + { + if (current.NewValue == null) + updateFromDifficulty(); + }); + + CurrentNumber.BindValueChanged(number => + { + if (!isInternalChange) + Current.Value = number.NewValue; + }); + } + + private void updateFromDifficulty() + { + var difficulty = beatmap.Value.BeatmapInfo.BaseDifficulty; + + if (difficulty == null) + return; + + if (Current.Value == null) + { + isInternalChange = true; + CurrentNumber.Value = UpdateFromDifficulty(difficulty); + isInternalChange = false; + } + } + + // TODO: make abstract + protected virtual float UpdateFromDifficulty(BeatmapDifficulty difficulty) => 0; + + private class ControlDrawable : CompositeDrawable, IHasCurrentValue + { + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + public Bindable Current + { + get => current.Current; + set => current.Current = value; + } + + public ControlDrawable(BindableNumber currentNumber) + { + InternalChildren = new Drawable[] + { + new SettingsSlider + { + ShowsDefaultIndicator = false, + Current = currentNumber, + } + }; + + AutoSizeAxes = Axes.Y; + RelativeSizeAxes = Axes.X; + } + } + } +} diff --git a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs index e98bd14720..4b68d9cc3f 100644 --- a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Mods protected const int LAST_SETTING_ORDER = 2; - [SettingSource("HP Drain", "Override a beatmap's set HP.", FIRST_SETTING_ORDER)] + [SettingSource("HP Drain", "Override a beatmap's set HP.", FIRST_SETTING_ORDER, SettingControlType = typeof(DifficultyAdjustSettingsControl))] public Bindable DrainRate { get; } = new Bindable { /* @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mods */ }; - [SettingSource("Accuracy", "Override a beatmap's set OD.", LAST_SETTING_ORDER)] + [SettingSource("Accuracy", "Override a beatmap's set OD.", LAST_SETTING_ORDER, SettingControlType = typeof(DifficultyAdjustSettingsControl))] public Bindable OverallDifficulty { get; } = new Bindable { /* From a6e94dd4918e70e1e5040b5523ec0958e1fb0355 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Jul 2021 16:40:32 +0900 Subject: [PATCH 0250/2442] Add back extended limits support --- .../Mods/CatchModDifficultyAdjust.cs | 17 ++-- .../Mods/OsuModDifficultyAdjust.cs | 20 ++--- .../Mods/TaikoModDifficultyAdjust.cs | 9 +- .../TestSceneModDifficultyAdjustSettings.cs | 2 +- .../Mods/ApproachRateSettingsControl.cs | 8 -- .../Mods/DifficultyAdjustSettingsControl.cs | 21 +++-- osu.Game/Rulesets/Mods/DifficultyBindable.cs | 84 +++++++++++++++++++ osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs | 81 ++---------------- 8 files changed, 121 insertions(+), 121 deletions(-) create mode 100644 osu.Game/Rulesets/Mods/DifficultyBindable.cs diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs index aaa426cd03..947edb5dd9 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs @@ -13,35 +13,28 @@ namespace osu.Game.Rulesets.Catch.Mods public class CatchModDifficultyAdjust : ModDifficultyAdjust, IApplicableToBeatmapProcessor { [SettingSource("Circle Size", "Override a beatmap's set CS.", FIRST_SETTING_ORDER - 1)] - public Bindable CircleSize { get; } = new Bindable + public DifficultyBindable CircleSize { get; } = new DifficultyBindable { - /* Precision = 0.1f, MinValue = 1, MaxValue = 10, - */ }; [SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1)] - public Bindable ApproachRate { get; } = new Bindable + public DifficultyBindable ApproachRate { get; } = new DifficultyBindable { - /* Precision = 0.1f, MinValue = 1, MaxValue = 10, - */ }; [SettingSource("Spicy Patterns", "Adjust the patterns as if Hard Rock is enabled.")] public BindableBool HardRockOffsets { get; } = new BindableBool(); - protected override void ApplyLimits(bool extended) + public CatchModDifficultyAdjust() { - base.ApplyLimits(extended); - - // TODO: reimplement - // CircleSize.MaxValue = extended ? 11 : 10; - // ApproachRate.MaxValue = extended ? 11 : 10; + CircleSize.ExtendedLimits.BindTo(ExtendedLimits); + ApproachRate.ExtendedLimits.BindTo(ExtendedLimits); } public override string SettingDescription diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs index 82c4a6fd56..403ec2c33d 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; -using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets.Mods; @@ -12,32 +11,27 @@ namespace osu.Game.Rulesets.Osu.Mods public class OsuModDifficultyAdjust : ModDifficultyAdjust { [SettingSource("Circle Size", "Override a beatmap's set CS.", FIRST_SETTING_ORDER - 1, SettingControlType = typeof(DifficultyAdjustSettingsControl))] - public Bindable CircleSize { get; } = new Bindable + public DifficultyBindable CircleSize { get; } = new DifficultyBindable { - /* Precision = 0.1f, MinValue = 0, MaxValue = 10, - */ + ExtendedMaxValue = 11, }; [SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1, SettingControlType = typeof(ApproachRateSettingsControl))] - public Bindable ApproachRate { get; } = new Bindable + public DifficultyBindable ApproachRate { get; } = new DifficultyBindable { - /* Precision = 0.1f, MinValue = 0, MaxValue = 10, - */ + ExtendedMaxValue = 11, }; - protected override void ApplyLimits(bool extended) + public OsuModDifficultyAdjust() { - base.ApplyLimits(extended); - - // TODO: reimplement - // CircleSize.MaxValue = extended ? 11 : 10; - // ApproachRate.MaxValue = extended ? 11 : 10; + CircleSize.ExtendedLimits.BindTo(ExtendedLimits); + ApproachRate.ExtendedLimits.BindTo(ExtendedLimits); } public override string SettingDescription diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs index 902ccdc14e..110a7eebc8 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; -using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets.Mods; @@ -11,14 +10,12 @@ namespace osu.Game.Rulesets.Taiko.Mods { public class TaikoModDifficultyAdjust : ModDifficultyAdjust { - [SettingSource("Scroll Speed", "Adjust a beatmap's set scroll speed", LAST_SETTING_ORDER + 1)] - public Bindable ScrollSpeed { get; } = new Bindable + [SettingSource("Scroll Speed", "Adjust a beatmap's set scroll speed", LAST_SETTING_ORDER + 1, SettingControlType = typeof(DifficultyAdjustSettingsControl))] + public DifficultyBindable ScrollSpeed { get; } = new DifficultyBindable { - /* Precision = 0.05f, MinValue = 0.25f, MaxValue = 4, - */ }; public override string SettingDescription @@ -39,7 +36,7 @@ namespace osu.Game.Rulesets.Taiko.Mods { base.ApplySettings(difficulty); - if (ScrollSpeed.Value != null) difficulty.SliderMultiplier = ScrollSpeed.Value.Value; + if (ScrollSpeed.Value != null) difficulty.SliderMultiplier *= ScrollSpeed.Value.Value; } } } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs index b1ad92273c..7d982a55cb 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs @@ -56,7 +56,7 @@ namespace osu.Game.Tests.Visual.UserInterface private void setBeatmapWithDifficultyParameters(float value) { - AddStep($"set beatmap with all {value}", () => Beatmap.Value = CreateWorkingBeatmap(new Beatmap() + AddStep($"set beatmap with all {value}", () => Beatmap.Value = CreateWorkingBeatmap(new Beatmap { BeatmapInfo = new BeatmapInfo { diff --git a/osu.Game/Rulesets/Mods/ApproachRateSettingsControl.cs b/osu.Game/Rulesets/Mods/ApproachRateSettingsControl.cs index 15a94cb271..18773afb30 100644 --- a/osu.Game/Rulesets/Mods/ApproachRateSettingsControl.cs +++ b/osu.Game/Rulesets/Mods/ApproachRateSettingsControl.cs @@ -7,14 +7,6 @@ namespace osu.Game.Rulesets.Mods { public class ApproachRateSettingsControl : DifficultyAdjustSettingsControl { - public ApproachRateSettingsControl() - { - CurrentNumber.Precision = 0.1f; - - CurrentNumber.MinValue = 0; - CurrentNumber.MaxValue = 10; - } - protected override float UpdateFromDifficulty(BeatmapDifficulty difficulty) => difficulty.ApproachRate; } } diff --git a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs index 2b437370ea..1aede3425a 100644 --- a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs +++ b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs @@ -17,17 +17,26 @@ namespace osu.Game.Rulesets.Mods [Resolved] private IBindable beatmap { get; set; } - protected readonly BindableNumber CurrentNumber = new BindableNumber - { - // TODO: these need to be pulled out of the main bindable. - MinValue = 0, - MaxValue = 10, - }; + protected readonly BindableNumber CurrentNumber = new BindableNumber(); protected override Drawable CreateControl() => new ControlDrawable(CurrentNumber); private bool isInternalChange; + private DifficultyBindable difficultyBindable; + + public override Bindable Current + { + get => base.Current; + set + { + // intercept and extract the DifficultyBindable. + difficultyBindable = (DifficultyBindable)value; + CurrentNumber.BindTo(difficultyBindable.CurrentNumber); + base.Current = value; + } + } + protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Rulesets/Mods/DifficultyBindable.cs b/osu.Game/Rulesets/Mods/DifficultyBindable.cs new file mode 100644 index 0000000000..d721154392 --- /dev/null +++ b/osu.Game/Rulesets/Mods/DifficultyBindable.cs @@ -0,0 +1,84 @@ +// 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 osu.Framework.Bindables; + +namespace osu.Game.Rulesets.Mods +{ + public class DifficultyBindable : Bindable + { + /// + /// Whether the extended limits should be applied to this bindable. + /// + public BindableBool ExtendedLimits { get; } = new BindableBool(); + + /// + /// An internal numeric bindable to hold and propagate min/max/precision. + /// The value of this bindable should not be set. + /// + public readonly BindableFloat CurrentNumber = new BindableFloat + { + MinValue = 0, + MaxValue = 10, + }; + + public float Precision + { + set => CurrentNumber.Precision = value; + } + + public float MinValue + { + set => CurrentNumber.MinValue = value; + } + + private float maxValue; + + public float MaxValue + { + set + { + if (value == maxValue) + return; + + maxValue = value; + updateMaxValue(); + } + } + + private float? extendedMaxValue; + + /// + /// The maximum value to be used when extended limits are applied. + /// + public float? ExtendedMaxValue + { + set + { + if (value == extendedMaxValue) + return; + + extendedMaxValue = value; + updateMaxValue(); + } + } + + public DifficultyBindable() + { + ExtendedLimits.BindValueChanged(_ => updateMaxValue()); + + BindValueChanged(val => + { + // Ensure that in the case serialisation runs in the wrong order (and limit extensions aren't applied yet) the deserialised value is still propagated. + if (val.NewValue != null) + CurrentNumber.MaxValue = MathF.Max(CurrentNumber.MaxValue, val.NewValue.Value); + }); + } + + private void updateMaxValue() + { + CurrentNumber.MaxValue = ExtendedLimits.Value && extendedMaxValue != null ? extendedMaxValue.Value : maxValue; + } + } +} diff --git a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs index 4b68d9cc3f..d636f22dea 100644 --- a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs @@ -33,23 +33,21 @@ namespace osu.Game.Rulesets.Mods protected const int LAST_SETTING_ORDER = 2; [SettingSource("HP Drain", "Override a beatmap's set HP.", FIRST_SETTING_ORDER, SettingControlType = typeof(DifficultyAdjustSettingsControl))] - public Bindable DrainRate { get; } = new Bindable + public DifficultyBindable DrainRate { get; } = new DifficultyBindable { - /* Precision = 0.1f, MinValue = 0, MaxValue = 10, - */ + ExtendedMaxValue = 11, }; [SettingSource("Accuracy", "Override a beatmap's set OD.", LAST_SETTING_ORDER, SettingControlType = typeof(DifficultyAdjustSettingsControl))] - public Bindable OverallDifficulty { get; } = new Bindable + public DifficultyBindable OverallDifficulty { get; } = new DifficultyBindable { - /* Precision = 0.1f, MinValue = 0, MaxValue = 10, - */ + ExtendedMaxValue = 11, }; [SettingSource("Extended Limits", "Adjust difficulty beyond sane limits.")] @@ -57,18 +55,8 @@ namespace osu.Game.Rulesets.Mods protected ModDifficultyAdjust() { - ExtendedLimits.BindValueChanged(extend => ApplyLimits(extend.NewValue)); - } - - /// - /// Changes the difficulty adjustment limits. Occurs when the value of is changed. - /// - /// Whether limits should extend beyond sane ranges. - protected virtual void ApplyLimits(bool extended) - { - // TODO: reimplement - // DrainRate.MaxValue = extended ? 11 : 10; - // OverallDifficulty.MaxValue = extended ? 11 : 10; + OverallDifficulty.ExtendedLimits.BindTo(ExtendedLimits); + DrainRate.ExtendedLimits.BindTo(ExtendedLimits); } public override string SettingDescription @@ -101,62 +89,5 @@ namespace osu.Game.Rulesets.Mods if (DrainRate.Value != null) difficulty.DrainRate = DrainRate.Value.Value; if (OverallDifficulty.Value != null) difficulty.OverallDifficulty = OverallDifficulty.Value.Value; } - - /// - /// A that extends its min/max values to support any assigned value. - /// - protected class BindableDoubleWithLimitExtension : BindableDouble - { - public override double Value - { - get => base.Value; - set - { - if (value < MinValue) - MinValue = value; - if (value > MaxValue) - MaxValue = value; - base.Value = value; - } - } - } - - /// - /// A that extends its min/max values to support any assigned value. - /// - protected class BindableFloatWithLimitExtension : BindableFloat - { - public override float Value - { - get => base.Value; - set - { - if (value < MinValue) - MinValue = value; - if (value > MaxValue) - MaxValue = value; - base.Value = value; - } - } - } - - /// - /// A that extends its min/max values to support any assigned value. - /// - protected class BindableIntWithLimitExtension : BindableInt - { - public override int Value - { - get => base.Value; - set - { - if (value < MinValue) - MinValue = value; - if (value > MaxValue) - MaxValue = value; - base.Value = value; - } - } - } } } From bd7c3345881f312d07074c4676f73f8fc532affb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Jul 2021 16:56:16 +0900 Subject: [PATCH 0251/2442] Avoid the need for per-settings control classes --- .../Mods/CatchModDifficultyAdjust.cs | 4 ++++ osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs | 4 +++- .../Mods/TaikoModDifficultyAdjust.cs | 1 + .../Rulesets/Mods/ApproachRateSettingsControl.cs | 12 ------------ .../Rulesets/Mods/DifficultyAdjustSettingsControl.cs | 6 +----- osu.Game/Rulesets/Mods/DifficultyBindable.cs | 6 ++++++ osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs | 2 ++ 7 files changed, 17 insertions(+), 18 deletions(-) delete mode 100644 osu.Game/Rulesets/Mods/ApproachRateSettingsControl.cs diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs index 947edb5dd9..80b5244c34 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs @@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Catch.Mods Precision = 0.1f, MinValue = 1, MaxValue = 10, + ExtendedMaxValue = 11, + ReadFromDifficulty = diff => diff.CircleSize, }; [SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1)] @@ -26,6 +28,8 @@ namespace osu.Game.Rulesets.Catch.Mods Precision = 0.1f, MinValue = 1, MaxValue = 10, + ExtendedMaxValue = 11, + ReadFromDifficulty = diff => diff.ApproachRate, }; [SettingSource("Spicy Patterns", "Adjust the patterns as if Hard Rock is enabled.")] diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs index 403ec2c33d..d93b097663 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs @@ -17,15 +17,17 @@ namespace osu.Game.Rulesets.Osu.Mods MinValue = 0, MaxValue = 10, ExtendedMaxValue = 11, + ReadFromDifficulty = diff => diff.CircleSize, }; - [SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1, SettingControlType = typeof(ApproachRateSettingsControl))] + [SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1, SettingControlType = typeof(DifficultyAdjustSettingsControl))] public DifficultyBindable ApproachRate { get; } = new DifficultyBindable { Precision = 0.1f, MinValue = 0, MaxValue = 10, ExtendedMaxValue = 11, + ReadFromDifficulty = diff => diff.ApproachRate, }; public OsuModDifficultyAdjust() diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs index 110a7eebc8..ace105b21c 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs @@ -16,6 +16,7 @@ namespace osu.Game.Rulesets.Taiko.Mods Precision = 0.05f, MinValue = 0.25f, MaxValue = 4, + ReadFromDifficulty = _ => 1, }; public override string SettingDescription diff --git a/osu.Game/Rulesets/Mods/ApproachRateSettingsControl.cs b/osu.Game/Rulesets/Mods/ApproachRateSettingsControl.cs deleted file mode 100644 index 18773afb30..0000000000 --- a/osu.Game/Rulesets/Mods/ApproachRateSettingsControl.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Beatmaps; - -namespace osu.Game.Rulesets.Mods -{ - public class ApproachRateSettingsControl : DifficultyAdjustSettingsControl - { - protected override float UpdateFromDifficulty(BeatmapDifficulty difficulty) => difficulty.ApproachRate; - } -} diff --git a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs index 1aede3425a..ef2b88846a 100644 --- a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs +++ b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs @@ -11,7 +11,6 @@ using osu.Game.Overlays.Settings; namespace osu.Game.Rulesets.Mods { - // TODO: make abstract once we finish making each implementation. public class DifficultyAdjustSettingsControl : SettingsItem { [Resolved] @@ -69,14 +68,11 @@ namespace osu.Game.Rulesets.Mods if (Current.Value == null) { isInternalChange = true; - CurrentNumber.Value = UpdateFromDifficulty(difficulty); + CurrentNumber.Value = difficultyBindable.ReadFromDifficulty(difficulty); isInternalChange = false; } } - // TODO: make abstract - protected virtual float UpdateFromDifficulty(BeatmapDifficulty difficulty) => 0; - private class ControlDrawable : CompositeDrawable, IHasCurrentValue { private readonly BindableWithCurrent current = new BindableWithCurrent(); diff --git a/osu.Game/Rulesets/Mods/DifficultyBindable.cs b/osu.Game/Rulesets/Mods/DifficultyBindable.cs index d721154392..7b01b1e0c7 100644 --- a/osu.Game/Rulesets/Mods/DifficultyBindable.cs +++ b/osu.Game/Rulesets/Mods/DifficultyBindable.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Bindables; +using osu.Game.Beatmaps; namespace osu.Game.Rulesets.Mods { @@ -23,6 +24,11 @@ namespace osu.Game.Rulesets.Mods MaxValue = 10, }; + /// + /// A function that can extract the current value of this setting from a beatmap difficulty for display purposes. + /// + public Func ReadFromDifficulty; + public float Precision { set => CurrentNumber.Precision = value; diff --git a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs index d636f22dea..06bf7d9a6b 100644 --- a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs @@ -39,6 +39,7 @@ namespace osu.Game.Rulesets.Mods MinValue = 0, MaxValue = 10, ExtendedMaxValue = 11, + ReadFromDifficulty = diff => diff.DrainRate, }; [SettingSource("Accuracy", "Override a beatmap's set OD.", LAST_SETTING_ORDER, SettingControlType = typeof(DifficultyAdjustSettingsControl))] @@ -48,6 +49,7 @@ namespace osu.Game.Rulesets.Mods MinValue = 0, MaxValue = 10, ExtendedMaxValue = 11, + ReadFromDifficulty = diff => diff.OverallDifficulty, }; [SettingSource("Extended Limits", "Adjust difficulty beyond sane limits.")] From 88b00123f6efe28c876d954b3a6957ad6febf2b9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Jul 2021 17:00:55 +0900 Subject: [PATCH 0252/2442] Use existing reflection methods to avoid manual binding of `ExtendedLimits` --- osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs | 6 ------ osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs | 6 ------ osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs | 7 +++++-- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs index 80b5244c34..8686627c2a 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs @@ -35,12 +35,6 @@ namespace osu.Game.Rulesets.Catch.Mods [SettingSource("Spicy Patterns", "Adjust the patterns as if Hard Rock is enabled.")] public BindableBool HardRockOffsets { get; } = new BindableBool(); - public CatchModDifficultyAdjust() - { - CircleSize.ExtendedLimits.BindTo(ExtendedLimits); - ApproachRate.ExtendedLimits.BindTo(ExtendedLimits); - } - public override string SettingDescription { get diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs index d93b097663..983216dfa1 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs @@ -30,12 +30,6 @@ namespace osu.Game.Rulesets.Osu.Mods ReadFromDifficulty = diff => diff.ApproachRate, }; - public OsuModDifficultyAdjust() - { - CircleSize.ExtendedLimits.BindTo(ExtendedLimits); - ApproachRate.ExtendedLimits.BindTo(ExtendedLimits); - } - public override string SettingDescription { get diff --git a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs index 06bf7d9a6b..d9d305d457 100644 --- a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs @@ -57,8 +57,11 @@ namespace osu.Game.Rulesets.Mods protected ModDifficultyAdjust() { - OverallDifficulty.ExtendedLimits.BindTo(ExtendedLimits); - DrainRate.ExtendedLimits.BindTo(ExtendedLimits); + foreach (var (_, property) in this.GetOrderedSettingsSourceProperties()) + { + if (property.GetValue(this) is DifficultyBindable diffAdjustBindable) + diffAdjustBindable.ExtendedLimits.BindTo(ExtendedLimits); + } } public override string SettingDescription From 533db01cc0782e3e31465e04efe1d19ce5bb189d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Jul 2021 17:14:51 +0900 Subject: [PATCH 0253/2442] Add comprehensive tests of difficulty adjust settings --- .../TestSceneModDifficultyAdjustSettings.cs | 143 +++++++++++++++++- 1 file changed, 137 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs index 7d982a55cb..d0a02a6c2d 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -8,6 +9,8 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Overlays.Settings; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; using osuTK; using osuTK.Graphics; @@ -16,12 +19,14 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneModDifficultyAdjustSettings : OsuManualInputManagerTestScene { + private OsuModDifficultyAdjust modDifficultyAdjust; + [SetUpSteps] public void SetUpSteps() { - AddStep("create difficulty adjust", () => + AddStep("create control", () => { - var modDifficultyAdjust = new OsuModDifficultyAdjust(); + modDifficultyAdjust = new OsuModDifficultyAdjust(); Child = new Container { @@ -44,14 +49,140 @@ namespace osu.Game.Tests.Visual.UserInterface } }; }); - - setBeatmapWithDifficultyParameters(5); - setBeatmapWithDifficultyParameters(8); } [Test] - public void TestBasic() + public void TestFollowsBeatmapDefaultsVisually() { + setBeatmapWithDifficultyParameters(5); + + checkSliderAtValue("Circle Size", 5); + checkBindableAtValue("Circle Size", null); + + setBeatmapWithDifficultyParameters(8); + + checkSliderAtValue("Circle Size", 8); + checkBindableAtValue("Circle Size", null); + } + + [Test] + public void TestOutOfRangeValueStillApplied() + { + AddStep("set override cs to 11", () => modDifficultyAdjust.CircleSize.Value = 11); + + checkSliderAtValue("Circle Size", 11); + checkBindableAtValue("Circle Size", 11); + + // this is a no-op, just showing that it won't reset the value during deserialisation. + setExtendedLimits(false); + + checkSliderAtValue("Circle Size", 11); + checkBindableAtValue("Circle Size", 11); + + // setting extended limits will reset the serialisation exception. + // this should be fine as the goal is to allow, at most, the value of extended limits. + setExtendedLimits(true); + + checkSliderAtValue("Circle Size", 11); + checkBindableAtValue("Circle Size", 11); + } + + [Test] + public void TestExtendedLimits() + { + setSliderValue("Circle Size", 99); + + checkSliderAtValue("Circle Size", 10); + checkBindableAtValue("Circle Size", 10); + + setExtendedLimits(true); + + checkSliderAtValue("Circle Size", 10); + checkBindableAtValue("Circle Size", 10); + + setSliderValue("Circle Size", 99); + + checkSliderAtValue("Circle Size", 11); + checkBindableAtValue("Circle Size", 11); + + setExtendedLimits(false); + + checkSliderAtValue("Circle Size", 10); + checkBindableAtValue("Circle Size", 10); + } + + [Test] + public void TestUserOverrideMaintainedOnBeatmapChange() + { + setSliderValue("Circle Size", 9); + + setBeatmapWithDifficultyParameters(2); + + checkSliderAtValue("Circle Size", 9); + checkBindableAtValue("Circle Size", 9); + } + + [Test] + public void TestResetToDefault() + { + setBeatmapWithDifficultyParameters(2); + + setSliderValue("Circle Size", 9); + checkSliderAtValue("Circle Size", 9); + checkBindableAtValue("Circle Size", 9); + + resetToDefault("Circle Size"); + checkSliderAtValue("Circle Size", 2); + checkBindableAtValue("Circle Size", null); + } + + [Test] + public void TestUserOverrideMaintainedOnMatchingBeatmapValue() + { + setBeatmapWithDifficultyParameters(2); + + checkSliderAtValue("Circle Size", 2); + checkBindableAtValue("Circle Size", null); + + setSliderValue("Circle Size", 9); + checkSliderAtValue("Circle Size", 9); + checkBindableAtValue("Circle Size", 9); + + setBeatmapWithDifficultyParameters(4); + + checkSliderAtValue("Circle Size", 9); + checkBindableAtValue("Circle Size", 9); + } + + private void resetToDefault(string name) + { + AddStep($"Reset {name} to default", () => + this.ChildrenOfType().First(c => c.LabelText == name) + .Current.SetDefault()); + } + + private void setExtendedLimits(bool status) => + AddStep($"Set extended limits {status}", () => modDifficultyAdjust.ExtendedLimits.Value = status); + + private void setSliderValue(string name, float value) + { + AddStep($"Set {name} slider to {value}", () => + this.ChildrenOfType().First(c => c.LabelText == name) + .ChildrenOfType>().First().Current.Value = value); + } + + private void checkBindableAtValue(string name, float? expectedValue) + { + AddAssert($"Bindable {name} is {(expectedValue?.ToString() ?? "null")}", () => + this.ChildrenOfType().First(c => c.LabelText == name) + .Current.Value == expectedValue); + } + + private void checkSliderAtValue(string name, float expectedValue) + { + AddAssert($"Slider {name} at {expectedValue}", () => + this.ChildrenOfType().First(c => c.LabelText == name) + .ChildrenOfType>().First().Current.Value == expectedValue); } private void setBeatmapWithDifficultyParameters(float value) From 52ea62e3b2176d3da8fe83e689da62694e01fda5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Jul 2021 17:39:59 +0900 Subject: [PATCH 0254/2442] Add more comments and xmldoc --- .../Mods/DifficultyAdjustSettingsControl.cs | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs index ef2b88846a..05a36d3d31 100644 --- a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs +++ b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs @@ -16,9 +16,13 @@ namespace osu.Game.Rulesets.Mods [Resolved] private IBindable beatmap { get; set; } - protected readonly BindableNumber CurrentNumber = new BindableNumber(); + /// + /// Used to track the display value on the setting slider. + /// This can either be a user override or the beatmap default (when is null). + /// + private readonly BindableNumber displayNumber = new BindableNumber(); - protected override Drawable CreateControl() => new ControlDrawable(CurrentNumber); + protected override Drawable CreateControl() => new ControlDrawable(displayNumber); private bool isInternalChange; @@ -31,7 +35,10 @@ namespace osu.Game.Rulesets.Mods { // intercept and extract the DifficultyBindable. difficultyBindable = (DifficultyBindable)value; - CurrentNumber.BindTo(difficultyBindable.CurrentNumber); + + // this bind is used to transfer bounds/precision only. + displayNumber.BindTo(difficultyBindable.CurrentNumber); + base.Current = value; } } @@ -40,18 +47,18 @@ namespace osu.Game.Rulesets.Mods { base.LoadComplete(); - beatmap.BindValueChanged(b => - { - updateFromDifficulty(); - }, true); + beatmap.BindValueChanged(b => updateFromDifficulty(), true); Current.BindValueChanged(current => { + // the user override has changed; transfer the correct value to the visual display. if (current.NewValue == null) updateFromDifficulty(); + else + displayNumber.Value = current.NewValue.Value; }); - CurrentNumber.BindValueChanged(number => + displayNumber.BindValueChanged(number => { if (!isInternalChange) Current.Value = number.NewValue; @@ -67,8 +74,9 @@ namespace osu.Game.Rulesets.Mods if (Current.Value == null) { + // ensure the beatmap's value is not transferred as a user override. isInternalChange = true; - CurrentNumber.Value = difficultyBindable.ReadFromDifficulty(difficulty); + displayNumber.Value = difficultyBindable.ReadFromDifficulty(difficulty); isInternalChange = false; } } @@ -77,6 +85,8 @@ namespace osu.Game.Rulesets.Mods { private readonly BindableWithCurrent current = new BindableWithCurrent(); + // Mainly just for fulfilling the interface requirements. + // The actual update flow is done via the provided number. public Bindable Current { get => current.Current; From ba939c0b657aa8bd683635b100bac9d2335c2e7f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Jul 2021 17:40:09 +0900 Subject: [PATCH 0255/2442] Simplify serialisation edge case by moving to `Value` override --- osu.Game/Rulesets/Mods/DifficultyBindable.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Mods/DifficultyBindable.cs b/osu.Game/Rulesets/Mods/DifficultyBindable.cs index 7b01b1e0c7..22d8472922 100644 --- a/osu.Game/Rulesets/Mods/DifficultyBindable.cs +++ b/osu.Game/Rulesets/Mods/DifficultyBindable.cs @@ -73,13 +73,19 @@ namespace osu.Game.Rulesets.Mods public DifficultyBindable() { ExtendedLimits.BindValueChanged(_ => updateMaxValue()); + } - BindValueChanged(val => + public override float? Value + { + get => base.Value; + set { // Ensure that in the case serialisation runs in the wrong order (and limit extensions aren't applied yet) the deserialised value is still propagated. - if (val.NewValue != null) - CurrentNumber.MaxValue = MathF.Max(CurrentNumber.MaxValue, val.NewValue.Value); - }); + if (value != null) + CurrentNumber.MaxValue = MathF.Max(CurrentNumber.MaxValue, value.Value); + + base.Value = value; + } } private void updateMaxValue() From c67f756c754426af42eecc6d5ccb19a23f4cdc5d Mon Sep 17 00:00:00 2001 From: StanR Date: Thu, 8 Jul 2021 11:52:43 +0300 Subject: [PATCH 0256/2442] Change approach rate bonuses to be less punishing on mid-length maps --- .../Difficulty/OsuPerformanceCalculator.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 749d7d1b41..40ae1fca91 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -98,11 +98,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty double approachRateFactor = 0.0; if (Attributes.ApproachRate > 10.33) - approachRateFactor += 0.4 * (Attributes.ApproachRate - 10.33); + approachRateFactor += Attributes.ApproachRate - 10.33; else if (Attributes.ApproachRate < 8.0) - approachRateFactor += 0.01 * (8.0 - Attributes.ApproachRate); + approachRateFactor += 0.025 * (8.0 - Attributes.ApproachRate); - aimValue *= 1.0 + Math.Min(approachRateFactor, approachRateFactor * (totalHits / 1000.0)); + aimValue *= 1.0 + (0.03 + 0.37 * (1.0 / Math.Exp(-(0.0007 * (totalHits - 400))))) * approachRateFactor; // We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR. if (mods.Any(h => h is OsuModHidden)) @@ -145,10 +145,16 @@ namespace osu.Game.Rulesets.Osu.Difficulty double approachRateFactor = 0.0; if (Attributes.ApproachRate > 10.33) - approachRateFactor += 0.4 * (Attributes.ApproachRate - 10.33); + approachRateFactor = Attributes.ApproachRate - 10.33; speedValue *= 1.0 + Math.Min(approachRateFactor, approachRateFactor * (totalHits / 1000.0)); + double approachRateFactor = 0.0; + if (Attributes.ApproachRate > 10.33) + approachRateFactor += Attributes.ApproachRate - 10.33; + + speedValue *= 1.0 + (0.03 + 0.37 * (1.0 / Math.Exp(-(0.0007 * (totalHits - 400))))) * approachRateFactor; + if (mods.Any(m => m is OsuModHidden)) speedValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); From 592d3fdf006f63fd020cb04197f24523a104c304 Mon Sep 17 00:00:00 2001 From: StanR Date: Thu, 8 Jul 2021 11:54:58 +0300 Subject: [PATCH 0257/2442] Cleanup --- .../Difficulty/OsuPerformanceCalculator.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 40ae1fca91..d53af98f92 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -98,9 +98,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty double approachRateFactor = 0.0; if (Attributes.ApproachRate > 10.33) - approachRateFactor += Attributes.ApproachRate - 10.33; + approachRateFactor = Attributes.ApproachRate - 10.33; else if (Attributes.ApproachRate < 8.0) - approachRateFactor += 0.025 * (8.0 - Attributes.ApproachRate); + approachRateFactor = 0.025 * (8.0 - Attributes.ApproachRate); aimValue *= 1.0 + (0.03 + 0.37 * (1.0 / Math.Exp(-(0.0007 * (totalHits - 400))))) * approachRateFactor; @@ -147,12 +147,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (Attributes.ApproachRate > 10.33) approachRateFactor = Attributes.ApproachRate - 10.33; - speedValue *= 1.0 + Math.Min(approachRateFactor, approachRateFactor * (totalHits / 1000.0)); - - double approachRateFactor = 0.0; - if (Attributes.ApproachRate > 10.33) - approachRateFactor += Attributes.ApproachRate - 10.33; - speedValue *= 1.0 + (0.03 + 0.37 * (1.0 / Math.Exp(-(0.0007 * (totalHits - 400))))) * approachRateFactor; if (mods.Any(m => m is OsuModHidden)) From af270cccc436e581bb9c987a20d8c425a65e4675 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Jul 2021 03:13:44 +0900 Subject: [PATCH 0258/2442] Fix cross talk between `ModSelectOverlay`s --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 6c47b92d29..98a79a62c8 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -429,7 +429,7 @@ namespace osu.Game.Overlays.Mods if (!Stacked) modEnumeration = ModUtils.FlattenMods(modEnumeration); - section.Mods = modEnumeration.Select(getValidModOrNull).Where(m => m != null); + section.Mods = modEnumeration.Select(getValidModOrNull).Where(m => m != null).Select(m => m.CreateCopy()); } updateSelectedButtons(); From 7153983dd4d8c62209967195e269523fc6bdd76c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Jul 2021 18:29:52 +0900 Subject: [PATCH 0259/2442] Add test coverage --- .../TestSceneModSelectOverlay.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 4a1d90d871..91edc226c9 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; +using osu.Game.Overlays.Settings; using osu.Game.Rulesets; using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Mania.Mods; @@ -50,6 +51,38 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("show", () => modSelect.Show()); } + /// + /// Ensure that two mod overlays are not cross polluting via central settings instances. + /// + [Test] + public void TestSettingsNotCrossPolluting() + { + Bindable> selectedMods2 = null; + + AddStep("select diff adjust", () => SelectedMods.Value = new Mod[] { new OsuModDifficultyAdjust() }); + + AddStep("set setting", () => modSelect.ChildrenOfType>().First().Current.Value = 8); + + AddAssert("ensure setting is propagated", () => SelectedMods.Value.OfType().Single().CircleSize.Value == 8); + + AddStep("create second bindable", () => selectedMods2 = new Bindable>(new Mod[] { new OsuModDifficultyAdjust() })); + + AddStep("create second overlay", () => + { + Add(modSelect = new TestModSelectOverlay().With(d => + { + d.Origin = Anchor.TopCentre; + d.Anchor = Anchor.TopCentre; + d.SelectedMods.BindTarget = selectedMods2; + })); + }); + + AddStep("show", () => modSelect.Show()); + + AddAssert("ensure first is unchanged", () => SelectedMods.Value.OfType().Single().CircleSize.Value == 8); + AddAssert("ensure second is default", () => selectedMods2.Value.OfType().Single().CircleSize.Value == 5); + } + [Test] public void TestSettingsResetOnDeselection() { From c937c45360228a8f8d11808d7d3903fa7cf2227f Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 8 Jul 2021 18:49:32 +0900 Subject: [PATCH 0260/2442] Don't move selected objects outside the playfield in catch editor --- .../Edit/CatchSelectionHandler.cs | 47 +++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index d35d74d93d..9fcfa22cac 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -1,9 +1,12 @@ // 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 osu.Framework.Allocation; using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; @@ -26,13 +29,19 @@ namespace osu.Game.Rulesets.Catch.Edit Vector2 targetPosition = HitObjectContainer.ToLocalSpace(blueprint.ScreenSpaceSelectionPoint + moveEvent.ScreenSpaceDelta); float deltaX = targetPosition.X - originalPosition.X; + foreach (float x in EditorBeatmap.SelectedHitObjects.SelectMany(getOriginalPositions)) + deltaX = Math.Clamp(deltaX, -x, CatchPlayfield.WIDTH - x); + + if (deltaX == 0) + { + // Returns true: even there is no positional change, there may be a time change. + return true; + } + EditorBeatmap.PerformOnSelection(h => { if (!(h is CatchHitObject hitObject)) return; - if (hitObject is BananaShower) return; - - // TODO: confine in bounds hitObject.OriginalX += deltaX; // Move the nested hit objects to give an instant result before nested objects are recreated. @@ -42,5 +51,37 @@ namespace osu.Game.Rulesets.Catch.Edit return true; } + + /// + /// Enumerate X positions that should be contained in-bounds after move offset is applied. + /// + private IEnumerable getOriginalPositions(HitObject hitObject) + { + switch (hitObject) + { + case Fruit fruit: + yield return fruit.OriginalX; + + break; + + case JuiceStream juiceStream: + foreach (var nested in juiceStream.NestedHitObjects.OfType()) + { + // Exclude tiny droplets: even if `OriginalX` is outside the playfield, it can be moved inside the playfield after the random offset application. + if (!(nested is TinyDroplet)) + yield return nested.OriginalX; + } + + break; + + case BananaShower _: + // A banana shower occupies the whole screen width. + // If the selection contains a banana shower, the selection cannot be moved horizontally. + yield return 0; + yield return CatchPlayfield.WIDTH; + + break; + } + } } } From 93eb385dd40e3d745691c09d8e4376354ae40bd8 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 8 Jul 2021 20:01:39 +0900 Subject: [PATCH 0261/2442] Add sound for switching between volume controls --- osu.Game/Overlays/Volume/VolumeMeter.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs index eed6f5c2f3..4822f411c5 100644 --- a/osu.Game/Overlays/Volume/VolumeMeter.cs +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -8,6 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -42,7 +43,8 @@ namespace osu.Game.Overlays.Volume private Container selectedGlowContainer; - private Sample sample; + private Sample hoverSample; + private Sample notchSample; private double sampleLastPlaybackTime; public event Action StateChanged; @@ -78,7 +80,8 @@ namespace osu.Game.Overlays.Volume [BackgroundDependencyLoader] private void load(OsuColour colours, AudioManager audio) { - sample = audio.Samples.Get(@"UI/notch-tick"); + hoverSample = audio.Samples.Get($"UI/{HoverSampleSet.Button.GetDescription()}-hover"); + notchSample = audio.Samples.Get(@"UI/notch-tick"); sampleLastPlaybackTime = Time.Current; Color4 backgroundColour = colours.Gray1; @@ -274,7 +277,7 @@ namespace osu.Game.Overlays.Volume if (Time.Current - sampleLastPlaybackTime <= tick_debounce_time) return; - var channel = sample.GetChannel(); + var channel = notchSample.GetChannel(); channel.Frequency.Value = 0.99f + RNG.NextDouble(0.02f) + displayVolume * 0.1f; @@ -395,6 +398,8 @@ namespace osu.Game.Overlays.Volume selectedGlowContainer.FadeOut(transition_length, Easing.OutExpo); break; } + + hoverSample?.Play(); } } } From 546f55d34114e94b4e09cb959149b4bb704a8f17 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 8 Jul 2021 20:23:11 +0900 Subject: [PATCH 0262/2442] Change profile section expansion to use dropdown sounds --- .../Containers/OsuClickableContainer.cs | 2 +- osu.Game/Online/Chat/DrawableLinkCompiler.cs | 2 +- .../Header/Components/ExpandDetailsButton.cs | 18 ++++++++++++++++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuClickableContainer.cs b/osu.Game/Graphics/Containers/OsuClickableContainer.cs index 0bc3c876e1..2caa864580 100644 --- a/osu.Game/Graphics/Containers/OsuClickableContainer.cs +++ b/osu.Game/Graphics/Containers/OsuClickableContainer.cs @@ -18,7 +18,7 @@ namespace osu.Game.Graphics.Containers protected override Container Content => content; - protected virtual HoverClickSounds CreateHoverClickSounds(HoverSampleSet sampleSet) => new HoverClickSounds(sampleSet); + protected virtual HoverSounds CreateHoverClickSounds(HoverSampleSet sampleSet) => new HoverClickSounds(sampleSet); public OsuClickableContainer(HoverSampleSet sampleSet = HoverSampleSet.Default) { diff --git a/osu.Game/Online/Chat/DrawableLinkCompiler.cs b/osu.Game/Online/Chat/DrawableLinkCompiler.cs index e7f47833a2..abd7b73b5f 100644 --- a/osu.Game/Online/Chat/DrawableLinkCompiler.cs +++ b/osu.Game/Online/Chat/DrawableLinkCompiler.cs @@ -28,7 +28,7 @@ namespace osu.Game.Online.Chat public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parts.Any(d => d.ReceivePositionalInputAt(screenSpacePos)); - protected override HoverClickSounds CreateHoverClickSounds(HoverSampleSet sampleSet) => new LinkHoverSounds(sampleSet, Parts); + protected override HoverSounds CreateHoverClickSounds(HoverSampleSet sampleSet) => new LinkHoverSounds(sampleSet, Parts); public DrawableLinkCompiler(IEnumerable parts) { diff --git a/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs b/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs index 527c70685f..e395ee8a97 100644 --- a/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs @@ -2,11 +2,14 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; +using osu.Game.Graphics.UserInterface; using osuTK; namespace osu.Game.Overlays.Profile.Header.Components @@ -18,18 +21,29 @@ namespace osu.Game.Overlays.Profile.Header.Components public override LocalisableString TooltipText => DetailsVisible.Value ? "collapse" : "expand"; private SpriteIcon icon; + private Sample sampleOpen; + private Sample sampleClose; + + protected override HoverSounds CreateHoverClickSounds(HoverSampleSet sampleSet) => new HoverSounds(); public ExpandDetailsButton() { - Action = () => DetailsVisible.Toggle(); + Action = () => + { + DetailsVisible.Toggle(); + (DetailsVisible.Value ? sampleOpen : sampleClose)?.Play(); + }; } [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) + private void load(OverlayColourProvider colourProvider, AudioManager audio) { IdleColour = colourProvider.Background2; HoverColour = colourProvider.Background2.Lighten(0.2f); + sampleOpen = audio.Samples.Get(@"UI/dropdown-open"); + sampleClose = audio.Samples.Get(@"UI/dropdown-close"); + Child = icon = new SpriteIcon { Anchor = Anchor.Centre, From 8746ef0ba9c3924c09eb6881b05af3b36a011b3c Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 8 Jul 2021 20:36:25 +0900 Subject: [PATCH 0263/2442] Avoid double playback of sample --- osu.Game/Overlays/Volume/VolumeMeter.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs index 4822f411c5..f4cbbf5a00 100644 --- a/osu.Game/Overlays/Volume/VolumeMeter.cs +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -391,6 +391,7 @@ namespace osu.Game.Overlays.Volume case SelectionState.Selected: this.ScaleTo(1.04f, transition_length, Easing.OutExpo); selectedGlowContainer.FadeIn(transition_length, Easing.OutExpo); + hoverSample?.Play(); break; case SelectionState.NotSelected: @@ -398,8 +399,6 @@ namespace osu.Game.Overlays.Volume selectedGlowContainer.FadeOut(transition_length, Easing.OutExpo); break; } - - hoverSample?.Play(); } } } From b7803b889e5560adbd11e325c9421322d3b39ee2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 8 Jul 2021 20:37:38 +0900 Subject: [PATCH 0264/2442] Rename control class to be more descriptive --- osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs index 05a36d3d31..da5fb516f6 100644 --- a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs +++ b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mods /// private readonly BindableNumber displayNumber = new BindableNumber(); - protected override Drawable CreateControl() => new ControlDrawable(displayNumber); + protected override Drawable CreateControl() => new SliderControl(displayNumber); private bool isInternalChange; @@ -81,19 +81,20 @@ namespace osu.Game.Rulesets.Mods } } - private class ControlDrawable : CompositeDrawable, IHasCurrentValue + private class SliderControl : CompositeDrawable, IHasCurrentValue { private readonly BindableWithCurrent current = new BindableWithCurrent(); // Mainly just for fulfilling the interface requirements. // The actual update flow is done via the provided number. + // Of note, this is used for the "reset to default" flow. public Bindable Current { get => current.Current; set => current.Current = value; } - public ControlDrawable(BindableNumber currentNumber) + public SliderControl(BindableNumber currentNumber) { InternalChildren = new Drawable[] { From 02298c2cf40e2b7df25d44c056d9507cb338d1fc Mon Sep 17 00:00:00 2001 From: StanR Date: Fri, 9 Jul 2021 00:06:05 +0300 Subject: [PATCH 0265/2442] Fix incorrect curve, move total hits factor into a separate variable for clarity --- .../Difficulty/OsuPerformanceCalculator.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index d53af98f92..8a3c426381 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -102,7 +102,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty else if (Attributes.ApproachRate < 8.0) approachRateFactor = 0.025 * (8.0 - Attributes.ApproachRate); - aimValue *= 1.0 + (0.03 + 0.37 * (1.0 / Math.Exp(-(0.0007 * (totalHits - 400))))) * approachRateFactor; + double approachRateTotalHitsFactor = 1.0 / (1.0 + Math.Exp(-(0.007 * (totalHits - 400)))); + + aimValue *= 1.0 + (0.03 + 0.37 * approachRateTotalHitsFactor) * approachRateFactor; // We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR. if (mods.Any(h => h is OsuModHidden)) @@ -147,7 +149,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (Attributes.ApproachRate > 10.33) approachRateFactor = Attributes.ApproachRate - 10.33; - speedValue *= 1.0 + (0.03 + 0.37 * (1.0 / Math.Exp(-(0.0007 * (totalHits - 400))))) * approachRateFactor; + double approachRateTotalHitsFactor = 1.0 / (1.0 + Math.Exp(-(0.007 * (totalHits - 400)))); + + speedValue *= 1.0 + (0.03 + 0.37 * approachRateTotalHitsFactor) * approachRateFactor; if (mods.Any(m => m is OsuModHidden)) speedValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); From a7be6327709a8e59561b257c21786c133860100a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 9 Jul 2021 00:39:09 +0300 Subject: [PATCH 0266/2442] Improve documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Overlays/RestoreDefaultValueButton.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/RestoreDefaultValueButton.cs b/osu.Game/Overlays/RestoreDefaultValueButton.cs index 44adb70108..7413837852 100644 --- a/osu.Game/Overlays/RestoreDefaultValueButton.cs +++ b/osu.Game/Overlays/RestoreDefaultValueButton.cs @@ -23,12 +23,8 @@ namespace osu.Game.Overlays // this is done to ensure a click on this button doesn't trigger focus on a parent element which contains the button. public override bool AcceptsFocus => true; - // this is intentionally not using BindableWithCurrent as this needs a BindableNumber instance for an accurate IsDefault value. - // - // this also cannot use IBindableWithCurrent.Create() due to BindableNumberWithCurrent - // directly casting given bindables to BindableNumber, which is not necessarily the case. - // - // therefore rely on the old method of taking each current bindable instance for now, until things are settled framework-side. + // this is intentionally not using BindableWithCurrent, as it can use the wrong IsDefault implementation when passed a BindableNumber. + // using GetBoundCopy() ensures that the received bindable is of the exact same type as the source bindable and uses the proper IsDefault implementation. private Bindable current; public Bindable Current From 0223c569df8395004aca92aa8923275b9ea22b61 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 9 Jul 2021 00:48:48 +0300 Subject: [PATCH 0267/2442] Remove no longer necessary method definitions --- osu.Game/Overlays/RestoreDefaultValueButton.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/RestoreDefaultValueButton.cs b/osu.Game/Overlays/RestoreDefaultValueButton.cs index 7413837852..469d48d82b 100644 --- a/osu.Game/Overlays/RestoreDefaultValueButton.cs +++ b/osu.Game/Overlays/RestoreDefaultValueButton.cs @@ -35,9 +35,9 @@ namespace osu.Game.Overlays current?.UnbindAll(); current = value.GetBoundCopy(); - current.ValueChanged += onValueChanged; - current.DefaultChanged += onDefaultChanged; - current.DisabledChanged += onDisabledChanged; + current.ValueChanged += _ => UpdateState(); + current.DefaultChanged += _ => UpdateState(); + current.DisabledChanged += _ => UpdateState(); UpdateState(); } } @@ -93,10 +93,6 @@ namespace osu.Game.Overlays UpdateState(); } - private void onValueChanged(ValueChangedEvent _) => UpdateState(); - private void onDefaultChanged(ValueChangedEvent _) => UpdateState(); - private void onDisabledChanged(bool _) => UpdateState(); - public void UpdateState() => Scheduler.AddOnce(updateState); private void updateState() From 2eb12a59b7d07979aabc95dfb1396452e41e72e0 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 9 Jul 2021 11:16:47 +0900 Subject: [PATCH 0268/2442] Rename function to be more accurate --- osu.Game/Graphics/Containers/OsuClickableContainer.cs | 4 ++-- osu.Game/Online/Chat/DrawableLinkCompiler.cs | 2 +- .../Overlays/Profile/Header/Components/ExpandDetailsButton.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuClickableContainer.cs b/osu.Game/Graphics/Containers/OsuClickableContainer.cs index 2caa864580..bf397e4251 100644 --- a/osu.Game/Graphics/Containers/OsuClickableContainer.cs +++ b/osu.Game/Graphics/Containers/OsuClickableContainer.cs @@ -18,7 +18,7 @@ namespace osu.Game.Graphics.Containers protected override Container Content => content; - protected virtual HoverSounds CreateHoverClickSounds(HoverSampleSet sampleSet) => new HoverClickSounds(sampleSet); + protected virtual HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverClickSounds(sampleSet); public OsuClickableContainer(HoverSampleSet sampleSet = HoverSampleSet.Default) { @@ -39,7 +39,7 @@ namespace osu.Game.Graphics.Containers InternalChildren = new Drawable[] { content, - CreateHoverClickSounds(sampleSet) + CreateHoverSounds(sampleSet) }; } } diff --git a/osu.Game/Online/Chat/DrawableLinkCompiler.cs b/osu.Game/Online/Chat/DrawableLinkCompiler.cs index abd7b73b5f..53ea1d6f99 100644 --- a/osu.Game/Online/Chat/DrawableLinkCompiler.cs +++ b/osu.Game/Online/Chat/DrawableLinkCompiler.cs @@ -28,7 +28,7 @@ namespace osu.Game.Online.Chat public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parts.Any(d => d.ReceivePositionalInputAt(screenSpacePos)); - protected override HoverSounds CreateHoverClickSounds(HoverSampleSet sampleSet) => new LinkHoverSounds(sampleSet, Parts); + protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new LinkHoverSounds(sampleSet, Parts); public DrawableLinkCompiler(IEnumerable parts) { diff --git a/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs b/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs index e395ee8a97..16b443875e 100644 --- a/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Profile.Header.Components private Sample sampleOpen; private Sample sampleClose; - protected override HoverSounds CreateHoverClickSounds(HoverSampleSet sampleSet) => new HoverSounds(); + protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds(); public ExpandDetailsButton() { From 9f7c6adb5849777f77de6ba48c129bf8b53fd130 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jul 2021 12:15:28 +0900 Subject: [PATCH 0269/2442] Fix test failures due to logger pollution As seen at https://github.com/ppy/osu/pull/13831/checks?check_run_id=3025050307. I can't confirm that this will fix the issue but it looks like the only plausible reason. I have confirmed that the logging is not coming from the local (first logging is guaranteed to be after `SetupForRun`). --- osu.Game/Tests/CleanRunHeadlessGameHost.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/CleanRunHeadlessGameHost.cs b/osu.Game/Tests/CleanRunHeadlessGameHost.cs index baa7b27d28..03ab94d1da 100644 --- a/osu.Game/Tests/CleanRunHeadlessGameHost.cs +++ b/osu.Game/Tests/CleanRunHeadlessGameHost.cs @@ -25,8 +25,11 @@ namespace osu.Game.Tests protected override void SetupForRun() { - base.SetupForRun(); Storage.DeleteDirectory(string.Empty); + + // base call needs to be run *after* storage is emptied, as it updates the (static) logger's storage and may start writing + // log entries from another source if a unit test host is shared over multiple tests, causing a file access denied exception. + base.SetupForRun(); } } } From 887035c12e85861704ed243ee2b3f5a9eb8ef2f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jul 2021 12:21:24 +0900 Subject: [PATCH 0270/2442] Fix migration target having left over files potentially causing test failures As seen at https://github.com/ppy/osu/pull/13831/checks?check_run_id=3025050324. --- osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs index a540ad7247..4c44e2ec72 100644 --- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -184,6 +184,9 @@ namespace osu.Game.Tests.NonVisual Assert.DoesNotThrow(() => osu.Migrate(customPath2)); Assert.That(File.Exists(Path.Combine(customPath2, database_filename))); + // some files may have been left behind for whatever reason, but that's not what we're testing here. + customPath = prepareCustomPath(); + Assert.DoesNotThrow(() => osu.Migrate(customPath)); Assert.That(File.Exists(Path.Combine(customPath, database_filename))); } From 9786e1a932426971cc09f11009785a842da70a3f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jul 2021 12:36:54 +0900 Subject: [PATCH 0271/2442] Ensure run-from-screen song select reaches correct point in execution Fixes issues as seen at https://github.com/ppy/osu/runs/3023581865?check_suite_focus=true. Song select may take a few frames to perform initial selection as there is a bit of internal async logic. This ensures that the beatmap has been updated before continuing with test execution. --- .../Visual/Navigation/TestScenePerformFromScreen.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs index 92152bce18..4ec76e1e4b 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs @@ -58,8 +58,7 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestPerformAtSongSelectFromPlayerLoader() { - AddStep("import beatmap", () => ImportBeatmapTest.LoadQuickOszIntoOsu(Game).Wait()); - PushAndConfirm(() => new TestPlaySongSelect()); + importAndWaitForSongSelect(); AddStep("Press enter", () => InputManager.Key(Key.Enter)); AddUntilStep("Wait for new screen", () => Game.ScreenStack.CurrentScreen is PlayerLoader); @@ -72,8 +71,7 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestPerformAtMenuFromPlayerLoader() { - AddStep("import beatmap", () => ImportBeatmapTest.LoadQuickOszIntoOsu(Game).Wait()); - PushAndConfirm(() => new TestPlaySongSelect()); + importAndWaitForSongSelect(); AddStep("Press enter", () => InputManager.Key(Key.Enter)); AddUntilStep("Wait for new screen", () => Game.ScreenStack.CurrentScreen is PlayerLoader); @@ -172,6 +170,13 @@ namespace osu.Game.Tests.Visual.Navigation } } + private void importAndWaitForSongSelect() + { + AddStep("import beatmap", () => ImportBeatmapTest.LoadQuickOszIntoOsu(Game).Wait()); + PushAndConfirm(() => new TestPlaySongSelect()); + AddUntilStep("beatmap updated", () => Game.Beatmap.Value.BeatmapSetInfo.OnlineBeatmapSetID == 241526); + } + public class DialogBlockingScreen : OsuScreen { [Resolved] From 7e146796066588763ab367a6915e32c7a1f5d902 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 9 Jul 2021 12:58:08 +0900 Subject: [PATCH 0272/2442] Expand the selection movement limiting code with detailed comments --- .../Edit/CatchSelectionHandler.cs | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index 9fcfa22cac..d3e79e1778 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -27,10 +27,9 @@ namespace osu.Game.Rulesets.Catch.Edit var blueprint = moveEvent.Blueprint; Vector2 originalPosition = HitObjectContainer.ToLocalSpace(blueprint.ScreenSpaceSelectionPoint); Vector2 targetPosition = HitObjectContainer.ToLocalSpace(blueprint.ScreenSpaceSelectionPoint + moveEvent.ScreenSpaceDelta); - float deltaX = targetPosition.X - originalPosition.X; - foreach (float x in EditorBeatmap.SelectedHitObjects.SelectMany(getOriginalPositions)) - deltaX = Math.Clamp(deltaX, -x, CatchPlayfield.WIDTH - x); + float deltaX = targetPosition.X - originalPosition.X; + deltaX = limitMovement(deltaX, EditorBeatmap.SelectedHitObjects); if (deltaX == 0) { @@ -52,6 +51,36 @@ namespace osu.Game.Rulesets.Catch.Edit return true; } + /// + /// Limit positional movement of the objects by the constraint that moved objects should stay in bounds. + /// + /// The positional movement. + /// The objects to be moved. + /// The positional movement with the restriction applied. + private float limitMovement(float deltaX, IEnumerable movingObjects) + { + float minX = float.PositiveInfinity; + float maxX = float.NegativeInfinity; + + foreach (float x in movingObjects.SelectMany(getOriginalPositions)) + { + minX = Math.Min(minX, x); + maxX = Math.Max(maxX, x); + } + + // To make an object with position `x` stay in bounds after `deltaX` movement, `0 <= x + deltaX <= WIDTH` should be satisfied. + // Subtracting `x`, we get `-x <= deltaX <= WIDTH - x`. + // We only need to apply the inequality to extreme values of `x`. + float lowerBound = -minX; + float upperBound = CatchPlayfield.WIDTH - maxX; + // The inequality may be unsatisfiable if the objects were already out of bounds. + // In that case, don't move objects at all. + if (!(lowerBound <= upperBound)) + return 0; + + return Math.Clamp(deltaX, lowerBound, upperBound); + } + /// /// Enumerate X positions that should be contained in-bounds after move offset is applied. /// From f3b68a4fbf5d8e810f8451299ac4b57ad138228d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jul 2021 13:17:25 +0900 Subject: [PATCH 0273/2442] Fix storage wrapping logic setting logger too early in startup sequence --- osu.Game/IO/OsuStorage.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/IO/OsuStorage.cs b/osu.Game/IO/OsuStorage.cs index 75130b0f9b..802c71e363 100644 --- a/osu.Game/IO/OsuStorage.cs +++ b/osu.Game/IO/OsuStorage.cs @@ -102,8 +102,15 @@ namespace osu.Game.IO protected override void ChangeTargetStorage(Storage newStorage) { + var lastStorage = UnderlyingStorage; base.ChangeTargetStorage(newStorage); - Logger.Storage = UnderlyingStorage.GetStorageForDirectory("logs"); + + if (lastStorage != null) + { + // for now we assume that if there was a previous storage, this is a migration operation. + // the logger shouldn't be set during initialisation as it can cause cross-talk in tests (due to being static). + Logger.Storage = UnderlyingStorage.GetStorageForDirectory("logs"); + } } public override void Migrate(Storage newStorage) From df4bd86cfc946e8ca44a24edf8dd21b78b174ecf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jul 2021 13:17:25 +0900 Subject: [PATCH 0274/2442] Fix storage wrapping logic setting logger too early in startup sequence --- osu.Game/IO/OsuStorage.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/IO/OsuStorage.cs b/osu.Game/IO/OsuStorage.cs index 75130b0f9b..802c71e363 100644 --- a/osu.Game/IO/OsuStorage.cs +++ b/osu.Game/IO/OsuStorage.cs @@ -102,8 +102,15 @@ namespace osu.Game.IO protected override void ChangeTargetStorage(Storage newStorage) { + var lastStorage = UnderlyingStorage; base.ChangeTargetStorage(newStorage); - Logger.Storage = UnderlyingStorage.GetStorageForDirectory("logs"); + + if (lastStorage != null) + { + // for now we assume that if there was a previous storage, this is a migration operation. + // the logger shouldn't be set during initialisation as it can cause cross-talk in tests (due to being static). + Logger.Storage = UnderlyingStorage.GetStorageForDirectory("logs"); + } } public override void Migrate(Storage newStorage) From 90326f8864723bc4427512705da4295a9f316300 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jul 2021 13:24:26 +0900 Subject: [PATCH 0275/2442] Standardise variables --- osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs | 4 ++-- osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs | 4 ++-- osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs | 2 +- osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs | 2 +- osu.Game/Rulesets/Mods/DifficultyBindable.cs | 4 ++-- osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs index 8686627c2a..bd78d3b085 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Catch.Mods MinValue = 1, MaxValue = 10, ExtendedMaxValue = 11, - ReadFromDifficulty = diff => diff.CircleSize, + ReadCurrentFromDifficulty = diff => diff.CircleSize, }; [SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1)] @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Catch.Mods MinValue = 1, MaxValue = 10, ExtendedMaxValue = 11, - ReadFromDifficulty = diff => diff.ApproachRate, + ReadCurrentFromDifficulty = diff => diff.ApproachRate, }; [SettingSource("Spicy Patterns", "Adjust the patterns as if Hard Rock is enabled.")] diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs index 983216dfa1..3a6b232f9f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Mods MinValue = 0, MaxValue = 10, ExtendedMaxValue = 11, - ReadFromDifficulty = diff => diff.CircleSize, + ReadCurrentFromDifficulty = diff => diff.CircleSize, }; [SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1, SettingControlType = typeof(DifficultyAdjustSettingsControl))] @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Mods MinValue = 0, MaxValue = 10, ExtendedMaxValue = 11, - ReadFromDifficulty = diff => diff.ApproachRate, + ReadCurrentFromDifficulty = diff => diff.ApproachRate, }; public override string SettingDescription diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs index ace105b21c..9540e35780 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Taiko.Mods Precision = 0.05f, MinValue = 0.25f, MaxValue = 4, - ReadFromDifficulty = _ => 1, + ReadCurrentFromDifficulty = _ => 1, }; public override string SettingDescription diff --git a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs index da5fb516f6..9ca44b25e8 100644 --- a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs +++ b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs @@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Mods { // ensure the beatmap's value is not transferred as a user override. isInternalChange = true; - displayNumber.Value = difficultyBindable.ReadFromDifficulty(difficulty); + displayNumber.Value = difficultyBindable.ReadCurrentFromDifficulty(difficulty); isInternalChange = false; } } diff --git a/osu.Game/Rulesets/Mods/DifficultyBindable.cs b/osu.Game/Rulesets/Mods/DifficultyBindable.cs index 22d8472922..26538fa4e3 100644 --- a/osu.Game/Rulesets/Mods/DifficultyBindable.cs +++ b/osu.Game/Rulesets/Mods/DifficultyBindable.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mods /// /// Whether the extended limits should be applied to this bindable. /// - public BindableBool ExtendedLimits { get; } = new BindableBool(); + public readonly BindableBool ExtendedLimits = new BindableBool(); /// /// An internal numeric bindable to hold and propagate min/max/precision. @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mods /// /// A function that can extract the current value of this setting from a beatmap difficulty for display purposes. /// - public Func ReadFromDifficulty; + public Func ReadCurrentFromDifficulty; public float Precision { diff --git a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs index d9d305d457..b78c30e8a5 100644 --- a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs @@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Mods MinValue = 0, MaxValue = 10, ExtendedMaxValue = 11, - ReadFromDifficulty = diff => diff.DrainRate, + ReadCurrentFromDifficulty = diff => diff.DrainRate, }; [SettingSource("Accuracy", "Override a beatmap's set OD.", LAST_SETTING_ORDER, SettingControlType = typeof(DifficultyAdjustSettingsControl))] @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Mods MinValue = 0, MaxValue = 10, ExtendedMaxValue = 11, - ReadFromDifficulty = diff => diff.OverallDifficulty, + ReadCurrentFromDifficulty = diff => diff.OverallDifficulty, }; [SettingSource("Extended Limits", "Adjust difficulty beyond sane limits.")] From f9cd7f10d827836be6080376b2adf5c7b0148ff4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jul 2021 13:26:00 +0900 Subject: [PATCH 0276/2442] Allow null values for `ReadCurrentFromDifficulty` As long as this isn't a constructor parameter it feels best to gracefully handle omission. Realistically having it in the ctor is the best move, but it doesn't feel great in line with the other parameters passed in via object initalisers. --- osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs index 9ca44b25e8..aa25f20c32 100644 --- a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs +++ b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Mods if (difficulty == null) return; - if (Current.Value == null) + if (Current.Value == null && difficultyBindable.ReadCurrentFromDifficulty != null) { // ensure the beatmap's value is not transferred as a user override. isInternalChange = true; From 51bd83b3f4998915cab2a055ad1012c6259b2775 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jul 2021 13:30:14 +0900 Subject: [PATCH 0277/2442] Update override matching test to match expectations --- .../TestSceneModDifficultyAdjustSettings.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs index d0a02a6c2d..bf494d4362 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs @@ -139,19 +139,22 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestUserOverrideMaintainedOnMatchingBeatmapValue() { - setBeatmapWithDifficultyParameters(2); + setBeatmapWithDifficultyParameters(3); - checkSliderAtValue("Circle Size", 2); + checkSliderAtValue("Circle Size", 3); checkBindableAtValue("Circle Size", null); - setSliderValue("Circle Size", 9); - checkSliderAtValue("Circle Size", 9); - checkBindableAtValue("Circle Size", 9); + // need to initially change it away from the current beatmap value to trigger an override. + setSliderValue("Circle Size", 4); + setSliderValue("Circle Size", 3); + + checkSliderAtValue("Circle Size", 3); + checkBindableAtValue("Circle Size", 3); setBeatmapWithDifficultyParameters(4); - checkSliderAtValue("Circle Size", 9); - checkBindableAtValue("Circle Size", 9); + checkSliderAtValue("Circle Size", 3); + checkBindableAtValue("Circle Size", 3); } private void resetToDefault(string name) From e0277763d0f7f504ba5e003cce10ba7ac2938632 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jul 2021 13:50:07 +0900 Subject: [PATCH 0278/2442] Refactor `DifficultyAdjustSettingsControl` to help with readability --- .../Mods/DifficultyAdjustSettingsControl.cs | 48 ++++++++++--------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs index aa25f20c32..9286dd58a9 100644 --- a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs +++ b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs @@ -18,11 +18,14 @@ namespace osu.Game.Rulesets.Mods /// /// Used to track the display value on the setting slider. - /// This can either be a user override or the beatmap default (when is null). /// - private readonly BindableNumber displayNumber = new BindableNumber(); + /// + /// When the mod is overriding a default, this will match the value of . + /// When there is no override (ie. is null), this value will match the beatmap provided default via . + /// + private readonly BindableNumber sliderDisplayCurrent = new BindableNumber(); - protected override Drawable CreateControl() => new SliderControl(displayNumber); + protected override Drawable CreateControl() => new SliderControl(sliderDisplayCurrent); private bool isInternalChange; @@ -33,11 +36,10 @@ namespace osu.Game.Rulesets.Mods get => base.Current; set { - // intercept and extract the DifficultyBindable. + // Intercept and extract the internal number bindable from DifficultyBindable. + // This will provide bounds and precision specifications for the slider bar. difficultyBindable = (DifficultyBindable)value; - - // this bind is used to transfer bounds/precision only. - displayNumber.BindTo(difficultyBindable.CurrentNumber); + sliderDisplayCurrent.BindTo(difficultyBindable.CurrentNumber); base.Current = value; } @@ -51,15 +53,22 @@ namespace osu.Game.Rulesets.Mods Current.BindValueChanged(current => { - // the user override has changed; transfer the correct value to the visual display. - if (current.NewValue == null) - updateFromDifficulty(); + if (current.NewValue != null) + { + // a user override has been added or updated. + sliderDisplayCurrent.Value = current.NewValue.Value; + } else - displayNumber.Value = current.NewValue.Value; + { + // user override was removed, so restore the beatmap provided value. + updateFromDifficulty(); + } }); - displayNumber.BindValueChanged(number => + sliderDisplayCurrent.BindValueChanged(number => { + // this handles the transfer of the slider value to the main bindable. + // as such, should be skipped if the slider is being updated via updateFromDifficulty(). if (!isInternalChange) Current.Value = number.NewValue; }); @@ -76,23 +85,16 @@ namespace osu.Game.Rulesets.Mods { // ensure the beatmap's value is not transferred as a user override. isInternalChange = true; - displayNumber.Value = difficultyBindable.ReadCurrentFromDifficulty(difficulty); + sliderDisplayCurrent.Value = difficultyBindable.ReadCurrentFromDifficulty(difficulty); isInternalChange = false; } } private class SliderControl : CompositeDrawable, IHasCurrentValue { - private readonly BindableWithCurrent current = new BindableWithCurrent(); - - // Mainly just for fulfilling the interface requirements. - // The actual update flow is done via the provided number. - // Of note, this is used for the "reset to default" flow. - public Bindable Current - { - get => current.Current; - set => current.Current = value; - } + // This is required as SettingsItem relies heavily on this bindable for internal use. + // The actual update flow is done via the bindable provided in the constructor. + public Bindable Current { get; set; } = new Bindable(); public SliderControl(BindableNumber currentNumber) { From 741062a6da3a7ad7d3126210585511ac2abdc4fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jul 2021 13:58:44 +0900 Subject: [PATCH 0279/2442] Simplify bindable update methods --- .../Mods/DifficultyAdjustSettingsControl.cs | 45 +++++++++---------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs index 9286dd58a9..0f48ed4190 100644 --- a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs +++ b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs @@ -21,12 +21,15 @@ namespace osu.Game.Rulesets.Mods /// /// /// When the mod is overriding a default, this will match the value of . - /// When there is no override (ie. is null), this value will match the beatmap provided default via . + /// When there is no override (ie. is null), this value will match the beatmap provided default via . /// private readonly BindableNumber sliderDisplayCurrent = new BindableNumber(); protected override Drawable CreateControl() => new SliderControl(sliderDisplayCurrent); + /// + /// Guards against beatmap values displayed on slider bars being transferred to user override. + /// private bool isInternalChange; private DifficultyBindable difficultyBindable; @@ -49,21 +52,8 @@ namespace osu.Game.Rulesets.Mods { base.LoadComplete(); - beatmap.BindValueChanged(b => updateFromDifficulty(), true); - - Current.BindValueChanged(current => - { - if (current.NewValue != null) - { - // a user override has been added or updated. - sliderDisplayCurrent.Value = current.NewValue.Value; - } - else - { - // user override was removed, so restore the beatmap provided value. - updateFromDifficulty(); - } - }); + Current.BindValueChanged(current => updateCurrentFromSlider()); + beatmap.BindValueChanged(b => updateCurrentFromSlider(), true); sliderDisplayCurrent.BindValueChanged(number => { @@ -74,20 +64,27 @@ namespace osu.Game.Rulesets.Mods }); } - private void updateFromDifficulty() + private void updateCurrentFromSlider() { + if (Current.Value != null) + { + // a user override has been added or updated. + sliderDisplayCurrent.Value = Current.Value.Value; + return; + } + var difficulty = beatmap.Value.BeatmapInfo.BaseDifficulty; if (difficulty == null) return; - if (Current.Value == null && difficultyBindable.ReadCurrentFromDifficulty != null) - { - // ensure the beatmap's value is not transferred as a user override. - isInternalChange = true; - sliderDisplayCurrent.Value = difficultyBindable.ReadCurrentFromDifficulty(difficulty); - isInternalChange = false; - } + // generally should always be implemented, else the slider will have a zero default. + if (difficultyBindable.ReadCurrentFromDifficulty == null) + return; + + isInternalChange = true; + sliderDisplayCurrent.Value = difficultyBindable.ReadCurrentFromDifficulty(difficulty); + isInternalChange = false; } private class SliderControl : CompositeDrawable, IHasCurrentValue From 6a5f0e823718cb5cd419eab59ebcff55c159aa37 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jul 2021 14:28:57 +0900 Subject: [PATCH 0280/2442] Move handling of replay seek operations out of progress bar This is in order to avoid using the now obsoleted property `SliderBar.AllowKeyboardInputWhenNotHovered` (see https://github.com/ppy/osu-framework/pull/4579). --- .../Input/Bindings/GlobalActionContainer.cs | 8 +++++ osu.Game/Screens/Play/ReplayPlayer.cs | 32 +++++++++++++++++++ osu.Game/Screens/Play/SongProgressBar.cs | 2 -- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 2482be90ee..d3cc90ef99 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -87,6 +87,8 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.Shift, InputKey.Tab }, GlobalAction.ToggleInGameInterface), new KeyBinding(InputKey.MouseMiddle, GlobalAction.PauseGameplay), new KeyBinding(InputKey.Space, GlobalAction.TogglePauseReplay), + new KeyBinding(InputKey.Left, GlobalAction.SeekReplayBackward), + new KeyBinding(InputKey.Right, GlobalAction.SeekReplayForward), new KeyBinding(InputKey.Control, GlobalAction.HoldForHUD), }; @@ -272,5 +274,11 @@ namespace osu.Game.Input.Bindings [Description("Next volume meter")] NextVolumeMeter, + + [Description("Seek replay forward")] + SeekReplayForward, + + [Description("Seek replay backward")] + SeekReplayBackward, } } diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index f70c05c2ff..adbb5a53f6 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -3,11 +3,15 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using osu.Framework.Input.Bindings; +using osu.Framework.Threading; using osu.Game.Beatmaps; +using osu.Game.Extensions; using osu.Game.Input.Bindings; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; using osu.Game.Scoring; using osu.Game.Screens.Ranking; @@ -43,10 +47,24 @@ namespace osu.Game.Screens.Play protected override ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score, false); + private ScheduledDelegate keyboardSeekDelegate; + public bool OnPressed(GlobalAction action) { + const double keyboard_seek_amount = 5000; + switch (action) { + case GlobalAction.SeekReplayBackward: + keyboardSeekDelegate?.Cancel(); + keyboardSeekDelegate = this.BeginKeyRepeat(Scheduler, () => keyboardSeek(-1)); + return true; + + case GlobalAction.SeekReplayForward: + keyboardSeekDelegate?.Cancel(); + keyboardSeekDelegate = this.BeginKeyRepeat(Scheduler, () => keyboardSeek(1)); + return true; + case GlobalAction.TogglePauseReplay: if (GameplayClockContainer.IsPaused.Value) GameplayClockContainer.Start(); @@ -56,10 +74,24 @@ namespace osu.Game.Screens.Play } return false; + + void keyboardSeek(int direction) + { + double target = Math.Clamp(GameplayClockContainer.CurrentTime + direction * keyboard_seek_amount, 0, GameplayBeatmap.HitObjects.Last().GetEndTime()); + + Seek(target); + } } public void OnReleased(GlobalAction action) { + switch (action) + { + case GlobalAction.SeekReplayBackward: + case GlobalAction.SeekReplayForward: + keyboardSeekDelegate?.Cancel(); + break; + } } } } diff --git a/osu.Game/Screens/Play/SongProgressBar.cs b/osu.Game/Screens/Play/SongProgressBar.cs index 939b5fad1f..5052b32335 100644 --- a/osu.Game/Screens/Play/SongProgressBar.cs +++ b/osu.Game/Screens/Play/SongProgressBar.cs @@ -57,8 +57,6 @@ namespace osu.Game.Screens.Play set => CurrentNumber.Value = value; } - protected override bool AllowKeyboardInputWhenNotHovered => true; - public SongProgressBar(float barHeight, float handleBarHeight, Vector2 handleSize) { CurrentNumber.MinValue = 0; From 9083b28114039b18078b0b0cc1402d15de88ed87 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jul 2021 14:47:11 +0900 Subject: [PATCH 0281/2442] Add test coverage of seeking and pausing --- .../Visual/Gameplay/TestSceneReplayPlayer.cs | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneReplayPlayer.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayPlayer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayPlayer.cs new file mode 100644 index 0000000000..fcd65eaff3 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayPlayer.cs @@ -0,0 +1,87 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneReplayPlayer : RateAdjustedBeatmapTestScene + { + protected TestReplayPlayer Player; + + public override void SetUpSteps() + { + base.SetUpSteps(); + + AddStep("Initialise player", () => Player = CreatePlayer(new OsuRuleset())); + AddStep("Load player", () => LoadScreen(Player)); + AddUntilStep("player loaded", () => Player.IsLoaded); + } + + [Test] + public void TestPause() + { + double? lastTime = null; + + AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0); + + AddStep("Pause playback", () => InputManager.Key(Key.Space)); + + AddUntilStep("Time stopped progressing", () => + { + double current = Player.GameplayClockContainer.CurrentTime; + bool changed = lastTime != current; + lastTime = current; + + return !changed; + }); + + AddWaitStep("wait some", 10); + + AddAssert("Time still stopped", () => lastTime == Player.GameplayClockContainer.CurrentTime); + } + + [Test] + public void TestSeekBackwards() + { + double? lastTime = null; + + AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0); + + AddStep("Seek backwards", () => + { + lastTime = Player.GameplayClockContainer.CurrentTime; + InputManager.Key(Key.Left); + }); + + AddAssert("Jumped backwards", () => Player.GameplayClockContainer.CurrentTime - lastTime < 0); + } + + [Test] + public void TestSeekForwards() + { + double? lastTime = null; + + AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0); + + AddStep("Seek forwards", () => + { + lastTime = Player.GameplayClockContainer.CurrentTime; + InputManager.Key(Key.Right); + }); + + AddAssert("Jumped forwards", () => Player.GameplayClockContainer.CurrentTime - lastTime > 500); + } + + protected TestReplayPlayer CreatePlayer(Ruleset ruleset) + { + Beatmap.Value = CreateWorkingBeatmap(ruleset.RulesetInfo); + SelectedMods.Value = new[] { ruleset.GetAutoplayMod() }; + + return new TestReplayPlayer(false); + } + } +} From 995ef953c689746c546457b9ec477368a89af366 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 9 Jul 2021 15:13:54 +0900 Subject: [PATCH 0282/2442] Modify comment --- osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index d3e79e1778..02dc6f61c8 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Edit if (deltaX == 0) { - // Returns true: even there is no positional change, there may be a time change. + // Even there is no positional change, there may be a time change. return true; } @@ -96,7 +96,7 @@ namespace osu.Game.Rulesets.Catch.Edit case JuiceStream juiceStream: foreach (var nested in juiceStream.NestedHitObjects.OfType()) { - // Exclude tiny droplets: even if `OriginalX` is outside the playfield, it can be moved inside the playfield after the random offset application. + // Even if `OriginalX` is outside the playfield, tiny droplets can be moved inside the playfield after the random offset application. if (!(nested is TinyDroplet)) yield return nested.OriginalX; } From 6ae631b03a1ac6048f94a29e97f370fa26454a58 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jul 2021 16:50:54 +0900 Subject: [PATCH 0283/2442] Remove previous seek testing logic from common test scene --- osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs index bc7cf8eee2..fdc3916c47 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs @@ -11,7 +11,6 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play; using osu.Game.Screens.Play.Break; using osu.Game.Screens.Ranking; -using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay { @@ -36,18 +35,6 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("rewind", () => Player.GameplayClockContainer.Seek(-80000)); AddUntilStep("key counter reset", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0)); - double? time = null; - - AddStep("store time", () => time = Player.GameplayClockContainer.GameplayClock.CurrentTime); - - // test seek via keyboard - AddStep("seek with right arrow key", () => InputManager.Key(Key.Right)); - AddAssert("time seeked forward", () => Player.GameplayClockContainer.GameplayClock.CurrentTime > time + 2000); - - AddStep("store time", () => time = Player.GameplayClockContainer.GameplayClock.CurrentTime); - AddStep("seek with left arrow key", () => InputManager.Key(Key.Left)); - AddAssert("time seeked backward", () => Player.GameplayClockContainer.GameplayClock.CurrentTime < time); - seekToBreak(0); seekToBreak(1); From a9250a0d984acdbcc6a92daebc1009cae3e2feaf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jul 2021 18:23:27 +0900 Subject: [PATCH 0284/2442] Limit update notifications to once per startup This logic was intentionally designed to continue to prompt the user to update if they haven't, but that seems pretty anti-user. The change will stop the update prompts from showing more than once per game startup, unless manually invoked by the user a second time. Closes https://github.com/ppy/osu/issues/13821. --- osu.Desktop/Updater/SquirrelUpdateManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Desktop/Updater/SquirrelUpdateManager.cs b/osu.Desktop/Updater/SquirrelUpdateManager.cs index 58d67c11d9..73f53721d1 100644 --- a/osu.Desktop/Updater/SquirrelUpdateManager.cs +++ b/osu.Desktop/Updater/SquirrelUpdateManager.cs @@ -68,6 +68,8 @@ namespace osu.Desktop.Updater return false; } + scheduleRecheck = false; + if (notification == null) { notification = new UpdateProgressNotification(this) { State = ProgressNotificationState.Active }; @@ -98,7 +100,6 @@ namespace osu.Desktop.Updater // could fail if deltas are unavailable for full update path (https://github.com/Squirrel/Squirrel.Windows/issues/959) // try again without deltas. await checkForUpdateAsync(false, notification).ConfigureAwait(false); - scheduleRecheck = false; } else { From 494089e402dcd358dc6d1e20eadf8736fa858407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 10 Jul 2021 11:22:54 +0200 Subject: [PATCH 0285/2442] Fix up English in comment --- osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index 02dc6f61c8..51ccdb7410 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Edit if (deltaX == 0) { - // Even there is no positional change, there may be a time change. + // Even if there is no positional change, there may be a time change. return true; } From c5011865fca0f93672703d183b3268d5071cbd86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 10 Jul 2021 11:23:38 +0200 Subject: [PATCH 0286/2442] Invert strangely negated condition --- osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index 51ccdb7410..7eebf04ca2 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -75,7 +75,7 @@ namespace osu.Game.Rulesets.Catch.Edit float upperBound = CatchPlayfield.WIDTH - maxX; // The inequality may be unsatisfiable if the objects were already out of bounds. // In that case, don't move objects at all. - if (!(lowerBound <= upperBound)) + if (lowerBound > upperBound) return 0; return Math.Clamp(deltaX, lowerBound, upperBound); From b705213ea930473e8f84f4cc92cb96fbc3dcb89f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 10 Jul 2021 11:44:32 +0200 Subject: [PATCH 0287/2442] Update test to match expectations after refactor --- .../Visual/UserInterface/TestSceneModSelectOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 91edc226c9..3485d7fbc3 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -80,7 +80,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("show", () => modSelect.Show()); AddAssert("ensure first is unchanged", () => SelectedMods.Value.OfType().Single().CircleSize.Value == 8); - AddAssert("ensure second is default", () => selectedMods2.Value.OfType().Single().CircleSize.Value == 5); + AddAssert("ensure second is default", () => selectedMods2.Value.OfType().Single().CircleSize.Value == null); } [Test] From e10b7867c158059e9f041edbdbc5ac10011f2203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 10 Jul 2021 12:13:36 +0200 Subject: [PATCH 0288/2442] Rewrite method again to hopefully help readability --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 54 ++++++++++++---------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 4c856ef4a0..cd63b618bd 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -148,7 +148,7 @@ namespace osu.Game.Rulesets.Osu.Mods /// The that this slider has been shifted by. private Vector2 moveSliderIntoPlayfield(Slider slider, RandomObjectInfo currentObjectInfo) { - var area = getSliderPlacementArea(slider); + var area = calculatePossibleMovementBounds(slider); var prevPosition = slider.Position; @@ -192,46 +192,52 @@ namespace osu.Game.Rulesets.Osu.Mods } /// - /// Calculates a that includes all possible positions of the slider such that - /// the entire slider is inside the playfield. + /// Calculates a which contains all of the possible movements of the slider (in relative X/Y coordinates) + /// such that the entire slider is inside the playfield. /// /// /// If the slider is larger than the playfield, the returned may have negative width/height. /// - private RectangleF getSliderPlacementArea(Slider slider) + private RectangleF calculatePossibleMovementBounds(Slider slider) { var pathPositions = new List(); slider.Path.GetPathToProgress(pathPositions, 0, 1); - // Initially, assume that the slider can be placed anywhere in the playfield - var box = new RectangleF(Vector2.Zero, OsuPlayfield.BASE_SIZE); + float minX = float.PositiveInfinity; + float maxX = float.NegativeInfinity; - // Then narrow down the area with each path position + float minY = float.PositiveInfinity; + float maxY = float.NegativeInfinity; + + // Compute the bounding box of the slider. foreach (var pos in pathPositions) { - // Reduce Width and Height accordingly after increasing X and Y - // to keep the right and bottom edge of the rectangle in place - var right = box.Right; - box.X = Math.Max(box.X, -pos.X); - box.Width = right - box.X; + minX = MathF.Min(minX, pos.X); + maxX = MathF.Max(maxX, pos.X); - var bottom = box.Bottom; - box.Y = Math.Max(box.Y, -pos.Y); - box.Height = bottom - box.Y; - - box.Width = Math.Min(box.Width, OsuPlayfield.BASE_SIZE.X - pos.X - box.X); - box.Height = Math.Min(box.Height, OsuPlayfield.BASE_SIZE.Y - pos.Y - box.Y); + minY = MathF.Min(minY, pos.Y); + maxY = MathF.Max(maxY, pos.Y); } - // Reduce the area by slider radius, so that the slider fits inside the playfield completely + // Take the circle radius into account. var radius = (float)slider.Radius; - box.X += radius; - box.Y += radius; - box.Width -= radius * 2; - box.Height -= radius * 2; + minX -= radius; + minY -= radius; - return box; + maxX += radius; + maxY += radius; + + // Given the bounding box of the slider (via min/max X/Y), + // the amount that the slider can move to the left is minX (with the sign flipped, since positive X is to the right), + // and the amount that it can move to the right is WIDTH - maxX. + // Same calculation applies for the Y axis. + float left = -minX; + float right = OsuPlayfield.BASE_SIZE.X - maxX; + float top = -minY; + float bottom = OsuPlayfield.BASE_SIZE.Y - maxY; + + return new RectangleF(left, top, right - left, bottom - top); } /// From 5cd11a02baeea3cd5f5766dcaf8bf1cabb424d16 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sat, 10 Jul 2021 17:56:37 +0700 Subject: [PATCH 0289/2442] add autolink test --- .../UserInterface/TestSceneOsuMarkdownContainer.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuMarkdownContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuMarkdownContainer.cs index 931af7bc95..82e26cb87d 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuMarkdownContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuMarkdownContainer.cs @@ -95,6 +95,15 @@ _**italic with underscore, bold with asterisk**_"; }); } + [Test] + public void TestAutoLink() + { + AddStep("Add autolink", () => + { + markdownContainer.Text = ""; + }); + } + [Test] public void TestInlineCode() { From 45ff28f83baae57d8a8ae4fc2f6c4f9f9ff04b2f Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sat, 10 Jul 2021 17:57:33 +0700 Subject: [PATCH 0290/2442] add autolink constructor --- .../Graphics/Containers/Markdown/OsuMarkdownLinkText.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownLinkText.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownLinkText.cs index f91a0e40e3..82e556f653 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownLinkText.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownLinkText.cs @@ -26,6 +26,12 @@ namespace osu.Game.Graphics.Containers.Markdown title = linkInline.Title; } + public OsuMarkdownLinkText(AutolinkInline autolinkInline) + : base(autolinkInline) + { + text = autolinkInline.Url; + } + [BackgroundDependencyLoader] private void load() { From e4f13e311ea7f36672ed9f8027c5ee597f3ca3ee Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sat, 10 Jul 2021 17:58:00 +0700 Subject: [PATCH 0291/2442] override add auto link in text flow container --- .../Containers/Markdown/OsuMarkdownTextFlowContainer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs index 36b48b7769..a7cd6b3905 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs @@ -17,6 +17,9 @@ namespace osu.Game.Graphics.Containers.Markdown protected override void AddLinkText(string text, LinkInline linkInline) => AddDrawable(new OsuMarkdownLinkText(text, linkInline)); + protected override void AddAutoLink(AutolinkInline autolinkInline) + => AddDrawable(new OsuMarkdownLinkText(autolinkInline)); + protected override void AddImage(LinkInline linkInline) => AddDrawable(new OsuMarkdownImage(linkInline)); // TODO : Change font to monospace From c44558e3c880ab5bf1384449acbb575b2267d4a8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 10 Jul 2021 17:57:52 +0300 Subject: [PATCH 0292/2442] Add back `LoadComplete` override --- osu.Game/Overlays/RestoreDefaultValueButton.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Overlays/RestoreDefaultValueButton.cs b/osu.Game/Overlays/RestoreDefaultValueButton.cs index 469d48d82b..fd3ee16fe6 100644 --- a/osu.Game/Overlays/RestoreDefaultValueButton.cs +++ b/osu.Game/Overlays/RestoreDefaultValueButton.cs @@ -78,6 +78,12 @@ namespace osu.Game.Overlays }; } + protected override void LoadComplete() + { + base.LoadComplete(); + UpdateState(); + } + public LocalisableString TooltipText => "revert to default"; protected override bool OnHover(HoverEvent e) From a1f3adc32004200198b227e3047c391ef828b754 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 10 Jul 2021 19:56:44 +0300 Subject: [PATCH 0293/2442] Add simple test cases --- .../Visual/Settings/TestSceneSettingsItem.cs | 60 +++++++++++++++---- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs index f63145f534..ebcdc8c702 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs @@ -4,7 +4,6 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Bindables; -using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Overlays.Settings; using osu.Game.Overlays; @@ -17,28 +16,63 @@ namespace osu.Game.Tests.Visual.Settings [Test] public void TestRestoreDefaultValueButtonVisibility() { - TestSettingsTextBox textBox = null; + SettingsTextBox textBox = null; + RestoreDefaultValueButton restoreDefaultValueButton = null; - AddStep("create settings item", () => Child = textBox = new TestSettingsTextBox + AddStep("create settings item", () => { - Current = new Bindable + Child = textBox = new SettingsTextBox { - Default = "test", - Value = "test" - } + Current = new Bindable + { + Default = "test", + Value = "test" + } + }; + + restoreDefaultValueButton = textBox.ChildrenOfType>().Single(); }); - AddAssert("restore button hidden", () => textBox.RestoreDefaultValueButton.Alpha == 0); + AddAssert("restore button hidden", () => restoreDefaultValueButton.Alpha == 0); AddStep("change value from default", () => textBox.Current.Value = "non-default"); - AddUntilStep("restore button shown", () => textBox.RestoreDefaultValueButton.Alpha > 0); + AddUntilStep("restore button shown", () => restoreDefaultValueButton.Alpha > 0); AddStep("restore default", () => textBox.Current.SetDefault()); - AddUntilStep("restore button hidden", () => textBox.RestoreDefaultValueButton.Alpha == 0); + AddUntilStep("restore button hidden", () => restoreDefaultValueButton.Alpha == 0); } - private class TestSettingsTextBox : SettingsTextBox + /// + /// Tests precision of the restore default value button, with a couple of single floating-point numbers that could potentially underflow. + /// + [TestCase(4.2f)] + [TestCase(9.9f)] + public void TestRestoreDefaultValueButtonPrecision(float initialValue) { - public Drawable RestoreDefaultValueButton => this.ChildrenOfType>().Single(); + SettingsSlider sliderBar = null; + RestoreDefaultValueButton restoreDefaultValueButton = null; + + AddStep("create settings item", () => + { + Child = sliderBar = new SettingsSlider + { + Current = new BindableFloat(initialValue) + { + MinValue = 0f, + MaxValue = 10f, + Precision = 0.1f, + } + }; + + restoreDefaultValueButton = sliderBar.ChildrenOfType>().Single(); + }); + + AddAssert("restore button hidden", () => restoreDefaultValueButton.Alpha == 0); + + AddStep("change value to 5.1f", () => sliderBar.Current.Value = 5.0f); + AddUntilStep("restore button shown", () => restoreDefaultValueButton.Alpha > 0); + + AddStep("restore default", () => sliderBar.Current.SetDefault()); + AddUntilStep("restore button hidden", () => restoreDefaultValueButton.Alpha == 0); } } -} \ No newline at end of file +} From 07ede7a14745c3b1adf4f22e704f1a5aa5529d75 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 11 Jul 2021 03:34:57 +0300 Subject: [PATCH 0294/2442] Disallow custom rulesets from score submission --- osu.Game/Screens/Play/SoloPlayer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/SoloPlayer.cs b/osu.Game/Screens/Play/SoloPlayer.cs index ef1087dd62..d90e8e0168 100644 --- a/osu.Game/Screens/Play/SoloPlayer.cs +++ b/osu.Game/Screens/Play/SoloPlayer.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using osu.Game.Online.API; using osu.Game.Online.Rooms; using osu.Game.Online.Solo; +using osu.Game.Rulesets; using osu.Game.Scoring; namespace osu.Game.Screens.Play @@ -27,7 +28,7 @@ namespace osu.Game.Screens.Play if (!(Beatmap.Value.BeatmapInfo.OnlineBeatmapID is int beatmapId)) return null; - if (!(Ruleset.Value.ID is int rulesetId)) + if (!(Ruleset.Value.ID is int rulesetId) || Ruleset.Value.ID > ILegacyRuleset.MAX_LEGACY_RULESET_ID) return null; return new CreateSoloScoreRequest(beatmapId, rulesetId, Game.VersionHash); From 6b8de2a10b6fc3cdbea511a66a4673c824c60fe8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 11 Jul 2021 03:35:35 +0300 Subject: [PATCH 0295/2442] Add test coverage for excluded cases in score submission --- .../TestScenePlayerScoreSubmission.cs | 92 +++++++++++++++---- 1 file changed, 73 insertions(+), 19 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs index f9ccb10778..5ff2e9c439 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs @@ -1,6 +1,7 @@ // 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.Linq; using NUnit.Framework; using osu.Framework.Screens; @@ -10,6 +11,7 @@ using osu.Game.Online.Rooms; using osu.Game.Online.Solo; using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Ranking; @@ -17,17 +19,26 @@ using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual.Gameplay { - public class TestScenePlayerScoreSubmission : OsuPlayerTestScene + public class TestScenePlayerScoreSubmission : PlayerTestScene { protected override bool AllowFail => allowFail; private bool allowFail; + private Func createCustomBeatmap; + private Func createCustomRuleset; + private DummyAPIAccess dummyAPI => (DummyAPIAccess)API; protected override bool HasCustomSteps => true; - protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) + protected override TestPlayer CreatePlayer(Ruleset ruleset) => new TestPlayer(false); + + protected override Ruleset CreatePlayerRuleset() => createCustomRuleset?.Invoke() ?? new OsuRuleset(); + + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => createCustomBeatmap?.Invoke(ruleset) ?? createTestBeatmap(ruleset); + + private IBeatmap createTestBeatmap(RulesetInfo ruleset) { var beatmap = (TestBeatmap)base.CreateBeatmap(ruleset); @@ -36,14 +47,12 @@ namespace osu.Game.Tests.Visual.Gameplay return beatmap; } - protected override TestPlayer CreatePlayer(Ruleset ruleset) => new TestPlayer(false); - [Test] public void TestNoSubmissionOnResultsWithNoToken() { prepareTokenResponse(false); - CreateTest(() => allowFail = false); + createPlayerTest(); AddUntilStep("wait for token request", () => Player.TokenCreationRequested); @@ -63,7 +72,7 @@ namespace osu.Game.Tests.Visual.Gameplay { prepareTokenResponse(true); - CreateTest(() => allowFail = false); + createPlayerTest(); AddUntilStep("wait for token request", () => Player.TokenCreationRequested); @@ -82,7 +91,7 @@ namespace osu.Game.Tests.Visual.Gameplay { prepareTokenResponse(false); - CreateTest(() => allowFail = false); + createPlayerTest(); AddUntilStep("wait for token request", () => Player.TokenCreationRequested); @@ -99,7 +108,7 @@ namespace osu.Game.Tests.Visual.Gameplay { prepareTokenResponse(true); - CreateTest(() => allowFail = true); + createPlayerTest(true); AddUntilStep("wait for token request", () => Player.TokenCreationRequested); @@ -114,7 +123,7 @@ namespace osu.Game.Tests.Visual.Gameplay { prepareTokenResponse(true); - CreateTest(() => allowFail = true); + createPlayerTest(true); AddUntilStep("wait for token request", () => Player.TokenCreationRequested); @@ -131,7 +140,7 @@ namespace osu.Game.Tests.Visual.Gameplay { prepareTokenResponse(true); - CreateTest(() => allowFail = false); + createPlayerTest(); AddUntilStep("wait for token request", () => Player.TokenCreationRequested); @@ -144,7 +153,7 @@ namespace osu.Game.Tests.Visual.Gameplay { prepareTokenResponse(true); - CreateTest(() => allowFail = false); + createPlayerTest(); AddUntilStep("wait for token request", () => Player.TokenCreationRequested); @@ -154,18 +163,49 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("ensure failing submission", () => Player.SubmittedScore?.ScoreInfo.Passed == false); } - private void addFakeHit() + [Test] + public void TestNoSubmissionOnLocalBeatmap() { - AddUntilStep("wait for first result", () => Player.Results.Count > 0); + prepareTokenResponse(true); - AddStep("force successfuly hit", () => + createPlayerTest(false, r => { - Player.ScoreProcessor.RevertResult(Player.Results.First()); - Player.ScoreProcessor.ApplyResult(new OsuJudgementResult(Beatmap.Value.Beatmap.HitObjects.First(), new OsuJudgement()) - { - Type = HitResult.Great, - }); + var beatmap = createTestBeatmap(r); + beatmap.BeatmapInfo.OnlineBeatmapID = null; + return beatmap; }); + + AddUntilStep("wait for token request", () => Player.TokenCreationRequested); + + addFakeHit(); + + AddStep("exit", () => Player.Exit()); + AddAssert("ensure no submission", () => Player.SubmittedScore == null); + } + + [Test] + public void TestNoSubmissionOnCustomRuleset() + { + prepareTokenResponse(true); + + createPlayerTest(false, createRuleset: () => new OsuRuleset { RulesetInfo = { ID = 10 } }); + + AddUntilStep("wait for token request", () => Player.TokenCreationRequested); + + addFakeHit(); + + AddStep("exit", () => Player.Exit()); + AddAssert("ensure no submission", () => Player.SubmittedScore == null); + } + + private void createPlayerTest(bool allowFail = false, Func createBeatmap = null, Func createRuleset = null) + { + CreateTest(() => AddStep("set up requirements", () => + { + this.allowFail = allowFail; + createCustomBeatmap = createBeatmap; + createCustomRuleset = createRuleset; + })); } private void prepareTokenResponse(bool validToken) @@ -188,5 +228,19 @@ namespace osu.Game.Tests.Visual.Gameplay }; }); } + + private void addFakeHit() + { + AddUntilStep("wait for first result", () => Player.Results.Count > 0); + + AddStep("force successfuly hit", () => + { + Player.ScoreProcessor.RevertResult(Player.Results.First()); + Player.ScoreProcessor.ApplyResult(new OsuJudgementResult(Beatmap.Value.Beatmap.HitObjects.First(), new OsuJudgement()) + { + Type = HitResult.Great, + }); + }); + } } } From f21ea3b790812bf48d108601191b11e1b462f087 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 11 Jul 2021 03:46:19 +0300 Subject: [PATCH 0296/2442] Update player test scene `Ruleset` bindable from creation method --- osu.Game/Tests/Visual/PlayerTestScene.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/PlayerTestScene.cs b/osu.Game/Tests/Visual/PlayerTestScene.cs index 088e997de9..93491c800f 100644 --- a/osu.Game/Tests/Visual/PlayerTestScene.cs +++ b/osu.Game/Tests/Visual/PlayerTestScene.cs @@ -57,7 +57,9 @@ namespace osu.Game.Tests.Visual protected void LoadPlayer() { - var ruleset = Ruleset.Value.CreateInstance(); + var ruleset = CreatePlayerRuleset(); + Ruleset.Value = ruleset.RulesetInfo; + var beatmap = CreateBeatmap(ruleset.RulesetInfo); Beatmap.Value = CreateWorkingBeatmap(beatmap); From 79d546afa2efef11dc7035567ebd9caefaa539e0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Jul 2021 10:14:42 +0900 Subject: [PATCH 0297/2442] Add missing osu!catch difficulty adjust attributes --- osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs index bd78d3b085..e59a0a0431 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Catch.Mods { public class CatchModDifficultyAdjust : ModDifficultyAdjust, IApplicableToBeatmapProcessor { - [SettingSource("Circle Size", "Override a beatmap's set CS.", FIRST_SETTING_ORDER - 1)] + [SettingSource("Circle Size", "Override a beatmap's set CS.", FIRST_SETTING_ORDER - 1, SettingControlType = typeof(DifficultyAdjustSettingsControl))] public DifficultyBindable CircleSize { get; } = new DifficultyBindable { Precision = 0.1f, @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.Mods ReadCurrentFromDifficulty = diff => diff.CircleSize, }; - [SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1)] + [SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1, SettingControlType = typeof(DifficultyAdjustSettingsControl))] public DifficultyBindable ApproachRate { get; } = new DifficultyBindable { Precision = 0.1f, From 32b4f5fbd60239274af90153d35e4db631986619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 11 Jul 2021 15:12:46 +0200 Subject: [PATCH 0298/2442] Do not store direct references to original bindable `DifficultyAdjustSettingsControl` and its inner `SliderControl` were holding different references to `DifficultyBindable`s from the difficulty adjust mod, therefore leading to bindings being lost to the framework-side automatic unbind logic if the mod was toggled off and back on in rapid succession. Resolve by adding a shadowed implementation of `GetBoundCopy()` and using it to isolate the controls from the mod bindable. --- .../Rulesets/Mods/DifficultyAdjustSettingsControl.cs | 4 ++-- osu.Game/Rulesets/Mods/DifficultyBindable.cs | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs index 0f48ed4190..067657159b 100644 --- a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs +++ b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs @@ -41,10 +41,10 @@ namespace osu.Game.Rulesets.Mods { // Intercept and extract the internal number bindable from DifficultyBindable. // This will provide bounds and precision specifications for the slider bar. - difficultyBindable = (DifficultyBindable)value; + difficultyBindable = ((DifficultyBindable)value).GetBoundCopy(); sliderDisplayCurrent.BindTo(difficultyBindable.CurrentNumber); - base.Current = value; + base.Current = difficultyBindable; } } diff --git a/osu.Game/Rulesets/Mods/DifficultyBindable.cs b/osu.Game/Rulesets/Mods/DifficultyBindable.cs index 26538fa4e3..0f16126464 100644 --- a/osu.Game/Rulesets/Mods/DifficultyBindable.cs +++ b/osu.Game/Rulesets/Mods/DifficultyBindable.cs @@ -92,5 +92,16 @@ namespace osu.Game.Rulesets.Mods { CurrentNumber.MaxValue = ExtendedLimits.Value && extendedMaxValue != null ? extendedMaxValue.Value : maxValue; } + + public new DifficultyBindable GetBoundCopy() => new DifficultyBindable + { + BindTarget = this, + CurrentNumber = { BindTarget = CurrentNumber }, + ExtendedLimits = { BindTarget = ExtendedLimits }, + ReadCurrentFromDifficulty = ReadCurrentFromDifficulty, + // the following is only safe as long as these values are effectively constants. + MaxValue = maxValue, + ExtendedMaxValue = extendedMaxValue + }; } } From c181a724c6e903e61d43f23f5ff12f65ae183788 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sun, 11 Jul 2021 22:01:28 +0800 Subject: [PATCH 0299/2442] Refactor hit object clamping --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 99 +++++++++++++--------- 1 file changed, 59 insertions(+), 40 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index cd63b618bd..e134dcef89 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -67,34 +67,32 @@ namespace osu.Game.Rulesets.Osu.Mods applyRandomisation(rateOfChangeMultiplier, previous, current); - // Move hit objects back into the playfield if they are outside of it, - // which would sometimes happen during big jumps otherwise. - current.PositionRandomised = clampToPlayfield(current.PositionRandomised, (float)hitObject.Radius); + // Move hit objects back into the playfield if they are outside of it + Vector2 shift = Vector2.Zero; - hitObject.Position = current.PositionRandomised; - - // update end position as it may have changed as a result of the position update. - current.EndPositionRandomised = current.PositionRandomised; - - if (hitObject is Slider slider) + if (hitObject is HitCircle circle) { - Vector2 shift = moveSliderIntoPlayfield(slider, current); + shift = clampHitCircleToPlayfield(circle, current); + } + else if (hitObject is Slider slider) + { + shift = clampSliderToPlayfield(slider, current); + } - if (shift != Vector2.Zero) + if (shift != Vector2.Zero) + { + var toBeShifted = new List(); + + for (int j = i - 1; j >= i - objects_to_shift_before_slider && j >= 0; j--) { - var toBeShifted = new List(); + // only shift hit circles + if (!(hitObjects[j] is HitCircle)) break; - for (int j = i - 1; j >= i - objects_to_shift_before_slider && j >= 0; j--) - { - // only shift hit circles - if (!(hitObjects[j] is HitCircle)) break; - - toBeShifted.Add(hitObjects[j]); - } - - if (toBeShifted.Count > 0) - applyDecreasingShift(toBeShifted, shift); + toBeShifted.Add(hitObjects[j]); } + + if (toBeShifted.Count > 0) + applyDecreasingShift(toBeShifted, shift); } previous = current; @@ -145,31 +143,29 @@ namespace osu.Game.Rulesets.Osu.Mods /// /// Moves the and all necessary nested s into the if they aren't already. /// - /// The that this slider has been shifted by. - private Vector2 moveSliderIntoPlayfield(Slider slider, RandomObjectInfo currentObjectInfo) + /// The deviation from the original randomised position in order to fit within the playfield. + private Vector2 clampSliderToPlayfield(Slider slider, RandomObjectInfo objectInfo) { var area = calculatePossibleMovementBounds(slider); - var prevPosition = slider.Position; + var previousPosition = objectInfo.PositionRandomised; // Clamp slider position to the placement area // If the slider is larger than the playfield, force it to stay at the original position var newX = area.Width < 0 - ? currentObjectInfo.PositionOriginal.X - : Math.Clamp(slider.Position.X, area.Left, area.Right); + ? objectInfo.PositionOriginal.X + : Math.Clamp(previousPosition.X, area.Left, area.Right); var newY = area.Height < 0 - ? currentObjectInfo.PositionOriginal.Y - : Math.Clamp(slider.Position.Y, area.Top, area.Bottom); + ? objectInfo.PositionOriginal.Y + : Math.Clamp(previousPosition.Y, area.Top, area.Bottom); - slider.Position = new Vector2(newX, newY); + slider.Position = objectInfo.PositionRandomised = new Vector2(newX, newY); + objectInfo.EndPositionRandomised = slider.EndPosition; - currentObjectInfo.PositionRandomised = slider.Position; - currentObjectInfo.EndPositionRandomised = slider.EndPosition; + shiftNestedObjects(slider, objectInfo.PositionRandomised - objectInfo.PositionOriginal); - shiftNestedObjects(slider, currentObjectInfo.PositionRandomised - currentObjectInfo.PositionOriginal); - - return slider.Position - prevPosition; + return objectInfo.PositionRandomised - previousPosition; } /// @@ -187,7 +183,7 @@ namespace osu.Game.Rulesets.Osu.Mods // The last object is shifted by a vector slightly larger than zero Vector2 position = hitObject.Position + shift * ((hitObjects.Count - i) / (float)(hitObjects.Count + 1)); - hitObject.Position = clampToPlayfield(position, (float)hitObject.Radius); + hitObject.Position = clampToPlayfieldWithPadding(position, (float)hitObject.Radius); } } @@ -256,12 +252,35 @@ namespace osu.Game.Rulesets.Osu.Mods } } - private Vector2 clampToPlayfield(Vector2 position, float radius) + /// + /// Move the randomised position of a hit circle so that it fits inside the playfield. + /// + /// The deviation from the original randomised position in order to fit within the playfield. + private Vector2 clampHitCircleToPlayfield(HitCircle circle, RandomObjectInfo objectInfo) { - position.X = Math.Clamp(position.X, radius, OsuPlayfield.BASE_SIZE.X - radius); - position.Y = Math.Clamp(position.Y, radius, OsuPlayfield.BASE_SIZE.Y - radius); + var previousPosition = objectInfo.PositionRandomised; + objectInfo.EndPositionRandomised = objectInfo.PositionRandomised = clampToPlayfieldWithPadding( + objectInfo.PositionRandomised, + (float)circle.Radius + ); - return position; + circle.Position = objectInfo.PositionRandomised; + + return objectInfo.PositionRandomised - previousPosition; + } + + /// + /// Clamp a position to playfield, keeping a specified distance from the edges. + /// + /// The position to be clamped. + /// The minimum distance allowed from playfield edges. + /// The clamped position. + private Vector2 clampToPlayfieldWithPadding(Vector2 position, float padding) + { + return new Vector2( + Math.Clamp(position.X, padding, OsuPlayfield.BASE_SIZE.X - padding), + Math.Clamp(position.Y, padding, OsuPlayfield.BASE_SIZE.Y - padding) + ); } private class RandomObjectInfo From 7aecafeecb26f72ca1d2e7a1d2f72b266a2b09a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 11 Jul 2021 16:46:30 +0200 Subject: [PATCH 0300/2442] Rename constant to reflect its purpose --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index e134dcef89..4409017ac9 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -27,9 +27,9 @@ namespace osu.Game.Rulesets.Osu.Mods private static readonly float playfield_diagonal = OsuPlayfield.BASE_SIZE.LengthFast; /// - /// Number of previous hit circles to be shifted together when a slider needs to be moved. + /// Number of previous hitobjects to be shifted together when another object is being moved. /// - private const int objects_to_shift_before_slider = 10; + private const int preceding_hitobjects_to_shift = 10; private Random rng; @@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Osu.Mods { var toBeShifted = new List(); - for (int j = i - 1; j >= i - objects_to_shift_before_slider && j >= 0; j--) + for (int j = i - 1; j >= i - preceding_hitobjects_to_shift && j >= 0; j--) { // only shift hit circles if (!(hitObjects[j] is HitCircle)) break; From 63dedb36dec1612c5d68c0293d1d551fbecd57fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 11 Jul 2021 16:49:23 +0200 Subject: [PATCH 0301/2442] Rename variable --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 4409017ac9..f919ecf839 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -146,19 +146,19 @@ namespace osu.Game.Rulesets.Osu.Mods /// The deviation from the original randomised position in order to fit within the playfield. private Vector2 clampSliderToPlayfield(Slider slider, RandomObjectInfo objectInfo) { - var area = calculatePossibleMovementBounds(slider); + var possibleMovementBounds = calculatePossibleMovementBounds(slider); var previousPosition = objectInfo.PositionRandomised; // Clamp slider position to the placement area // If the slider is larger than the playfield, force it to stay at the original position - var newX = area.Width < 0 + var newX = possibleMovementBounds.Width < 0 ? objectInfo.PositionOriginal.X - : Math.Clamp(previousPosition.X, area.Left, area.Right); + : Math.Clamp(previousPosition.X, possibleMovementBounds.Left, possibleMovementBounds.Right); - var newY = area.Height < 0 + var newY = possibleMovementBounds.Height < 0 ? objectInfo.PositionOriginal.Y - : Math.Clamp(previousPosition.Y, area.Top, area.Bottom); + : Math.Clamp(previousPosition.Y, possibleMovementBounds.Top, possibleMovementBounds.Bottom); slider.Position = objectInfo.PositionRandomised = new Vector2(newX, newY); objectInfo.EndPositionRandomised = slider.EndPosition; From 9e70136100c6550abd53ef07aa36adc0318b2c24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 11 Jul 2021 17:26:00 +0200 Subject: [PATCH 0302/2442] Adjust test case slightly --- osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs index ebcdc8c702..df59b9284b 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs @@ -42,12 +42,14 @@ namespace osu.Game.Tests.Visual.Settings } /// - /// Tests precision of the restore default value button, with a couple of single floating-point numbers that could potentially underflow. + /// Ensures that the reset to default button uses the correct implementation of IsDefault to determine whether it should be shown or not. + /// Values have been chosen so that after being set, Value != Default (but they are close enough that the difference is negligible compared to Precision). /// [TestCase(4.2f)] [TestCase(9.9f)] public void TestRestoreDefaultValueButtonPrecision(float initialValue) { + BindableFloat current = null; SettingsSlider sliderBar = null; RestoreDefaultValueButton restoreDefaultValueButton = null; @@ -55,7 +57,7 @@ namespace osu.Game.Tests.Visual.Settings { Child = sliderBar = new SettingsSlider { - Current = new BindableFloat(initialValue) + Current = current = new BindableFloat(initialValue) { MinValue = 0f, MaxValue = 10f, @@ -68,7 +70,7 @@ namespace osu.Game.Tests.Visual.Settings AddAssert("restore button hidden", () => restoreDefaultValueButton.Alpha == 0); - AddStep("change value to 5.1f", () => sliderBar.Current.Value = 5.0f); + AddStep("change value to next closest", () => sliderBar.Current.Value += current.Precision * 0.6f); AddUntilStep("restore button shown", () => restoreDefaultValueButton.Alpha > 0); AddStep("restore default", () => sliderBar.Current.SetDefault()); From a6258d705ed1f188061333ac7299e5c55ca81761 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jul 2021 11:26:30 +0900 Subject: [PATCH 0303/2442] Make `CurrentNumber` internal --- osu.Game/Rulesets/Mods/DifficultyBindable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/DifficultyBindable.cs b/osu.Game/Rulesets/Mods/DifficultyBindable.cs index 0f16126464..1f76a3e366 100644 --- a/osu.Game/Rulesets/Mods/DifficultyBindable.cs +++ b/osu.Game/Rulesets/Mods/DifficultyBindable.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mods /// An internal numeric bindable to hold and propagate min/max/precision. /// The value of this bindable should not be set. /// - public readonly BindableFloat CurrentNumber = new BindableFloat + internal readonly BindableFloat CurrentNumber = new BindableFloat { MinValue = 0, MaxValue = 10, From 3642febbb684180f937b6e9dc06d5b4a5695eade Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jul 2021 12:35:40 +0900 Subject: [PATCH 0304/2442] Fix one new incorrect formatting inspection from EAP6 --- .../Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs index 271fbde5c3..8cff790e80 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -31,10 +31,7 @@ namespace osu.Game.Tests.Visual.SongSelect private readonly TestBeatmapDifficultyCache testDifficultyCache = new TestBeatmapDifficultyCache(); [Test] - public void TestLocal([Values("Beatmap", "Some long title and stuff")] - string title, - [Values("Trial", "Some1's very hardest difficulty")] - string version) + public void TestLocal([Values("Beatmap", "Some long title and stuff")] string title, [Values("Trial", "Some1's very hardest difficulty")] string version) { showMetadataForBeatmap(() => CreateWorkingBeatmap(new Beatmap { From f548ba4f6925058b182319d597a0968d2aff29bb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jul 2021 14:07:17 +0900 Subject: [PATCH 0305/2442] Update realm libraries to fix windows 8.1 incompatibility --- 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 8a3c69e40c..f4cf01eb82 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From b4b0b862ef98c9ec300ba97a5a45d2b8de345a20 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Jul 2021 14:07:21 +0900 Subject: [PATCH 0306/2442] Adjust some layout --- .../OnlinePlay/Components/RoomStatusInfo.cs | 2 +- .../Lounge/Components/DrawableRoom.cs | 30 ++++++++++++++----- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomStatusInfo.cs b/osu.Game/Screens/OnlinePlay/Components/RoomStatusInfo.cs index 900dea1e8c..adb823c68a 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomStatusInfo.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomStatusInfo.cs @@ -34,7 +34,7 @@ namespace osu.Game.Screens.OnlinePlay.Components { AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - Spacing = new Vector2(2), + Spacing = new Vector2(4), Children = new Drawable[] { statusPill = new StatusPill(), diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 062b83b66c..8eb3f24b0d 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -98,7 +98,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Room = room; RelativeSizeAxes = Axes.X; - Height = height + SELECTION_BORDER_WIDTH * 2; + Height = height; CornerRadius = corner_radius + SELECTION_BORDER_WIDTH / 2; Masking = true; } @@ -125,11 +125,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components }, Children = new Drawable[] { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"#27302E"), - }, new Container { Anchor = Anchor.CentreRight, @@ -138,10 +133,29 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components FillMode = FillMode.Fill, Child = new OnlinePlayBackgroundSprite(BeatmapSetCoverType.List) { RelativeSizeAxes = Axes.Both } }, - new Box + new GridContainer { RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal(Color4Extensions.FromHex(@"#27302E"), Color4Extensions.FromHex(@"#27302E").Opacity(0.3f)) + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.Relative, 0.2f) + }, + Content = new[] + { + new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex(@"#27302E"), + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientHorizontal(Color4Extensions.FromHex(@"#27302E"), Color4Extensions.FromHex(@"#27302E").Opacity(0.3f)) + }, + } + } }, new Container { From 37dac1a775ee22035b3cf9660883912d7985a0d8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jul 2021 14:36:28 +0900 Subject: [PATCH 0307/2442] Update mobile projects' local references to older realm --- osu.Android.props | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 9280eaf97c..f8c446555d 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -56,6 +56,6 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 2eea646c61..79746be0b3 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -99,6 +99,6 @@ - + From 47a593ad7d9bb118eec72e484f7f3a7988139528 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jul 2021 14:55:09 +0900 Subject: [PATCH 0308/2442] Force a re-check on any exception being thrown --- osu.Desktop/Updater/SquirrelUpdateManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Desktop/Updater/SquirrelUpdateManager.cs b/osu.Desktop/Updater/SquirrelUpdateManager.cs index 73f53721d1..910751a723 100644 --- a/osu.Desktop/Updater/SquirrelUpdateManager.cs +++ b/osu.Desktop/Updater/SquirrelUpdateManager.cs @@ -111,6 +111,7 @@ namespace osu.Desktop.Updater catch (Exception) { // we'll ignore this and retry later. can be triggered by no internet connection or thread abortion. + scheduleRecheck = true; } finally { From 185e36bf97f1cf670be5f56340c8e4f44ce90cb6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Jul 2021 15:11:07 +0900 Subject: [PATCH 0309/2442] Split pill display into abstract class --- .../OnlinePlay/Components/EndDateInfo.cs | 65 +++++++++ .../OnlinePlay/Components/RoomInfoPill.cs | 78 ++++++++++ .../OnlinePlay/Components/RoomStatusInfo.cs | 135 ------------------ .../OnlinePlay/Components/RoomStatusPill.cs | 54 +++++++ .../Lounge/Components/DrawableRoom.cs | 13 +- .../OnlinePlay/Lounge/Components/RoomInfo.cs | 4 +- 6 files changed, 211 insertions(+), 138 deletions(-) create mode 100644 osu.Game/Screens/OnlinePlay/Components/EndDateInfo.cs create mode 100644 osu.Game/Screens/OnlinePlay/Components/RoomInfoPill.cs delete mode 100644 osu.Game/Screens/OnlinePlay/Components/RoomStatusInfo.cs create mode 100644 osu.Game/Screens/OnlinePlay/Components/RoomStatusPill.cs diff --git a/osu.Game/Screens/OnlinePlay/Components/EndDateInfo.cs b/osu.Game/Screens/OnlinePlay/Components/EndDateInfo.cs new file mode 100644 index 0000000000..3f93279461 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Components/EndDateInfo.cs @@ -0,0 +1,65 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Graphics; + +namespace osu.Game.Screens.OnlinePlay.Components +{ + public class EndDateInfo : OnlinePlayComposite + { + public EndDateInfo() + { + AutoSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = new EndDatePart + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 12), + EndDate = { BindTarget = EndDate } + }; + } + + private class EndDatePart : DrawableDate + { + public readonly IBindable EndDate = new Bindable(); + + public EndDatePart() + : base(DateTimeOffset.UtcNow) + { + EndDate.BindValueChanged(date => + { + // If null, set a very large future date to prevent unnecessary schedules. + Date = date.NewValue ?? DateTimeOffset.Now.AddYears(1); + }, true); + } + + protected override string Format() + { + if (EndDate.Value == null) + return string.Empty; + + var diffToNow = Date.Subtract(DateTimeOffset.Now); + + if (diffToNow.TotalSeconds < -5) + return $"Closed {base.Format()}"; + + if (diffToNow.TotalSeconds < 0) + return "Closed"; + + if (diffToNow.TotalSeconds < 5) + return "Closing soon"; + + return $"Closing {base.Format()}"; + } + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomInfoPill.cs b/osu.Game/Screens/OnlinePlay/Components/RoomInfoPill.cs new file mode 100644 index 0000000000..55de75cbcd --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Components/RoomInfoPill.cs @@ -0,0 +1,78 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Screens.OnlinePlay.Lounge.Components; +using osuTK.Graphics; + +namespace osu.Game.Screens.OnlinePlay.Components +{ + /// + /// Abstract class for "pill" components displayed as part of s. + /// + public abstract class RoomInfoPill : OnlinePlayComposite + { + private const float padding = 8; + + protected Drawable Background { get; private set; } + + protected RoomInfoPill() + { + AutoSizeAxes = Axes.X; + Height = 16; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Masking = true, + Children = new[] + { + Background = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.5f + }, + new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = padding }, + Child = new GridContainer + { + AutoSizeAxes = Axes.Both, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize, minSize: 80 - 2 * padding) + }, + Content = new[] + { + new[] + { + CreateContent().With(d => + { + d.Anchor = Anchor.Centre; + d.Origin = Anchor.Centre; + }) + } + } + } + } + } + }; + } + + protected abstract Drawable CreateContent(); + } +} diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomStatusInfo.cs b/osu.Game/Screens/OnlinePlay/Components/RoomStatusInfo.cs deleted file mode 100644 index adb823c68a..0000000000 --- a/osu.Game/Screens/OnlinePlay/Components/RoomStatusInfo.cs +++ /dev/null @@ -1,135 +0,0 @@ -// 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 osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Online.Rooms; -using osu.Game.Online.Rooms.RoomStatuses; -using osuTK; -using osuTK.Graphics; - -namespace osu.Game.Screens.OnlinePlay.Components -{ - public class RoomStatusInfo : OnlinePlayComposite - { - public RoomStatusInfo() - { - AutoSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader] - private void load() - { - StatusPill statusPill; - EndDatePart endDatePart; - - InternalChild = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(4), - Children = new Drawable[] - { - statusPill = new StatusPill(), - endDatePart = new EndDatePart - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 12) - } - } - }; - - statusPill.EndDate.BindTo(EndDate); - statusPill.Status.BindTo(Status); - endDatePart.EndDate.BindTo(EndDate); - } - - private class EndDatePart : DrawableDate - { - public readonly IBindable EndDate = new Bindable(); - - public EndDatePart() - : base(DateTimeOffset.UtcNow) - { - EndDate.BindValueChanged(date => - { - // If null, set a very large future date to prevent unnecessary schedules. - Date = date.NewValue ?? DateTimeOffset.Now.AddYears(1); - }, true); - } - - protected override string Format() - { - if (EndDate.Value == null) - return string.Empty; - - var diffToNow = Date.Subtract(DateTimeOffset.Now); - - if (diffToNow.TotalSeconds < -5) - return $"Closed {base.Format()}"; - - if (diffToNow.TotalSeconds < 0) - return "Closed"; - - if (diffToNow.TotalSeconds < 5) - return "Closing soon"; - - return $"Closing {base.Format()}"; - } - } - - private class StatusPill : CompositeDrawable - { - public readonly IBindable EndDate = new Bindable(); - public readonly IBindable Status = new Bindable(); - - [Resolved] - private OsuColour colours { get; set; } - - private Drawable background; - private SpriteText statusText; - - [BackgroundDependencyLoader] - private void load() - { - Size = new Vector2(60, 16); - - InternalChildren = new[] - { - background = new Circle { RelativeSizeAxes = Axes.Both }, - statusText = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 12), - Colour = Color4.Black - } - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - EndDate.BindValueChanged(_ => updateDisplay()); - Status.BindValueChanged(_ => updateDisplay(), true); - } - - private void updateDisplay() - { - RoomStatus status = EndDate.Value < DateTimeOffset.Now ? new RoomStatusEnded() : Status.Value ?? new RoomStatusOpen(); - - background.FadeColour(status.GetAppropriateColour(colours), 100); - statusText.Text = status.Message; - } - } - } -} diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomStatusPill.cs b/osu.Game/Screens/OnlinePlay/Components/RoomStatusPill.cs new file mode 100644 index 0000000000..6d9e84d618 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Components/RoomStatusPill.cs @@ -0,0 +1,54 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.Rooms; +using osu.Game.Online.Rooms.RoomStatuses; +using osuTK.Graphics; + +namespace osu.Game.Screens.OnlinePlay.Components +{ + /// + /// A pill that displays the room's current status. + /// + public class RoomStatusPill : RoomInfoPill + { + [Resolved] + private OsuColour colours { get; set; } + + private bool firstDisplay = true; + private SpriteText statusText; + + protected override void LoadComplete() + { + base.LoadComplete(); + + EndDate.BindValueChanged(_ => updateDisplay()); + Status.BindValueChanged(_ => updateDisplay(), true); + } + + private void updateDisplay() + { + RoomStatus status = EndDate.Value < DateTimeOffset.Now ? new RoomStatusEnded() : Status.Value ?? new RoomStatusOpen(); + + Background.Alpha = 1; + Background.FadeColour(status.GetAppropriateColour(colours), firstDisplay ? 0 : 100); + statusText.Text = status.Message; + + firstDisplay = false; + } + + protected override Drawable CreateContent() => statusText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 12), + Colour = Color4.Black + }; + } +} diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 8eb3f24b0d..56b3cfa11c 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -181,9 +181,20 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Origin = Anchor.BottomLeft, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, + Spacing = new Vector2(4), Children = new Drawable[] { - new StarRatingRangeDisplay { Scale = new Vector2(0.85f) } + new PlaylistInfoPill + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + new StarRatingRangeDisplay + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Scale = new Vector2(0.85f) + } } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInfo.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInfo.cs index a0a7f2dc28..819e19ad1e 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInfo.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInfo.cs @@ -21,7 +21,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components AutoSizeAxes = Axes.Y; RoomLocalUserInfo localUserInfo; - RoomStatusInfo statusInfo; + EndDateInfo statusInfo; ModeTypeInfo typeInfo; ParticipantInfo participantInfo; @@ -47,7 +47,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components AutoSizeAxes = Axes.Y, Children = new Drawable[] { - statusInfo = new RoomStatusInfo(), + statusInfo = new EndDateInfo(), typeInfo = new ModeTypeInfo { Anchor = Anchor.BottomRight, From a8cbffa57ea5620fc18e8095d6a4e61ee780a9fc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Jul 2021 15:11:53 +0900 Subject: [PATCH 0310/2442] Add playlist count pill --- .../Components/PlaylistCountPill.cs | 39 +++++++++++++++++++ .../Lounge/Components/DrawableRoom.cs | 22 ++++++++++- 2 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 osu.Game/Screens/OnlinePlay/Components/PlaylistCountPill.cs diff --git a/osu.Game/Screens/OnlinePlay/Components/PlaylistCountPill.cs b/osu.Game/Screens/OnlinePlay/Components/PlaylistCountPill.cs new file mode 100644 index 0000000000..7a4a638c2a --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Components/PlaylistCountPill.cs @@ -0,0 +1,39 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Specialized; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Screens.OnlinePlay.Components +{ + /// + /// A pill that displays the playlist item count. + /// + public class PlaylistCountPill : RoomInfoPill + { + private OsuTextFlowContainer count; + + protected override void LoadComplete() + { + base.LoadComplete(); + + Playlist.BindCollectionChanged(updateCount, true); + } + + private void updateCount(object sender, NotifyCollectionChangedEventArgs e) + { + count.Clear(); + count.AddText(Playlist.Count.ToString(), s => s.Font = s.Font.With(weight: FontWeight.Bold)); + count.AddText(" Maps"); + } + + protected override Drawable CreateContent() => count = new OsuTextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12)) + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + } +} diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 56b3cfa11c..b795b74e04 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -168,7 +168,25 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components }, Children = new Drawable[] { - new RoomStatusInfo(), + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(4), + Children = new Drawable[] + { + new RoomStatusPill + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft + }, + new EndDateInfo + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft + }, + } + }, new RoomName { Anchor = Anchor.CentreLeft, @@ -184,7 +202,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Spacing = new Vector2(4), Children = new Drawable[] { - new PlaylistInfoPill + new PlaylistCountPill { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, From 435b4b0e6ed4400125d1e6ce512b76237dc15ff9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Jul 2021 15:18:38 +0900 Subject: [PATCH 0311/2442] Remove pill inheritance --- .../{RoomInfoPill.cs => PillContainer.cs} | 27 +++++++------- .../Components/PlaylistCountPill.cs | 29 ++++++++++----- .../OnlinePlay/Components/RoomStatusPill.cs | 35 +++++++++++++------ 3 files changed, 57 insertions(+), 34 deletions(-) rename osu.Game/Screens/OnlinePlay/Components/{RoomInfoPill.cs => PillContainer.cs} (72%) diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomInfoPill.cs b/osu.Game/Screens/OnlinePlay/Components/PillContainer.cs similarity index 72% rename from osu.Game/Screens/OnlinePlay/Components/RoomInfoPill.cs rename to osu.Game/Screens/OnlinePlay/Components/PillContainer.cs index 55de75cbcd..10a14d16da 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomInfoPill.cs +++ b/osu.Game/Screens/OnlinePlay/Components/PillContainer.cs @@ -5,21 +5,27 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Game.Screens.OnlinePlay.Lounge.Components; using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay.Components { /// - /// Abstract class for "pill" components displayed as part of s. + /// Displays contents in a "pill". /// - public abstract class RoomInfoPill : OnlinePlayComposite + public class PillContainer : Container { private const float padding = 8; - protected Drawable Background { get; private set; } + public Drawable Background { get; private set; } - protected RoomInfoPill() + protected override Container Content { get; } = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + }; + + public PillContainer() { AutoSizeAxes = Axes.X; Height = 16; @@ -58,21 +64,12 @@ namespace osu.Game.Screens.OnlinePlay.Components }, Content = new[] { - new[] - { - CreateContent().With(d => - { - d.Anchor = Anchor.Centre; - d.Origin = Anchor.Centre; - }) - } + new[] { Content } } } } } }; } - - protected abstract Drawable CreateContent(); } } diff --git a/osu.Game/Screens/OnlinePlay/Components/PlaylistCountPill.cs b/osu.Game/Screens/OnlinePlay/Components/PlaylistCountPill.cs index 7a4a638c2a..2d9e7c143c 100644 --- a/osu.Game/Screens/OnlinePlay/Components/PlaylistCountPill.cs +++ b/osu.Game/Screens/OnlinePlay/Components/PlaylistCountPill.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Specialized; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -11,10 +12,29 @@ namespace osu.Game.Screens.OnlinePlay.Components /// /// A pill that displays the playlist item count. /// - public class PlaylistCountPill : RoomInfoPill + public class PlaylistCountPill : OnlinePlayComposite { private OsuTextFlowContainer count; + public PlaylistCountPill() + { + AutoSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = new PillContainer + { + Child = count = new OsuTextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12)) + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + }; + } + protected override void LoadComplete() { base.LoadComplete(); @@ -28,12 +48,5 @@ namespace osu.Game.Screens.OnlinePlay.Components count.AddText(Playlist.Count.ToString(), s => s.Font = s.Font.With(weight: FontWeight.Bold)); count.AddText(" Maps"); } - - protected override Drawable CreateContent() => count = new OsuTextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12)) - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }; } } diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomStatusPill.cs b/osu.Game/Screens/OnlinePlay/Components/RoomStatusPill.cs index 6d9e84d618..d18eb1bd94 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomStatusPill.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomStatusPill.cs @@ -16,14 +16,35 @@ namespace osu.Game.Screens.OnlinePlay.Components /// /// A pill that displays the room's current status. /// - public class RoomStatusPill : RoomInfoPill + public class RoomStatusPill : OnlinePlayComposite { [Resolved] private OsuColour colours { get; set; } private bool firstDisplay = true; + private PillContainer pill; private SpriteText statusText; + public RoomStatusPill() + { + AutoSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = pill = new PillContainer + { + Child = statusText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 12), + Colour = Color4.Black + } + }; + } + protected override void LoadComplete() { base.LoadComplete(); @@ -36,19 +57,11 @@ namespace osu.Game.Screens.OnlinePlay.Components { RoomStatus status = EndDate.Value < DateTimeOffset.Now ? new RoomStatusEnded() : Status.Value ?? new RoomStatusOpen(); - Background.Alpha = 1; - Background.FadeColour(status.GetAppropriateColour(colours), firstDisplay ? 0 : 100); + pill.Background.Alpha = 1; + pill.Background.FadeColour(status.GetAppropriateColour(colours), firstDisplay ? 0 : 100); statusText.Text = status.Message; firstDisplay = false; } - - protected override Drawable CreateContent() => statusText = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 12), - Colour = Color4.Black - }; } } From 0db316d644064bd168454fcb1354b804ebe23918 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jul 2021 16:08:20 +0900 Subject: [PATCH 0312/2442] Add password scaffolding --- osu.Game/Online/Rooms/Room.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index b28680ffef..9270c4b69d 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -35,6 +35,9 @@ namespace osu.Game.Online.Rooms [JsonProperty("channel_id")] public readonly Bindable ChannelId = new Bindable(); + [JsonProperty("password")] + public readonly Bindable Password = new Bindable(); + [Cached] [JsonIgnore] public readonly Bindable Category = new Bindable(); @@ -143,6 +146,7 @@ namespace osu.Game.Online.Rooms ChannelId.Value = other.ChannelId.Value; Status.Value = other.Status.Value; + Password.Value = other.Password.Value; Availability.Value = other.Availability.Value; Type.Value = other.Type.Value; MaxParticipants.Value = other.MaxParticipants.Value; From 24f330e5c1ae697f8e02eefb314fcefc559f9d4b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jul 2021 16:12:28 +0900 Subject: [PATCH 0313/2442] Avoid `MatchSettingsOverlay` base class potentially accessing an uninitialised field --- .../OnlinePlay/Match/Components/MatchSettingsOverlay.cs | 4 ++++ .../Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs | 7 ++----- .../OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs | 7 ++----- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs index 5699da740c..61bb39d0c5 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs @@ -25,8 +25,12 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components private void load() { Masking = true; + + Add(Settings = CreateSettings()); } + protected abstract OnlinePlayComposite CreateSettings(); + protected override void PopIn() { Settings.MoveToY(0, TRANSITION_DURATION, Easing.OutQuint); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 2e180f31fd..81ee580a24 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -27,16 +27,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { public class MultiplayerMatchSettingsOverlay : MatchSettingsOverlay { - [BackgroundDependencyLoader] - private void load() - { - Child = Settings = new MatchSettings + protected override OnlinePlayComposite CreateSettings() + => new MatchSettings { RelativeSizeAxes = Axes.Both, RelativePositionAxes = Axes.Y, SettingsApplied = Hide }; - } protected class MatchSettings : OnlinePlayComposite { diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs index 5eb2b545cb..88ac5ef6e5 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs @@ -26,16 +26,13 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { public Action EditPlaylist; - [BackgroundDependencyLoader] - private void load() - { - Child = Settings = new MatchSettings + protected override OnlinePlayComposite CreateSettings() + => new MatchSettings { RelativeSizeAxes = Axes.Both, RelativePositionAxes = Axes.Y, EditPlaylist = () => EditPlaylist?.Invoke() }; - } protected class MatchSettings : OnlinePlayComposite { From 4fd6f2101c2d890372fc0c096d09df5c39284b6c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jul 2021 16:28:48 +0900 Subject: [PATCH 0314/2442] Add password textbox input --- osu.Game/Online/Rooms/Room.cs | 1 - .../Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index 9270c4b69d..bccb6f854c 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -146,7 +146,6 @@ namespace osu.Game.Online.Rooms ChannelId.Value = other.ChannelId.Value; Status.Value = other.Status.Value; - Password.Value = other.Password.Value; Availability.Value = other.Availability.Value; Type.Value = other.Type.Value; MaxParticipants.Value = other.MaxParticipants.Value; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 81ee580a24..bc2f263a68 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -44,6 +44,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match public OsuTextBox NameField, MaxParticipantsField; public RoomAvailabilityPicker AvailabilityPicker; public GameTypePicker TypePicker; + public OsuTextBox PasswordTextBox; public TriangleButton ApplyButton; public OsuSpriteText ErrorText; @@ -190,12 +191,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match }, new Section("Password (optional)") { - Alpha = disabled_alpha, - Child = new SettingsPasswordTextBox + Child = PasswordTextBox = new SettingsPasswordTextBox { RelativeSizeAxes = Axes.X, TabbableContentContainer = this, - ReadOnly = true, }, }, } @@ -317,6 +316,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match currentRoom.Value.Name.Value = NameField.Text; currentRoom.Value.Availability.Value = AvailabilityPicker.Current.Value; currentRoom.Value.Type.Value = TypePicker.Current.Value; + currentRoom.Value.Password.Value = PasswordTextBox.Current.Value; if (int.TryParse(MaxParticipantsField.Text, out int max)) currentRoom.Value.MaxParticipants.Value = max; From 6a74fde0824fe127c614a06ef5b8afcc7c105f71 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 7 Jul 2021 18:53:13 +0900 Subject: [PATCH 0315/2442] Add `has_password` flag and region post only parameters --- osu.Game/Online/Rooms/Room.cs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index bccb6f854c..d1701d544e 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -35,9 +35,6 @@ namespace osu.Game.Online.Rooms [JsonProperty("channel_id")] public readonly Bindable ChannelId = new Bindable(); - [JsonProperty("password")] - public readonly Bindable Password = new Bindable(); - [Cached] [JsonIgnore] public readonly Bindable Category = new Bindable(); @@ -51,10 +48,6 @@ namespace osu.Game.Online.Rooms set => Category.Value = value; } - [Cached] - [JsonIgnore] - public readonly Bindable Duration = new Bindable(); - [Cached] [JsonIgnore] public readonly Bindable MaxAttempts = new Bindable(); @@ -79,6 +72,9 @@ namespace osu.Game.Online.Rooms [JsonProperty("current_user_score")] public readonly Bindable UserScore = new Bindable(); + [JsonProperty("has_password")] + public readonly BindableBool HasPassword = new BindableBool(); + [Cached] [JsonProperty("recent_participants")] public readonly BindableList RecentParticipants = new BindableList(); @@ -87,6 +83,15 @@ namespace osu.Game.Online.Rooms [JsonProperty("participant_count")] public readonly Bindable ParticipantCount = new Bindable(); + #region Properties only used for room creation request + + [JsonProperty("password")] + public readonly Bindable Password = new Bindable(); + + [Cached] + [JsonIgnore] + public readonly Bindable Duration = new Bindable(); + [JsonProperty("duration")] private int? duration { @@ -100,6 +105,8 @@ namespace osu.Game.Online.Rooms } } + #endregion + // Only supports retrieval for now [Cached] [JsonProperty("ends_at")] From 2ca11d458a3ea885ebdae8a033fd0c9eda984317 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jul 2021 00:53:10 +0900 Subject: [PATCH 0316/2442] Add password to room settings and multiplayer lounge interface --- .../Multiplayer/IMultiplayerLoungeServer.cs | 10 +++++++++ .../Multiplayer/InvalidPasswordException.cs | 22 +++++++++++++++++++ .../Online/Multiplayer/MultiplayerClient.cs | 2 ++ .../Multiplayer/MultiplayerRoomSettings.cs | 5 +++++ 4 files changed, 39 insertions(+) create mode 100644 osu.Game/Online/Multiplayer/InvalidPasswordException.cs diff --git a/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs b/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs index 4640640c5f..a04ec53578 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs @@ -15,6 +15,16 @@ namespace osu.Game.Online.Multiplayer /// /// The databased room ID. /// If the user is already in the requested (or another) room. + /// If the room required a password. Task JoinRoom(long roomId); + + /// + /// Request to join a multiplayer room with a provided password. + /// + /// The databased room ID. + /// The password for the join request. + /// If the user is already in the requested (or another) room. + /// If the room provided password was incorrect. + Task JoinRoom(long roomId, string password); } } diff --git a/osu.Game/Online/Multiplayer/InvalidPasswordException.cs b/osu.Game/Online/Multiplayer/InvalidPasswordException.cs new file mode 100644 index 0000000000..0441aea287 --- /dev/null +++ b/osu.Game/Online/Multiplayer/InvalidPasswordException.cs @@ -0,0 +1,22 @@ +// 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.Runtime.Serialization; +using Microsoft.AspNetCore.SignalR; + +namespace osu.Game.Online.Multiplayer +{ + [Serializable] + public class InvalidPasswordException : HubException + { + public InvalidPasswordException() + { + } + + protected InvalidPasswordException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 2e65f7cf1c..a9f4dc9e2f 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -212,6 +212,8 @@ namespace osu.Game.Online.Multiplayer return ChangeSettings(new MultiplayerRoomSettings { Name = name.GetOr(Room.Settings.Name), + // TODO: add changing support + Password = Room.Settings.Password, BeatmapID = item.GetOr(existingPlaylistItem).BeatmapID, BeatmapChecksum = item.GetOr(existingPlaylistItem).Beatmap.Value.MD5Hash, RulesetID = item.GetOr(existingPlaylistItem).RulesetID, diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs index ee72df4c10..4e94c5982f 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs @@ -36,12 +36,16 @@ namespace osu.Game.Online.Multiplayer [Key(6)] public long PlaylistItemId { get; set; } + [Key(7)] + public string Password { get; set; } = string.Empty; + public bool Equals(MultiplayerRoomSettings other) => BeatmapID == other.BeatmapID && BeatmapChecksum == other.BeatmapChecksum && RequiredMods.SequenceEqual(other.RequiredMods) && AllowedMods.SequenceEqual(other.AllowedMods) && RulesetID == other.RulesetID + && Password.Equals(other.Password, StringComparison.Ordinal) && Name.Equals(other.Name, StringComparison.Ordinal) && PlaylistItemId == other.PlaylistItemId; @@ -49,6 +53,7 @@ namespace osu.Game.Online.Multiplayer + $" Beatmap:{BeatmapID} ({BeatmapChecksum})" + $" RequiredMods:{string.Join(',', RequiredMods)}" + $" AllowedMods:{string.Join(',', AllowedMods)}" + + $" Password:{(string.IsNullOrEmpty(Password) ? "no" : "yes")}" + $" Ruleset:{RulesetID}" + $" Item:{PlaylistItemId}"; } From 5148069efe501424d788993f7dc719470e551b7a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jul 2021 16:01:45 +0900 Subject: [PATCH 0317/2442] Update signatures in line with no-overload methods (unsupported by signalr) --- osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs | 2 +- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 3 ++- osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs | 4 ++-- osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs | 5 +++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs b/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs index a04ec53578..0a618c8f5c 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs @@ -25,6 +25,6 @@ namespace osu.Game.Online.Multiplayer /// The password for the join request. /// If the user is already in the requested (or another) room. /// If the room provided password was incorrect. - Task JoinRoom(long roomId, string password); + Task JoinRoomWithPassword(long roomId, string password); } } diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index a9f4dc9e2f..543b3cd752 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -152,8 +152,9 @@ namespace osu.Game.Online.Multiplayer /// Joins the with a given ID. /// /// The room ID. + /// An optional password to use when joining the room. /// The joined . - protected abstract Task JoinRoom(long roomId); + protected abstract Task JoinRoom(long roomId, string? password = null); public Task LeaveRoom() { diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs index cf1e18e059..726e26ebe1 100644 --- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs @@ -62,12 +62,12 @@ namespace osu.Game.Online.Multiplayer } } - protected override Task JoinRoom(long roomId) + protected override Task JoinRoom(long roomId, string? password = null) { if (!IsConnected.Value) return Task.FromCanceled(new CancellationToken(true)); - return connection.InvokeAsync(nameof(IMultiplayerServer.JoinRoom), roomId); + return connection.InvokeAsync(nameof(IMultiplayerServer.JoinRoomWithPassword), roomId, password ?? string.Empty); } protected override Task LeaveRoomInternal() diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index b0c8d6d19b..adc632a2b1 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -115,7 +115,7 @@ namespace osu.Game.Tests.Visual.Multiplayer ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged(userId, newBeatmapAvailability); } - protected override Task JoinRoom(long roomId) + protected override Task JoinRoom(long roomId, string? password = null) { var apiRoom = roomManager.Rooms.Single(r => r.RoomID.Value == roomId); @@ -134,7 +134,8 @@ namespace osu.Game.Tests.Visual.Multiplayer BeatmapChecksum = apiRoom.Playlist.Last().Beatmap.Value.MD5Hash, RequiredMods = apiRoom.Playlist.Last().RequiredMods.Select(m => new APIMod(m)).ToArray(), AllowedMods = apiRoom.Playlist.Last().AllowedMods.Select(m => new APIMod(m)).ToArray(), - PlaylistItemId = apiRoom.Playlist.Last().ID + PlaylistItemId = apiRoom.Playlist.Last().ID, + Password = password ?? string.Empty, }, Users = { localUser }, Host = localUser From 84b0a3290ce2ff6767308797f46e76770d020204 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Jul 2021 16:01:52 +0900 Subject: [PATCH 0318/2442] Add multiplayer lounge test coverage --- .../TestSceneMultiplayerLoungeSubScreen.cs | 44 +++++++++++++++++++ .../Visual/OnlinePlay/BasicTestRoomManager.cs | 5 ++- 2 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs new file mode 100644 index 0000000000..0d90b75be7 --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs @@ -0,0 +1,44 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Screens; +using osu.Framework.Testing; +using osu.Game.Screens.OnlinePlay.Lounge; +using osu.Game.Screens.OnlinePlay.Lounge.Components; +using osu.Game.Screens.OnlinePlay.Multiplayer; +using osu.Game.Tests.Visual.OnlinePlay; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneMultiplayerLoungeSubScreen : OnlinePlayTestScene + { + protected new BasicTestRoomManager RoomManager => (BasicTestRoomManager)base.RoomManager; + + private LoungeSubScreen loungeScreen; + + public override void SetUpSteps() + { + base.SetUpSteps(); + + AddStep("push screen", () => LoadScreen(loungeScreen = new MultiplayerLoungeSubScreen())); + + AddUntilStep("wait for present", () => loungeScreen.IsCurrentScreen()); + } + + [Test] + public void TestJoinRoomWithoutPassword() + { + AddStep("add room", () => RoomManager.AddRooms(1, withPassword: false)); + } + + [Test] + public void TestJoinRoomWithPassword() + { + AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true)); + } + + private RoomsContainer roomsContainer => loungeScreen.ChildrenOfType().First(); + } +} diff --git a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs index 813e617ac5..f2ca3d6357 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay { } - public void AddRooms(int count, RulesetInfo ruleset = null) + public void AddRooms(int count, RulesetInfo ruleset = null, bool withPassword = false) { for (int i = 0; i < count; i++) { @@ -53,7 +53,8 @@ namespace osu.Game.Tests.Visual.OnlinePlay Name = { Value = $"Room {i}" }, Host = { Value = new User { Username = "Host" } }, EndDate = { Value = DateTimeOffset.Now + TimeSpan.FromSeconds(10) }, - Category = { Value = i % 2 == 0 ? RoomCategory.Spotlight : RoomCategory.Normal } + Category = { Value = i % 2 == 0 ? RoomCategory.Spotlight : RoomCategory.Normal }, + Password = { Value = withPassword ? "password" : string.Empty } }; if (ruleset != null) From 08c40938db22e20a6ece17b1e26776a8664629e9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 10 Jul 2021 13:50:52 +0900 Subject: [PATCH 0319/2442] Add support for updating a room's password --- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 8 ++++---- .../Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 543b3cd752..052489ca97 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -127,7 +127,7 @@ namespace osu.Game.Online.Multiplayer Debug.Assert(room.RoomID.Value != null); // Join the server-side room. - var joinedRoom = await JoinRoom(room.RoomID.Value.Value).ConfigureAwait(false); + var joinedRoom = await JoinRoom(room.RoomID.Value.Value, room.Password.Value).ConfigureAwait(false); Debug.Assert(joinedRoom != null); // Populate users. @@ -190,8 +190,9 @@ namespace osu.Game.Online.Multiplayer /// A room must be joined for this to have any effect. /// /// The new room name, if any. + /// The new password, if any. /// The new room playlist item, if any. - public Task ChangeSettings(Optional name = default, Optional item = default) + public Task ChangeSettings(Optional name = default, Optional password = default, Optional item = default) { if (Room == null) throw new InvalidOperationException("Must be joined to a match to change settings."); @@ -213,8 +214,7 @@ namespace osu.Game.Online.Multiplayer return ChangeSettings(new MultiplayerRoomSettings { Name = name.GetOr(Room.Settings.Name), - // TODO: add changing support - Password = Room.Settings.Password, + Password = password.GetOr(Room.Settings.Password), BeatmapID = item.GetOr(existingPlaylistItem).BeatmapID, BeatmapChecksum = item.GetOr(existingPlaylistItem).Beatmap.Value.MD5Hash, RulesetID = item.GetOr(existingPlaylistItem).RulesetID, diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index bc2f263a68..fceb124e0a 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -303,7 +303,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match // Otherwise, update the room directly in preparation for it to be submitted to the API on match creation. if (client.Room != null) { - client.ChangeSettings(name: NameField.Text).ContinueWith(t => Schedule(() => + client.ChangeSettings(name: NameField.Text, password: PasswordTextBox.Text).ContinueWith(t => Schedule(() => { if (t.IsCompletedSuccessfully) onSuccess(currentRoom.Value); From f35d55c32f3805fe87e54b5c094c25be4975d154 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 10 Jul 2021 14:14:22 +0900 Subject: [PATCH 0320/2442] Fix `HasPassword` not being in sync with `Password` value for client-side rooms --- osu.Game/Online/Rooms/Room.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index d1701d544e..416dc7e5c0 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -126,6 +126,11 @@ namespace osu.Game.Online.Rooms [JsonIgnore] public readonly Bindable Position = new Bindable(-1); + public Room() + { + Password.BindValueChanged(p => HasPassword.Value = !string.IsNullOrEmpty(p.NewValue)); + } + /// /// Create a copy of this room without online information. /// Should be used to create a local copy of a room for submitting in the future. From 4ac812de86da47906a1be2c6d267eefed697ef10 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Jul 2021 16:15:26 +0900 Subject: [PATCH 0321/2442] Add rank range pill --- .../Multiplayer/TestSceneRankRangePill.cs | 67 ++++++++++++++++ .../OnlinePlay/Components/RankRangePill.cs | 77 +++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs create mode 100644 osu.Game/Screens/OnlinePlay/Components/RankRangePill.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs new file mode 100644 index 0000000000..d6bbaabfa6 --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs @@ -0,0 +1,67 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Game.Screens.OnlinePlay.Components; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneRankRangePill : MultiplayerTestScene + { + [SetUp] + public new void Setup() => Schedule(() => + { + Child = new RankRangePill + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }; + }); + + [Test] + public void TestSingleUser() + { + AddStep("add user", () => + { + Client.AddUser(new User + { + Id = 2, + Statistics = { GlobalRank = 1234 } + }); + + // Remove the local user so only the one above is displayed. + Client.RemoveUser(API.LocalUser.Value); + }); + } + + [Test] + public void TestMultipleUsers() + { + AddStep("add users", () => + { + Client.AddUser(new User + { + Id = 2, + Statistics = { GlobalRank = 1234 } + }); + + Client.AddUser(new User + { + Id = 3, + Statistics = { GlobalRank = 3333 } + }); + + Client.AddUser(new User + { + Id = 4, + Statistics = { GlobalRank = 4321 } + }); + + // Remove the local user so only the ones above are displayed. + Client.RemoveUser(API.LocalUser.Value); + }); + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Components/RankRangePill.cs b/osu.Game/Screens/OnlinePlay/Components/RankRangePill.cs new file mode 100644 index 0000000000..d7bdf13f6a --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Components/RankRangePill.cs @@ -0,0 +1,77 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Screens.OnlinePlay.Multiplayer; +using osuTK; + +namespace osu.Game.Screens.OnlinePlay.Components +{ + public class RankRangePill : MultiplayerRoomComposite + { + private OsuTextFlowContainer rankFlow; + + public RankRangePill() + { + AutoSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = new PillContainer + { + Child = new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(4), + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(8), + Icon = FontAwesome.Solid.User + }, + rankFlow = new OsuTextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12)) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + } + } + } + }; + } + + protected override void OnRoomUpdated() + { + base.OnRoomUpdated(); + + rankFlow.Clear(); + + if (Room == null || Room.Users.All(u => u.User == null)) + { + rankFlow.AddText("-"); + return; + } + + rankFlow.AddText("#"); + rankFlow.AddText(Room.Users.Select(u => u.User?.Statistics.GlobalRank ?? 0).DefaultIfEmpty(0).Min().ToString(), s => s.Font = s.Font.With(weight: FontWeight.Bold)); + + rankFlow.AddText(" - "); + + rankFlow.AddText("#"); + rankFlow.AddText(Room.Users.Select(u => u.User?.Statistics.GlobalRank ?? 0).DefaultIfEmpty(0).Max().ToString(), s => s.Font = s.Font.With(weight: FontWeight.Bold)); + } + } +} From aba09b20a5ffea00f513566ca35c7ec07fb6ee94 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Jul 2021 16:28:22 +0900 Subject: [PATCH 0322/2442] Add host under room title --- .../Multiplayer/TestSceneDrawableRoom.cs | 21 +++++--- .../Lounge/Components/DrawableRoom.cs | 52 +++++++++++++++++-- 2 files changed, 62 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index 7fef5aba4d..3d65b5afda 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -8,6 +8,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online.Rooms; using osu.Game.Online.Rooms.RoomStatuses; using osu.Game.Screens.OnlinePlay.Lounge.Components; +using osu.Game.Users; using osuTK; namespace osu.Game.Tests.Visual.Multiplayer @@ -27,27 +28,31 @@ namespace osu.Game.Tests.Visual.Multiplayer { createDrawableRoom(new Room { - Name = { Value = "Room name: Open - ending in 1 day" }, + Name = { Value = "Room 1" }, Status = { Value = new RoomStatusOpen() }, - EndDate = { Value = DateTimeOffset.Now.AddDays(1) } + EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, + Host = { Value = new User { Username = "peppy", Id = 2 } } }), createDrawableRoom(new Room { - Name = { Value = "Room name: Playing - ending in 1 day" }, + Name = { Value = "Room 2" }, Status = { Value = new RoomStatusPlaying() }, - EndDate = { Value = DateTimeOffset.Now.AddDays(1) } + EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, + Host = { Value = new User { Username = "peppy", Id = 2 } } }), createDrawableRoom(new Room { - Name = { Value = "Room name: Ended" }, + Name = { Value = "Room 3" }, Status = { Value = new RoomStatusEnded() }, - EndDate = { Value = DateTimeOffset.Now } + EndDate = { Value = DateTimeOffset.Now }, + Host = { Value = new User { Username = "peppy", Id = 2 } } }), createDrawableRoom(new Room { - Name = { Value = "Room name: Open" }, + Name = { Value = "Room 4" }, Status = { Value = new RoomStatusOpen() }, - Category = { Value = RoomCategory.Realtime } + Category = { Value = RoomCategory.Realtime }, + Host = { Value = new User { Username = "peppy", Id = 2 } } }), } }; diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index b795b74e04..4c7847cef0 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -187,11 +187,17 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components }, } }, - new RoomName + new FillFlowContainer { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Font = OsuFont.GetFont(size: 28) + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new RoomNameText(), + new RoomHostText() + } }, new FillFlowContainer { @@ -262,11 +268,16 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components protected override bool ShouldBeConsideredForInput(Drawable child) => state == SelectionState.Selected; - private class RoomName : OsuSpriteText + private class RoomNameText : OsuSpriteText { [Resolved(typeof(Room), nameof(Online.Rooms.Room.Name))] private Bindable name { get; set; } + public RoomNameText() + { + Font = OsuFont.GetFont(size: 24); + } + [BackgroundDependencyLoader] private void load() { @@ -274,6 +285,41 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } + private class RoomHostText : OnlinePlayComposite + { + private LinkFlowContainer hostText; + + public RoomHostText() + { + AutoSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = hostText = new LinkFlowContainer(s => s.Font = OsuFont.GetFont(size: 14)) + { + AutoSizeAxes = Axes.Both + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Host.BindValueChanged(host => + { + hostText.Clear(); + + if (host.NewValue != null) + { + hostText.AddText("hosted by "); + hostText.AddUserLink(host.NewValue); + } + }, true); + } + } + public MenuItem[] ContextMenuItems => new MenuItem[] { new OsuMenuItem("Create copy", MenuItemType.Standard, () => From c1fba3da6b49ed30abf2892fd63ab4eb2ff28fa7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Jul 2021 16:30:34 +0900 Subject: [PATCH 0323/2442] Add solid background --- .../Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 4c7847cef0..a1f90f2d89 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -125,6 +125,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components }, Children = new Drawable[] { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex(@"#27302E"), + }, new Container { Anchor = Anchor.CentreRight, From 242982730fb5fca4c029bb1ca3e23cf59e222121 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Jul 2021 16:52:57 +0900 Subject: [PATCH 0324/2442] Fix incorrect DifficultyBindable binding implementation --- osu.Game/Rulesets/Mods/DifficultyBindable.cs | 29 ++++++++++++++------ 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/osu.Game/Rulesets/Mods/DifficultyBindable.cs b/osu.Game/Rulesets/Mods/DifficultyBindable.cs index 1f76a3e366..cf86e9ad19 100644 --- a/osu.Game/Rulesets/Mods/DifficultyBindable.cs +++ b/osu.Game/Rulesets/Mods/DifficultyBindable.cs @@ -71,6 +71,12 @@ namespace osu.Game.Rulesets.Mods } public DifficultyBindable() + : this(null) + { + } + + public DifficultyBindable(float? defaultValue = null) + : base(defaultValue) { ExtendedLimits.BindValueChanged(_ => updateMaxValue()); } @@ -93,15 +99,22 @@ namespace osu.Game.Rulesets.Mods CurrentNumber.MaxValue = ExtendedLimits.Value && extendedMaxValue != null ? extendedMaxValue.Value : maxValue; } - public new DifficultyBindable GetBoundCopy() => new DifficultyBindable + public override void BindTo(Bindable them) { - BindTarget = this, - CurrentNumber = { BindTarget = CurrentNumber }, - ExtendedLimits = { BindTarget = ExtendedLimits }, - ReadCurrentFromDifficulty = ReadCurrentFromDifficulty, + if (!(them is DifficultyBindable otherDifficultyBindable)) + throw new InvalidOperationException($"Cannot bind to a non-{nameof(DifficultyBindable)}."); + + base.BindTo(them); + + CurrentNumber.BindTarget = otherDifficultyBindable.CurrentNumber; + ExtendedLimits.BindTarget = otherDifficultyBindable.ExtendedLimits; + ReadCurrentFromDifficulty = otherDifficultyBindable.ReadCurrentFromDifficulty; + // the following is only safe as long as these values are effectively constants. - MaxValue = maxValue, - ExtendedMaxValue = extendedMaxValue - }; + MaxValue = otherDifficultyBindable.maxValue; + ExtendedMaxValue = otherDifficultyBindable.extendedMaxValue; + } + + public new DifficultyBindable GetBoundCopy() => new DifficultyBindable { BindTarget = this }; } } From 4b393209ec2715fb57ce3750e8fab11e572cb8d1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Jul 2021 17:33:29 +0900 Subject: [PATCH 0325/2442] Implement UnbindFrom() --- osu.Game/Rulesets/Mods/DifficultyBindable.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/Rulesets/Mods/DifficultyBindable.cs b/osu.Game/Rulesets/Mods/DifficultyBindable.cs index cf86e9ad19..ff859de30b 100644 --- a/osu.Game/Rulesets/Mods/DifficultyBindable.cs +++ b/osu.Game/Rulesets/Mods/DifficultyBindable.cs @@ -115,6 +115,17 @@ namespace osu.Game.Rulesets.Mods ExtendedMaxValue = otherDifficultyBindable.extendedMaxValue; } + public override void UnbindFrom(IUnbindable them) + { + if (!(them is DifficultyBindable otherDifficultyBindable)) + throw new InvalidOperationException($"Cannot unbind from a non-{nameof(DifficultyBindable)}."); + + base.UnbindFrom(them); + + CurrentNumber.UnbindFrom(otherDifficultyBindable.CurrentNumber); + ExtendedLimits.UnbindFrom(otherDifficultyBindable.ExtendedLimits); + } + public new DifficultyBindable GetBoundCopy() => new DifficultyBindable { BindTarget = this }; } } From 78c74e97d1d2e33735511caf475e0a3d9b34efc1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jul 2021 18:08:19 +0900 Subject: [PATCH 0326/2442] Change to alternative formatting --- .../Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs index 8cff790e80..449401c0bf 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -31,7 +31,11 @@ namespace osu.Game.Tests.Visual.SongSelect private readonly TestBeatmapDifficultyCache testDifficultyCache = new TestBeatmapDifficultyCache(); [Test] - public void TestLocal([Values("Beatmap", "Some long title and stuff")] string title, [Values("Trial", "Some1's very hardest difficulty")] string version) + public void TestLocal( + [Values("Beatmap", "Some long title and stuff")] + string title, + [Values("Trial", "Some1's very hardest difficulty")] + string version) { showMetadataForBeatmap(() => CreateWorkingBeatmap(new Beatmap { From 3c49b46c5fdfedd20691c307a3797529b3c18ed6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 10 Jul 2021 14:14:58 +0900 Subject: [PATCH 0327/2442] Add lock overlay for rooms which are password protected --- .../TestSceneLoungeRoomsContainer.cs | 6 +++ .../Lounge/Components/DrawableRoom.cs | 43 ++++++++++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs index 75cc687ee8..798748b4df 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs @@ -123,6 +123,12 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("3 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 3); } + [Test] + public void TestPasswordProtectedRooms() + { + AddStep("add rooms", () => RoomManager.AddRooms(3, withPassword: true)); + } + private bool checkRoomSelected(Room room) => SelectedRoom.Value == room; private void joinRequested(Room room) => room.Status.Value = new JoinedRoomStatus(); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 35782c6104..6fec872a11 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; @@ -46,6 +47,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components [Resolved] private BeatmapManager beatmaps { get; set; } + private Container content; + public readonly Room Room; private SelectionState state; @@ -124,7 +127,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding(SELECTION_BORDER_WIDTH), - Child = new Container + Child = content = new Container { RelativeSizeAxes = Axes.Both, Masking = true, @@ -204,6 +207,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components }, }, }; + + if (Room.HasPassword.Value) + { + content.Add(new PasswordProtectedIcon()); + } } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) @@ -245,5 +253,38 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components parentScreen?.OpenNewRoom(Room.CreateCopy()); }) }; + + private class PasswordProtectedIcon : CompositeDrawable + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Anchor = Anchor.TopRight; + Origin = Anchor.TopRight; + + Size = new Vector2(32); + + InternalChildren = new Drawable[] + { + new Box + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopCentre, + Colour = colours.Gray5, + Rotation = 45, + RelativeSizeAxes = Axes.Both, + Width = 2, + }, + new SpriteIcon + { + Icon = FontAwesome.Solid.Lock, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Margin = new MarginPadding(6), + Size = new Vector2(14), + } + }; + } + } } } From 9f9d7f9125c61fde650d6dff79c031da148733d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 10 Jul 2021 16:08:12 +0900 Subject: [PATCH 0328/2442] Add remaining pieces of password flow (for osu-web join request) --- .../TestScenePlaylistsMatchSettingsOverlay.cs | 2 +- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 5 +++-- osu.Game/Online/Rooms/JoinRoomRequest.cs | 2 +- osu.Game/Screens/OnlinePlay/Components/RoomManager.cs | 5 ++++- osu.Game/Screens/OnlinePlay/IRoomManager.cs | 7 +++++-- osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 4 ++-- .../OnlinePlay/Multiplayer/MultiplayerRoomManager.cs | 10 +++++----- .../Tests/Visual/OnlinePlay/BasicTestRoomManager.cs | 2 +- 8 files changed, 22 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs index a320cb240f..cdc655500d 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs @@ -152,7 +152,7 @@ namespace osu.Game.Tests.Visual.Playlists onSuccess?.Invoke(room); } - public void JoinRoom(Room room, Action onSuccess = null, Action onError = null) => throw new NotImplementedException(); + public void JoinRoom(Room room, string password, Action onSuccess = null, Action onError = null) => throw new NotImplementedException(); public void PartRoom() => throw new NotImplementedException(); } diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 052489ca97..42d436ef11 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -115,7 +115,8 @@ namespace osu.Game.Online.Multiplayer /// Joins the for a given API . /// /// The API . - public async Task JoinRoom(Room room) + /// An optional password to use for the join operation. + public async Task JoinRoom(Room room, string? password = null) { var cancellationSource = joinCancellationSource = new CancellationTokenSource(); @@ -127,7 +128,7 @@ namespace osu.Game.Online.Multiplayer Debug.Assert(room.RoomID.Value != null); // Join the server-side room. - var joinedRoom = await JoinRoom(room.RoomID.Value.Value, room.Password.Value).ConfigureAwait(false); + var joinedRoom = await JoinRoom(room.RoomID.Value.Value, password ?? room.Password.Value).ConfigureAwait(false); Debug.Assert(joinedRoom != null); // Populate users. diff --git a/osu.Game/Online/Rooms/JoinRoomRequest.cs b/osu.Game/Online/Rooms/JoinRoomRequest.cs index faa20a3e6c..a82dc5a859 100644 --- a/osu.Game/Online/Rooms/JoinRoomRequest.cs +++ b/osu.Game/Online/Rooms/JoinRoomRequest.cs @@ -23,6 +23,6 @@ namespace osu.Game.Online.Rooms return req; } - protected override string Target => $"rooms/{room.RoomID.Value}/users/{User.Id}"; + protected override string Target => $"rooms/{room.RoomID.Value}/users/{User.Id}?password={room.Password.Value}"; } } diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs index 227a772b2d..da02f9624f 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs @@ -84,8 +84,11 @@ namespace osu.Game.Screens.OnlinePlay.Components private JoinRoomRequest currentJoinRoomRequest; - public virtual void JoinRoom(Room room, Action onSuccess = null, Action onError = null) + public virtual void JoinRoom(Room room, string password = null, Action onSuccess = null, Action onError = null) { + // todo: send into JoinRoomRequest directly? + room.Password.Value = password; + currentJoinRoomRequest?.Cancel(); currentJoinRoomRequest = new JoinRoomRequest(room); diff --git a/osu.Game/Screens/OnlinePlay/IRoomManager.cs b/osu.Game/Screens/OnlinePlay/IRoomManager.cs index 8ff02536f3..34c1393ff1 100644 --- a/osu.Game/Screens/OnlinePlay/IRoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/IRoomManager.cs @@ -6,6 +6,8 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Online.Rooms; +#nullable enable + namespace osu.Game.Screens.OnlinePlay { [Cached(typeof(IRoomManager))] @@ -32,15 +34,16 @@ namespace osu.Game.Screens.OnlinePlay /// The to create. /// An action to be invoked if the creation succeeds. /// An action to be invoked if an error occurred. - void CreateRoom(Room room, Action onSuccess = null, Action onError = null); + void CreateRoom(Room room, Action? onSuccess = null, Action? onError = null); /// /// Joins a . /// /// The to join. must be populated. + /// An optional password to use for the join operation. /// /// - void JoinRoom(Room room, Action onSuccess = null, Action onError = null); + void JoinRoom(Room room, string? password = null, Action? onSuccess = null, Action? onError = null); /// /// Parts the currently-joined . diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index f24577a8a5..a8b80655f9 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -167,14 +167,14 @@ namespace osu.Game.Screens.OnlinePlay.Lounge filter.HoldFocus = false; } - private void joinRequested(Room room) + public void Join(Room room, string password) { if (joiningRoomOperation != null) return; joiningRoomOperation = ongoingOperationTracker?.BeginOperation(); - RoomManager?.JoinRoom(room, r => + RoomManager?.JoinRoom(room, password, r => { Open(room); joiningRoomOperation?.Dispose(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs index 8526196902..cbba4babe5 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs @@ -38,9 +38,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } public override void CreateRoom(Room room, Action onSuccess = null, Action onError = null) - => base.CreateRoom(room, r => joinMultiplayerRoom(r, onSuccess, onError), onError); + => base.CreateRoom(room, r => joinMultiplayerRoom(r, r.Password.Value, onSuccess, onError), onError); - public override void JoinRoom(Room room, Action onSuccess = null, Action onError = null) + public override void JoinRoom(Room room, string password = null, Action onSuccess = null, Action onError = null) { if (!multiplayerClient.IsConnected.Value) { @@ -56,7 +56,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer return; } - base.JoinRoom(room, r => joinMultiplayerRoom(r, onSuccess, onError), onError); + base.JoinRoom(room, password, r => joinMultiplayerRoom(r, password, onSuccess, onError), onError); } public override void PartRoom() @@ -79,11 +79,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer }); } - private void joinMultiplayerRoom(Room room, Action onSuccess = null, Action onError = null) + private void joinMultiplayerRoom(Room room, string password, Action onSuccess = null, Action onError = null) { Debug.Assert(room.RoomID.Value != null); - multiplayerClient.JoinRoom(room).ContinueWith(t => + multiplayerClient.JoinRoom(room, password).ContinueWith(t => { if (t.IsCompletedSuccessfully) Schedule(() => onSuccess?.Invoke(room)); diff --git a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs index f2ca3d6357..5d3fa34ec5 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs @@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay onSuccess?.Invoke(room); } - public void JoinRoom(Room room, Action onSuccess = null, Action onError = null) => onSuccess?.Invoke(room); + public void JoinRoom(Room room, string password, Action onSuccess = null, Action onError = null) => onSuccess?.Invoke(room); public void PartRoom() { From e25b3518dc0988149a48ac4b8a91cddc644606c3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Jul 2021 10:13:57 +0900 Subject: [PATCH 0329/2442] Make password popover display inside `RoomsContainer` rooms --- .../Lounge/Components/DrawableRoom.cs | 100 +++++++++++++++++- .../Lounge/Components/RoomsContainer.cs | 49 +++------ .../OnlinePlay/Lounge/LoungeSubScreen.cs | 2 +- 3 files changed, 114 insertions(+), 37 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 6fec872a11..bc860772b7 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -14,12 +14,15 @@ using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Input.Bindings; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Components; using osuTK; @@ -27,7 +30,7 @@ using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { - public class DrawableRoom : OsuClickableContainer, IStateful, IFilterable, IHasContextMenu + public class DrawableRoom : OsuClickableContainer, IStateful, IFilterable, IHasContextMenu, IHasPopover, IKeyBindingHandler { public const float SELECTION_BORDER_WIDTH = 4; private const float corner_radius = 5; @@ -47,6 +50,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components [Resolved] private BeatmapManager beatmaps { get; set; } + [Resolved] + private Bindable selectedRoom { get; set; } + + [Resolved(canBeNull: true)] + private LoungeSubScreen lounge { get; set; } + private Container content; public readonly Room Room; @@ -232,6 +241,22 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Alpha = 0; } + public bool OnPressed(GlobalAction action) + { + switch (action) + { + case GlobalAction.Select: + // TODO: this needs to be able to show the popover on demand. + return true; + } + + return false; + } + + public void OnReleased(GlobalAction action) + { + } + protected override bool ShouldBeConsideredForInput(Drawable child) => state == SelectionState.Selected; private class RoomName : OsuSpriteText @@ -286,5 +311,78 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components }; } } + + protected override bool OnMouseDown(MouseDownEvent e) + { + if (selectedRoom.Value != Room) + return true; + + return base.OnMouseDown(e); + } + + protected override bool OnClick(ClickEvent e) + { + if (Room != selectedRoom.Value) + { + selectedRoom.Value = Room; + return true; + } + + if (Room.HasPassword.Value) + // we want our popover to show. this is a bit of a hack. + return false; + + lounge?.Join(Room, null); + + return base.OnClick(e); + } + + public Popover GetPopover() => new PasswordEntryPopover(Room); + + public class PasswordEntryPopover : Popover + { + [Resolved(canBeNull: true)] + private LoungeSubScreen lounge { get; set; } + + public PasswordEntryPopover(Room room) + { + OsuPasswordTextBox passwordTextbox; + + Child = new Container + { + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + Colour = Color4.OliveDrab, + RelativeSizeAxes = Axes.Both, + }, + new FillFlowContainer + { + Margin = new MarginPadding(10), + Spacing = new Vector2(5), + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + passwordTextbox = new OsuPasswordTextBox + { + Width = 200, + }, + new TriangleButton + { + Width = 80, + Text = "Join Room", + Action = () => lounge?.Join(room, passwordTextbox.Text) + } + } + }, + } + }; + } + + protected override Drawable CreateArrow() => Drawable.Empty(); + } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index 5f135a3e90..5a9721a8e3 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -10,6 +10,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Threading; @@ -24,8 +25,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { public class RoomsContainer : CompositeDrawable, IKeyBindingHandler { - public Action JoinRequested; - private readonly IBindableList rooms = new BindableList(); private readonly FillFlowContainer roomFlow; @@ -51,16 +50,21 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - InternalChild = new OsuContextMenuContainer + InternalChild = new PopoverContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Child = roomFlow = new FillFlowContainer + Child = new OsuContextMenuContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(2), + Child = roomFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(2), + } } }; } @@ -121,19 +125,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { foreach (var room in rooms) { - roomFlow.Add(new DrawableRoom(room) - { - Action = () => - { - if (room == selectedRoom.Value) - { - joinSelected(); - return; - } - - selectRoom(room); - } - }); + roomFlow.Add(new DrawableRoom(room)); } Filter(filter?.Value); @@ -150,7 +142,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components roomFlow.Remove(toRemove); - selectRoom(null); + selectedRoom.Value = null; } } @@ -160,18 +152,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components roomFlow.SetLayoutPosition(room, room.Room.Position.Value); } - private void selectRoom(Room room) => selectedRoom.Value = room; - - private void joinSelected() - { - if (selectedRoom.Value == null) return; - - JoinRequested?.Invoke(selectedRoom.Value); - } - protected override bool OnClick(ClickEvent e) { - selectRoom(null); + selectedRoom.Value = null; return base.OnClick(e); } @@ -181,10 +164,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { switch (action) { - case GlobalAction.Select: - joinSelected(); - return true; - case GlobalAction.SelectNext: beginRepeatSelection(() => selectNext(1), action); return true; @@ -253,7 +232,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components // we already have a valid selection only change selection if we still have a room to switch to. if (room != null) - selectRoom(room); + selectedRoom.Value = room; } #endregion diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index a8b80655f9..11298037d0 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -70,7 +70,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge RelativeSizeAxes = Axes.Both, ScrollbarOverlapsContent = false, Padding = new MarginPadding(10), - Child = roomsContainer = new RoomsContainer { JoinRequested = joinRequested } + Child = roomsContainer = new RoomsContainer() }, loadingLayer = new LoadingLayer(true), } From a3e0168a46d5f2ff87f08ed465ac94178256b84b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jul 2021 14:35:08 +0900 Subject: [PATCH 0330/2442] Update tests --- .../TestSceneLoungeRoomsContainer.cs | 20 +------------------ .../TestScenePlaylistsLoungeSubScreen.cs | 2 +- .../Lounge/Components/DrawableRoom.cs | 2 +- 3 files changed, 3 insertions(+), 21 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs index 798748b4df..4d5bf8f225 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs @@ -4,13 +4,11 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; -using osu.Game.Graphics; using osu.Game.Online.Rooms; using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Osu; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Tests.Visual.OnlinePlay; -using osuTK.Graphics; using osuTK.Input; namespace osu.Game.Tests.Visual.Multiplayer @@ -29,7 +27,6 @@ namespace osu.Game.Tests.Visual.Multiplayer Anchor = Anchor.Centre, Origin = Anchor.Centre, Width = 0.5f, - JoinRequested = joinRequested }; }); @@ -43,11 +40,8 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("has 2 rooms", () => container.Rooms.Count == 2); AddAssert("first room removed", () => container.Rooms.All(r => r.Room.RoomID.Value != 0)); - AddStep("select first room", () => container.Rooms.First().Action?.Invoke()); + AddStep("select first room", () => container.Rooms.First().Click()); AddAssert("first room selected", () => checkRoomSelected(RoomManager.Rooms.First())); - - AddStep("join first room", () => container.Rooms.First().Action?.Invoke()); - AddAssert("first room joined", () => RoomManager.Rooms.First().Status.Value is JoinedRoomStatus); } [Test] @@ -66,9 +60,6 @@ namespace osu.Game.Tests.Visual.Multiplayer press(Key.Down); press(Key.Down); AddAssert("last room selected", () => checkRoomSelected(RoomManager.Rooms.Last())); - - press(Key.Enter); - AddAssert("last room joined", () => RoomManager.Rooms.Last().Status.Value is JoinedRoomStatus); } [Test] @@ -130,14 +121,5 @@ namespace osu.Game.Tests.Visual.Multiplayer } private bool checkRoomSelected(Room room) => SelectedRoom.Value == room; - - private void joinRequested(Room room) => room.Status.Value = new JoinedRoomStatus(); - - private class JoinedRoomStatus : RoomStatus - { - public override string Message => "Joined"; - - public override Color4 GetAppropriateColour(OsuColour colours) => colours.Yellow; - } } } diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs index b16b61c5c7..7bf161d1d0 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs @@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.Playlists AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms.First())); - AddStep("select last room", () => roomsContainer.Rooms.Last().Action?.Invoke()); + AddStep("select last room", () => roomsContainer.Rooms.Last().Click()); AddUntilStep("first room is masked", () => !checkRoomVisible(roomsContainer.Rooms.First())); AddUntilStep("last room is not masked", () => checkRoomVisible(roomsContainer.Rooms.Last())); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index bc860772b7..678a97a04c 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -50,7 +50,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components [Resolved] private BeatmapManager beatmaps { get; set; } - [Resolved] + [Resolved(canBeNull: true)] private Bindable selectedRoom { get; set; } [Resolved(canBeNull: true)] From b4ca6b6188d90c4db0436a19ec57364b6942ac41 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jul 2021 14:53:12 +0900 Subject: [PATCH 0331/2442] Update popover logic to take advantage of new explicit popup functionality --- .../Lounge/Components/DrawableRoom.cs | 78 ++++++++++--------- 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 678a97a04c..1747dc6565 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -241,12 +242,25 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Alpha = 0; } + public Popover GetPopover() => new PasswordEntryPopover(Room); + + public MenuItem[] ContextMenuItems => new MenuItem[] + { + new OsuMenuItem("Create copy", MenuItemType.Standard, () => + { + parentScreen?.OpenNewRoom(Room.CreateCopy()); + }) + }; + public bool OnPressed(GlobalAction action) { + if (selectedRoom.Value != Room) + return false; + switch (action) { case GlobalAction.Select: - // TODO: this needs to be able to show the popover on demand. + Click(); return true; } @@ -259,6 +273,33 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components protected override bool ShouldBeConsideredForInput(Drawable child) => state == SelectionState.Selected; + protected override bool OnMouseDown(MouseDownEvent e) + { + if (selectedRoom.Value != Room) + return true; + + return base.OnMouseDown(e); + } + + protected override bool OnClick(ClickEvent e) + { + if (Room != selectedRoom.Value) + { + selectedRoom.Value = Room; + return true; + } + + if (Room.HasPassword.Value) + { + this.ShowPopover(); + return true; + } + + lounge?.Join(Room, null); + + return base.OnClick(e); + } + private class RoomName : OsuSpriteText { [Resolved(typeof(Room), nameof(Online.Rooms.Room.Name))] @@ -271,14 +312,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } - public MenuItem[] ContextMenuItems => new MenuItem[] - { - new OsuMenuItem("Create copy", MenuItemType.Standard, () => - { - parentScreen?.OpenNewRoom(Room.CreateCopy()); - }) - }; - private class PasswordProtectedIcon : CompositeDrawable { [BackgroundDependencyLoader] @@ -312,33 +345,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } - protected override bool OnMouseDown(MouseDownEvent e) - { - if (selectedRoom.Value != Room) - return true; - - return base.OnMouseDown(e); - } - - protected override bool OnClick(ClickEvent e) - { - if (Room != selectedRoom.Value) - { - selectedRoom.Value = Room; - return true; - } - - if (Room.HasPassword.Value) - // we want our popover to show. this is a bit of a hack. - return false; - - lounge?.Join(Room, null); - - return base.OnClick(e); - } - - public Popover GetPopover() => new PasswordEntryPopover(Room); - public class PasswordEntryPopover : Popover { [Resolved(canBeNull: true)] From 947460c3c54d46900385f3321ecfae0d8c55a855 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jul 2021 15:49:09 +0900 Subject: [PATCH 0332/2442] Add test flow for joining passworded rooms via UI --- .../TestSceneMultiplayerLoungeSubScreen.cs | 35 ++++++++++++++++++- .../Visual/OnlinePlay/BasicTestRoomManager.cs | 8 ++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs index 0d90b75be7..3ba0f4969a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs @@ -3,12 +3,16 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Screens; using osu.Framework.Testing; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Tests.Visual.OnlinePlay; +using osuTK.Input; namespace osu.Game.Tests.Visual.Multiplayer { @@ -18,6 +22,9 @@ namespace osu.Game.Tests.Visual.Multiplayer private LoungeSubScreen loungeScreen; + private Room lastJoinedRoom; + private string lastJoinedPassword; + public override void SetUpSteps() { base.SetUpSteps(); @@ -25,20 +32,46 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("push screen", () => LoadScreen(loungeScreen = new MultiplayerLoungeSubScreen())); AddUntilStep("wait for present", () => loungeScreen.IsCurrentScreen()); + + AddStep("bind to event", () => + { + lastJoinedRoom = null; + lastJoinedPassword = null; + RoomManager.JoinRoomRequested = onRoomJoined; + }); } [Test] public void TestJoinRoomWithoutPassword() { AddStep("add room", () => RoomManager.AddRooms(1, withPassword: false)); + AddStep("select room", () => InputManager.Key(Key.Down)); + AddStep("join room", () => InputManager.Key(Key.Enter)); + + AddAssert("room join requested", () => lastJoinedRoom == RoomManager.Rooms.First()); + AddAssert("room join password correct", () => lastJoinedPassword == null); } [Test] public void TestJoinRoomWithPassword() { + DrawableRoom.PasswordEntryPopover passwordEntryPopover = null; + AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true)); + AddStep("select room", () => InputManager.Key(Key.Down)); + AddStep("attempt join room", () => InputManager.Key(Key.Enter)); + AddUntilStep("password prompt appeared", () => (passwordEntryPopover = loungeScreen.ChildrenOfType().FirstOrDefault()) != null); + AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password"); + AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().Click()); + + AddAssert("room join requested", () => lastJoinedRoom == RoomManager.Rooms.First()); + AddAssert("room join password correct", () => lastJoinedPassword == "password"); } - private RoomsContainer roomsContainer => loungeScreen.ChildrenOfType().First(); + private void onRoomJoined(Room room, string password) + { + lastJoinedRoom = room; + lastJoinedPassword = password; + } } } diff --git a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs index 5d3fa34ec5..82c7266598 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs @@ -26,6 +26,8 @@ namespace osu.Game.Tests.Visual.OnlinePlay public readonly BindableList Rooms = new BindableList(); + public Action JoinRoomRequested; + public IBindable InitialRoomsReceived { get; } = new Bindable(true); IBindableList IRoomManager.Rooms => Rooms; @@ -37,7 +39,11 @@ namespace osu.Game.Tests.Visual.OnlinePlay onSuccess?.Invoke(room); } - public void JoinRoom(Room room, string password, Action onSuccess = null, Action onError = null) => onSuccess?.Invoke(room); + public void JoinRoom(Room room, string password, Action onSuccess = null, Action onError = null) + { + JoinRoomRequested?.Invoke(room, password); + onSuccess?.Invoke(room); + } public void PartRoom() { From 413f8adb36e36115deb020160acee13b8a1e6ec7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jul 2021 15:53:28 +0900 Subject: [PATCH 0333/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 9280eaf97c..f9ec8dd099 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8a3c69e40c..8f9a57167f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 2eea646c61..c6e52b8dd5 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From bbc3a013c888596a8e4501033c9bd813112735bc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jul 2021 18:29:50 +0900 Subject: [PATCH 0334/2442] Use `BasicPopover` for now --- .../Lounge/Components/DrawableRoom.cs | 46 ++++++++----------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 1747dc6565..a95e0c2b16 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -345,50 +345,44 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } - public class PasswordEntryPopover : Popover + public class PasswordEntryPopover : BasicPopover { + private readonly Room room; + [Resolved(canBeNull: true)] private LoungeSubScreen lounge { get; set; } public PasswordEntryPopover(Room room) + { + this.room = room; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) { OsuPasswordTextBox passwordTextbox; - Child = new Container + Child = new FillFlowContainer { + Margin = new MarginPadding(10), + Spacing = new Vector2(5), AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, Children = new Drawable[] { - new Box + passwordTextbox = new OsuPasswordTextBox { - Colour = Color4.OliveDrab, - RelativeSizeAxes = Axes.Both, + Width = 200, }, - new FillFlowContainer + new TriangleButton { - Margin = new MarginPadding(10), - Spacing = new Vector2(5), - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Children = new Drawable[] - { - passwordTextbox = new OsuPasswordTextBox - { - Width = 200, - }, - new TriangleButton - { - Width = 80, - Text = "Join Room", - Action = () => lounge?.Join(room, passwordTextbox.Text) - } - } - }, + Width = 80, + Text = "Join Room", + Action = () => lounge?.Join(room, passwordTextbox.Text) + } } }; } - - protected override Drawable CreateArrow() => Drawable.Empty(); } } } From c5319c06c2e29ddc1f1903ad48b1ecd7e832224b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jul 2021 18:54:07 +0900 Subject: [PATCH 0335/2442] Add password attributes to `CopyFrom` to make testing work better --- osu.Game/Online/Rooms/Room.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index 416dc7e5c0..f79a410bd9 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -159,6 +159,8 @@ namespace osu.Game.Online.Rooms ChannelId.Value = other.ChannelId.Value; Status.Value = other.Status.Value; Availability.Value = other.Availability.Value; + Password.Value = other.Password.Value; + HasPassword.Value = other.HasPassword.Value; Type.Value = other.Type.Value; MaxParticipants.Value = other.MaxParticipants.Value; ParticipantCount.Value = other.ParticipantCount.Value; From 4dea2d97782151a1a8e383cd09cd225662b99d15 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jul 2021 18:54:17 +0900 Subject: [PATCH 0336/2442] Dismiss popovers on returning to lounge --- .../Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs | 7 +++++++ osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index 5a9721a8e3..8ab80d947c 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -7,6 +7,7 @@ using System.Collections.Specialized; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -244,5 +245,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components if (roomManager != null) roomManager.RoomsUpdated -= updateSorting; } + + public void HideAnyPopovers() + { + // must be called on a child of the PopoverContainer due to parent traversal not considering self. + roomFlow.HidePopover(); + } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 11298037d0..dd6106b868 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -46,10 +46,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge [CanBeNull] private IDisposable joiningRoomOperation { get; set; } + private RoomsContainer roomsContainer; + [BackgroundDependencyLoader] private void load() { - RoomsContainer roomsContainer; OsuScrollContainer scrollContainer; InternalChildren = new Drawable[] @@ -165,6 +166,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { base.OnSuspending(next); filter.HoldFocus = false; + + // ensure any password prompt is dismissed. + roomsContainer.HideAnyPopovers(); } public void Join(Room room, string password) From c6bd58ea4bf0cecfaa3456038ee29a928b801a46 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Jul 2021 19:20:39 +0900 Subject: [PATCH 0337/2442] Exit match sub screen when a room goes away Closes #13847. I think we can probably get some test coverage of this if required, but needs a bit of thought (basically an error needs to be thrown during the multiplayer client portion of the join procedure, after `CurrentRoom` is non-null but before the join completes). Manual testing on password branch (#13861) is possible since it currently errors due to missing method on the live/dev servers. - Create game, which will fail with `MethodNotExists`. - Note the fields on the settings screen are emptied. - Fill fields again and press create game (crash). --- .../Multiplayer/MultiplayerMatchSubScreen.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 4b8c4422ec..4f5d54e203 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -48,6 +48,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private OngoingOperationTracker ongoingOperationTracker { get; set; } + [Resolved] + private Bindable currentRoom { get; set; } + private MultiplayerMatchSettingsOverlay settingsOverlay; private readonly IBindable isConnected = new Bindable(); @@ -273,6 +276,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer if (!connected.NewValue) Schedule(this.Exit); }, true); + + currentRoom.BindValueChanged(room => + { + if (room.NewValue == null) + { + // the room has gone away. + // this could mean something happened during the join process, or an external connection issue occurred. + Schedule(this.Exit); + } + }, true); } protected override void UpdateMods() From 1deaefacb7c7236c20a5526db5ffb75a58bbc7d8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 12 Jul 2021 17:53:15 +0300 Subject: [PATCH 0338/2442] Add "basic" lime colour theme --- osu.Game/Overlays/OverlayColourProvider.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Overlays/OverlayColourProvider.cs b/osu.Game/Overlays/OverlayColourProvider.cs index abd1e43f25..4c698c2af7 100644 --- a/osu.Game/Overlays/OverlayColourProvider.cs +++ b/osu.Game/Overlays/OverlayColourProvider.cs @@ -14,6 +14,7 @@ namespace osu.Game.Overlays public static OverlayColourProvider Red { get; } = new OverlayColourProvider(OverlayColourScheme.Red); public static OverlayColourProvider Pink { get; } = new OverlayColourProvider(OverlayColourScheme.Pink); public static OverlayColourProvider Orange { get; } = new OverlayColourProvider(OverlayColourScheme.Orange); + public static OverlayColourProvider Lime { get; } = new OverlayColourProvider(OverlayColourScheme.Lime); public static OverlayColourProvider Green { get; } = new OverlayColourProvider(OverlayColourScheme.Green); public static OverlayColourProvider Purple { get; } = new OverlayColourProvider(OverlayColourScheme.Purple); public static OverlayColourProvider Blue { get; } = new OverlayColourProvider(OverlayColourScheme.Blue); @@ -68,6 +69,9 @@ namespace osu.Game.Overlays case OverlayColourScheme.Orange: return 46 / 360f; + case OverlayColourScheme.Lime: + return 90 / 360f; + case OverlayColourScheme.Green: return 115 / 360f; @@ -85,6 +89,7 @@ namespace osu.Game.Overlays Red, Pink, Orange, + Lime, Green, Purple, Blue From 9869986c59482f4c64b718e1bceb17d0ebc5d9aa Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 12 Jul 2021 17:55:29 +0300 Subject: [PATCH 0339/2442] Remove duplicated colour definitions --- osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs | 3 ++- osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs | 3 ++- osu.Game/Graphics/OsuColour.cs | 4 ---- osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs | 5 ++--- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs index 8a6cfaf688..b871f8bcc7 100644 --- a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs @@ -3,6 +3,7 @@ using osu.Framework.Bindables; using osu.Game.Graphics; +using osu.Game.Overlays; using osuTK.Graphics; namespace osu.Game.Beatmaps.ControlPoints @@ -25,7 +26,7 @@ namespace osu.Game.Beatmaps.ControlPoints MaxValue = 10 }; - public override Color4 GetRepresentingColour(OsuColour colours) => colours.Lime1; + public override Color4 GetRepresentingColour(OsuColour colours) => OverlayColourProvider.Lime.Colour1; /// /// The speed multiplier at this control point. diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs index ec20328fab..22808671b0 100644 --- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs @@ -4,6 +4,7 @@ using osu.Framework.Bindables; using osu.Game.Beatmaps.Timing; using osu.Game.Graphics; +using osu.Game.Overlays; using osuTK.Graphics; namespace osu.Game.Beatmaps.ControlPoints @@ -20,7 +21,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// private const double default_beat_length = 60000.0 / 60.0; - public override Color4 GetRepresentingColour(OsuColour colours) => colours.Orange1; + public override Color4 GetRepresentingColour(OsuColour colours) => OverlayColourProvider.Orange.Colour1; public static readonly TimingControlPoint DEFAULT = new TimingControlPoint { diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index a44c28eaa6..3533ab177c 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -198,10 +198,6 @@ namespace osu.Game.Graphics public readonly Color4 GrayE = Color4Extensions.FromHex(@"eee"); public readonly Color4 GrayF = Color4Extensions.FromHex(@"fff"); - // in latest editor design logic, need to figure out where these sit... - public readonly Color4 Lime1 = Color4Extensions.FromHex(@"b2ff66"); - public readonly Color4 Orange1 = Color4Extensions.FromHex(@"ffd966"); - // Content Background public readonly Color4 B5 = Color4Extensions.FromHex(@"222a28"); diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs index 421806eea8..91d6460ea6 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers.Markdown; using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; namespace osu.Game.Overlays.Wiki.Markdown { @@ -66,7 +65,7 @@ namespace osu.Game.Overlays.Wiki.Markdown public string Text { get; set; } [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider, OsuColour colour) + private void load(OverlayColourProvider colourProvider) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; @@ -81,7 +80,7 @@ namespace osu.Game.Overlays.Wiki.Markdown }, textFlow = parentFlowComponent.CreateTextFlow().With(t => { - t.Colour = colour.Orange1; + t.Colour = OverlayColourProvider.Orange.Colour1; t.Padding = new MarginPadding { Vertical = 10, From 143777271164b912107d949b1727324347ebf609 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 12 Jul 2021 18:11:51 +0300 Subject: [PATCH 0340/2442] Update hue of orange colour scheme --- osu.Game/Overlays/OverlayColourProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/OverlayColourProvider.cs b/osu.Game/Overlays/OverlayColourProvider.cs index abd1e43f25..1f78ffc870 100644 --- a/osu.Game/Overlays/OverlayColourProvider.cs +++ b/osu.Game/Overlays/OverlayColourProvider.cs @@ -66,7 +66,7 @@ namespace osu.Game.Overlays return 333 / 360f; case OverlayColourScheme.Orange: - return 46 / 360f; + return 45 / 360f; case OverlayColourScheme.Green: return 115 / 360f; From c96a76df673e46320147e6ee6b7fe0886df2bc13 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 12 Jul 2021 18:17:13 +0300 Subject: [PATCH 0341/2442] Update specified link --- osu.Game/Overlays/OverlayColourProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/OverlayColourProvider.cs b/osu.Game/Overlays/OverlayColourProvider.cs index 1f78ffc870..ac1cfb15c7 100644 --- a/osu.Game/Overlays/OverlayColourProvider.cs +++ b/osu.Game/Overlays/OverlayColourProvider.cs @@ -51,7 +51,7 @@ namespace osu.Game.Overlays private Color4 getColour(float saturation, float lightness) => Color4.FromHsl(new Vector4(getBaseHue(colourScheme), saturation, lightness, 1)); - // See https://github.com/ppy/osu-web/blob/4218c288292d7c810b619075471eaea8bbb8f9d8/app/helpers.php#L1463 + // See https://github.com/ppy/osu-web/blob/5a536d217a21582aad999db50a981003d3ad5659/app/helpers.php#L1620-L1628 private static float getBaseHue(OverlayColourScheme colourScheme) { switch (colourScheme) From b2b966463a87a3e441f8cf18acea6bb3abe07e23 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 12 Jul 2021 19:05:36 +0300 Subject: [PATCH 0342/2442] Update hue of green colour scheme --- osu.Game/Overlays/OverlayColourProvider.cs | 23 +++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/OverlayColourProvider.cs b/osu.Game/Overlays/OverlayColourProvider.cs index ac1cfb15c7..8ef3ccbf80 100644 --- a/osu.Game/Overlays/OverlayColourProvider.cs +++ b/osu.Game/Overlays/OverlayColourProvider.cs @@ -12,11 +12,12 @@ namespace osu.Game.Overlays private readonly OverlayColourScheme colourScheme; public static OverlayColourProvider Red { get; } = new OverlayColourProvider(OverlayColourScheme.Red); - public static OverlayColourProvider Pink { get; } = new OverlayColourProvider(OverlayColourScheme.Pink); + public static OverlayColourProvider DarkOrange { get; } = new OverlayColourProvider(OverlayColourScheme.DarkOrange); public static OverlayColourProvider Orange { get; } = new OverlayColourProvider(OverlayColourScheme.Orange); public static OverlayColourProvider Green { get; } = new OverlayColourProvider(OverlayColourScheme.Green); - public static OverlayColourProvider Purple { get; } = new OverlayColourProvider(OverlayColourScheme.Purple); public static OverlayColourProvider Blue { get; } = new OverlayColourProvider(OverlayColourScheme.Blue); + public static OverlayColourProvider Purple { get; } = new OverlayColourProvider(OverlayColourScheme.Purple); + public static OverlayColourProvider Pink { get; } = new OverlayColourProvider(OverlayColourScheme.Pink); public OverlayColourProvider(OverlayColourScheme colourScheme) { @@ -62,8 +63,8 @@ namespace osu.Game.Overlays case OverlayColourScheme.Red: return 0; - case OverlayColourScheme.Pink: - return 333 / 360f; + case OverlayColourScheme.DarkOrange: + return 20 / 360f; case OverlayColourScheme.Orange: return 45 / 360f; @@ -71,11 +72,17 @@ namespace osu.Game.Overlays case OverlayColourScheme.Green: return 115 / 360f; - case OverlayColourScheme.Purple: - return 255 / 360f; + case OverlayColourScheme.Cyan: + return 160 / 360f; case OverlayColourScheme.Blue: return 200 / 360f; + + case OverlayColourScheme.Purple: + return 255 / 360f; + + case OverlayColourScheme.Pink: + return 333 / 360f; } } } @@ -84,9 +91,11 @@ namespace osu.Game.Overlays { Red, Pink, + DarkOrange, Orange, Green, Purple, - Blue + Blue, + Cyan, } } From 62a00a82de0c184ca525ae639594f26e74d7e18a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 12 Jul 2021 19:11:14 +0300 Subject: [PATCH 0343/2442] Revert completely irrelevant changes This reverts commit b2b966463a87a3e441f8cf18acea6bb3abe07e23. --- osu.Game/Overlays/OverlayColourProvider.cs | 23 +++++++--------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/osu.Game/Overlays/OverlayColourProvider.cs b/osu.Game/Overlays/OverlayColourProvider.cs index 8ef3ccbf80..ac1cfb15c7 100644 --- a/osu.Game/Overlays/OverlayColourProvider.cs +++ b/osu.Game/Overlays/OverlayColourProvider.cs @@ -12,12 +12,11 @@ namespace osu.Game.Overlays private readonly OverlayColourScheme colourScheme; public static OverlayColourProvider Red { get; } = new OverlayColourProvider(OverlayColourScheme.Red); - public static OverlayColourProvider DarkOrange { get; } = new OverlayColourProvider(OverlayColourScheme.DarkOrange); + public static OverlayColourProvider Pink { get; } = new OverlayColourProvider(OverlayColourScheme.Pink); public static OverlayColourProvider Orange { get; } = new OverlayColourProvider(OverlayColourScheme.Orange); public static OverlayColourProvider Green { get; } = new OverlayColourProvider(OverlayColourScheme.Green); - public static OverlayColourProvider Blue { get; } = new OverlayColourProvider(OverlayColourScheme.Blue); public static OverlayColourProvider Purple { get; } = new OverlayColourProvider(OverlayColourScheme.Purple); - public static OverlayColourProvider Pink { get; } = new OverlayColourProvider(OverlayColourScheme.Pink); + public static OverlayColourProvider Blue { get; } = new OverlayColourProvider(OverlayColourScheme.Blue); public OverlayColourProvider(OverlayColourScheme colourScheme) { @@ -63,8 +62,8 @@ namespace osu.Game.Overlays case OverlayColourScheme.Red: return 0; - case OverlayColourScheme.DarkOrange: - return 20 / 360f; + case OverlayColourScheme.Pink: + return 333 / 360f; case OverlayColourScheme.Orange: return 45 / 360f; @@ -72,17 +71,11 @@ namespace osu.Game.Overlays case OverlayColourScheme.Green: return 115 / 360f; - case OverlayColourScheme.Cyan: - return 160 / 360f; - - case OverlayColourScheme.Blue: - return 200 / 360f; - case OverlayColourScheme.Purple: return 255 / 360f; - case OverlayColourScheme.Pink: - return 333 / 360f; + case OverlayColourScheme.Blue: + return 200 / 360f; } } } @@ -91,11 +84,9 @@ namespace osu.Game.Overlays { Red, Pink, - DarkOrange, Orange, Green, Purple, - Blue, - Cyan, + Blue } } From 821c1b5335d5d3a7b8bd3bd89b3a30f255bc320e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 12 Jul 2021 19:11:26 +0300 Subject: [PATCH 0344/2442] Update hue of green colour scheme --- osu.Game/Overlays/OverlayColourProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/OverlayColourProvider.cs b/osu.Game/Overlays/OverlayColourProvider.cs index ac1cfb15c7..3fb8c6c4cf 100644 --- a/osu.Game/Overlays/OverlayColourProvider.cs +++ b/osu.Game/Overlays/OverlayColourProvider.cs @@ -69,7 +69,7 @@ namespace osu.Game.Overlays return 45 / 360f; case OverlayColourScheme.Green: - return 115 / 360f; + return 125 / 360f; case OverlayColourScheme.Purple: return 255 / 360f; From f3fe472a3313eb9f3406571d57d87cc7ed1559e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 12 Jul 2021 22:25:21 +0200 Subject: [PATCH 0345/2442] Add failing test case for reset to defaults --- .../TestSceneModDifficultyAdjustSettings.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs index bf494d4362..e0d76b3e4a 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs @@ -157,6 +157,23 @@ namespace osu.Game.Tests.Visual.UserInterface checkBindableAtValue("Circle Size", 3); } + [Test] + public void TestResetToDefaults() + { + setBeatmapWithDifficultyParameters(5); + + setSliderValue("Circle Size", 3); + setExtendedLimits(true); + + checkSliderAtValue("Circle Size", 3); + checkBindableAtValue("Circle Size", 3); + + AddStep("reset mod settings", () => modDifficultyAdjust.ResetSettingsToDefaults()); + + checkSliderAtValue("Circle Size", 5); + checkBindableAtValue("Circle Size", null); + } + private void resetToDefault(string name) { AddStep($"Reset {name} to default", () => From cce4a4dc31553b1a8ba2358529a4718fa71523f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 12 Jul 2021 22:25:33 +0200 Subject: [PATCH 0346/2442] Fix incorrect value copy order in `BindTo()` --- osu.Game/Rulesets/Mods/DifficultyBindable.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Mods/DifficultyBindable.cs b/osu.Game/Rulesets/Mods/DifficultyBindable.cs index ff859de30b..664b88eef4 100644 --- a/osu.Game/Rulesets/Mods/DifficultyBindable.cs +++ b/osu.Game/Rulesets/Mods/DifficultyBindable.cs @@ -104,15 +104,17 @@ namespace osu.Game.Rulesets.Mods if (!(them is DifficultyBindable otherDifficultyBindable)) throw new InvalidOperationException($"Cannot bind to a non-{nameof(DifficultyBindable)}."); - base.BindTo(them); - - CurrentNumber.BindTarget = otherDifficultyBindable.CurrentNumber; - ExtendedLimits.BindTarget = otherDifficultyBindable.ExtendedLimits; ReadCurrentFromDifficulty = otherDifficultyBindable.ReadCurrentFromDifficulty; - // the following is only safe as long as these values are effectively constants. + // the following max value copies are only safe as long as these values are effectively constants. MaxValue = otherDifficultyBindable.maxValue; ExtendedMaxValue = otherDifficultyBindable.extendedMaxValue; + + ExtendedLimits.BindTarget = otherDifficultyBindable.ExtendedLimits; + + // the actual values need to be copied after the max value constraints. + CurrentNumber.BindTarget = otherDifficultyBindable.CurrentNumber; + base.BindTo(them); } public override void UnbindFrom(IUnbindable them) From ac15dae93062b2cba9f934d9ae1ed5f7e822abe3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jul 2021 12:35:25 +0900 Subject: [PATCH 0347/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 9280eaf97c..d36ae50280 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8a3c69e40c..cf4918346c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 2eea646c61..e573ca97e3 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 125bd36ab161f683811d93d434ecbbcddc12f6ac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jul 2021 14:27:07 +0900 Subject: [PATCH 0348/2442] Send password in request ctor directly --- osu.Game/Online/Rooms/JoinRoomRequest.cs | 7 +++++-- osu.Game/Screens/OnlinePlay/Components/RoomManager.cs | 5 +---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/Rooms/JoinRoomRequest.cs b/osu.Game/Online/Rooms/JoinRoomRequest.cs index a82dc5a859..53bd2a6106 100644 --- a/osu.Game/Online/Rooms/JoinRoomRequest.cs +++ b/osu.Game/Online/Rooms/JoinRoomRequest.cs @@ -10,19 +10,22 @@ namespace osu.Game.Online.Rooms public class JoinRoomRequest : APIRequest { private readonly Room room; + private readonly string password; - public JoinRoomRequest(Room room) + public JoinRoomRequest(Room room, string password) { this.room = room; + this.password = password; } protected override WebRequest CreateWebRequest() { var req = base.CreateWebRequest(); req.Method = HttpMethod.Put; + req.AddParameter("password", password); return req; } - protected override string Target => $"rooms/{room.RoomID.Value}/users/{User.Id}?password={room.Password.Value}"; + protected override string Target => $"rooms/{room.RoomID.Value}/users/{User.Id}"; } } diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs index da02f9624f..422576648c 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs @@ -86,11 +86,8 @@ namespace osu.Game.Screens.OnlinePlay.Components public virtual void JoinRoom(Room room, string password = null, Action onSuccess = null, Action onError = null) { - // todo: send into JoinRoomRequest directly? - room.Password.Value = password; - currentJoinRoomRequest?.Cancel(); - currentJoinRoomRequest = new JoinRoomRequest(room); + currentJoinRoomRequest = new JoinRoomRequest(room, password); currentJoinRoomRequest.Success += () => { From 5cffaf4d3b591c366209228afd17822b06c73881 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jul 2021 14:34:56 +0900 Subject: [PATCH 0349/2442] Add extra explanatory comment to avoid any confusion --- .../Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 4f5d54e203..aa8a59e13b 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -283,6 +283,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { // the room has gone away. // this could mean something happened during the join process, or an external connection issue occurred. + // one specific scenario is where the underlying room is created, but the signalr server returns an error during the join process. this triggers a PartRoom operation (see https://github.com/ppy/osu/blob/7654df94f6f37b8382be7dfcb4f674e03bd35427/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs#L97) Schedule(this.Exit); } }, true); From 8c4a257742060336bb022edd6f379aa938467f05 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 13 Jul 2021 15:10:44 +0900 Subject: [PATCH 0350/2442] Add recent participants --- .../Multiplayer/TestSceneDrawableRoom.cs | 20 +- .../TestSceneRecentParticipantsList.cs | 93 +++++++ .../Components/RecentParticipantsList.cs | 238 ++++++++++++++++++ .../Lounge/Components/DrawableRoom.cs | 38 +++ 4 files changed, 381 insertions(+), 8 deletions(-) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs create mode 100644 osu.Game/Screens/OnlinePlay/Components/RecentParticipantsList.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index 3d65b5afda..0970085c25 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; @@ -31,28 +32,24 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = { Value = "Room 1" }, Status = { Value = new RoomStatusOpen() }, EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, - Host = { Value = new User { Username = "peppy", Id = 2 } } }), createDrawableRoom(new Room { Name = { Value = "Room 2" }, Status = { Value = new RoomStatusPlaying() }, EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, - Host = { Value = new User { Username = "peppy", Id = 2 } } }), createDrawableRoom(new Room { Name = { Value = "Room 3" }, Status = { Value = new RoomStatusEnded() }, EndDate = { Value = DateTimeOffset.Now }, - Host = { Value = new User { Username = "peppy", Id = 2 } } }), createDrawableRoom(new Room { Name = { Value = "Room 4" }, Status = { Value = new RoomStatusOpen() }, Category = { Value = RoomCategory.Realtime }, - Host = { Value = new User { Username = "peppy", Id = 2 } } }), } }; @@ -60,11 +57,18 @@ namespace osu.Game.Tests.Visual.Multiplayer private DrawableRoom createDrawableRoom(Room room) { - var drawableRoom = new DrawableRoom(room) - { - MatchingFilter = true - }; + room.Host.Value ??= new User { Username = "peppy", Id = 2 }; + if (room.RecentParticipants.Count == 0) + { + room.RecentParticipants.AddRange(Enumerable.Range(0, 20).Select(i => new User + { + Id = i, + Username = $"User {i}" + })); + } + + var drawableRoom = new DrawableRoom(room) { MatchingFilter = true }; drawableRoom.Action = () => drawableRoom.State = drawableRoom.State == SelectionState.Selected ? SelectionState.NotSelected : SelectionState.Selected; return drawableRoom; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs new file mode 100644 index 0000000000..ca8dc74759 --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs @@ -0,0 +1,93 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Testing; +using osu.Game.Online.Rooms; +using osu.Game.Screens.OnlinePlay.Components; +using osu.Game.Tests.Visual.OnlinePlay; +using osu.Game.Users; +using osu.Game.Users.Drawables; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneRecentParticipantsList : OnlinePlayTestScene + { + private RecentParticipantsList list; + + [SetUp] + public new void Setup() => Schedule(() => + { + SelectedRoom.Value = new Room { Name = { Value = "test room" } }; + + Child = list = new RecentParticipantsList + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + NumberOfAvatars = 3 + }; + }); + + [Test] + public void TestAvatarCount() + { + AddStep("add 50 users", () => + { + for (int i = 0; i < 50; i++) + { + SelectedRoom.Value.RecentParticipants.Add(new User + { + Id = i, + Username = $"User {i}" + }); + } + }); + + AddStep("set 3 avatars", () => list.NumberOfAvatars = 3); + AddAssert("3 avatars displayed", () => list.ChildrenOfType().Count() == 3); + AddAssert("47 hidden users", () => list.ChildrenOfType().Single().Count == 47); + + AddStep("set 10 avatars", () => list.NumberOfAvatars = 10); + AddAssert("10 avatars displayed", () => list.ChildrenOfType().Count() == 10); + AddAssert("40 hidden users", () => list.ChildrenOfType().Single().Count == 40); + } + + [Test] + public void TestAddAndRemoveUsers() + { + AddStep("add 50 users", () => + { + for (int i = 0; i < 50; i++) + { + SelectedRoom.Value.RecentParticipants.Add(new User + { + Id = i, + Username = $"User {i}" + }); + } + }); + + AddStep("remove from start", () => SelectedRoom.Value.RecentParticipants.RemoveAt(0)); + AddAssert("3 avatars displayed", () => list.ChildrenOfType().Count() == 3); + AddAssert("46 hidden users", () => list.ChildrenOfType().Single().Count == 46); + + AddStep("remove from end", () => SelectedRoom.Value.RecentParticipants.RemoveAt(SelectedRoom.Value.RecentParticipants.Count - 1)); + AddAssert("3 avatars displayed", () => list.ChildrenOfType().Count() == 3); + AddAssert("45 hidden users", () => list.ChildrenOfType().Single().Count == 45); + + AddRepeatStep("remove 45 users", () => SelectedRoom.Value.RecentParticipants.RemoveAt(0), 45); + AddAssert("3 avatars displayed", () => list.ChildrenOfType().Count() == 3); + AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0); + AddAssert("hidden users bubble hidden", () => list.ChildrenOfType().Single().Alpha < 0.5f); + + AddStep("remove another user", () => SelectedRoom.Value.RecentParticipants.RemoveAt(0)); + AddAssert("2 avatars displayed", () => list.ChildrenOfType().Count() == 2); + AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0); + + AddRepeatStep("remove the remaining two users", () => SelectedRoom.Value.RecentParticipants.RemoveAt(0), 2); + AddAssert("0 avatars displayed", () => !list.ChildrenOfType().Any()); + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Components/RecentParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Components/RecentParticipantsList.cs new file mode 100644 index 0000000000..d73387342a --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Components/RecentParticipantsList.cs @@ -0,0 +1,238 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Specialized; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics.Sprites; +using osu.Game.Users; +using osu.Game.Users.Drawables; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Screens.OnlinePlay.Components +{ + public class RecentParticipantsList : OnlinePlayComposite + { + private const float avatar_size = 36; + + private FillFlowContainer avatarFlow; + private HiddenUserCount hiddenUsers; + + public RecentParticipantsList() + { + AutoSizeAxes = Axes.X; + Height = 60; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChildren = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + MaskingSmoothness = 2, + CornerRadius = 10, + Shear = new Vector2(0.2f, 0), + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex(@"#2E3835") + } + }, + new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(4), + Padding = new MarginPadding { Left = 8, Right = 16 }, + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(16), + Icon = FontAwesome.Solid.User, + }, + avatarFlow = new FillFlowContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(4) + }, + hiddenUsers = new HiddenUserCount + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + } + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + RecentParticipants.BindCollectionChanged(onParticipantsChanged, true); + } + + private int numberOfAvatars = 3; + + public int NumberOfAvatars + { + get => numberOfAvatars; + set + { + numberOfAvatars = value; + + if (LoadState < LoadState.Loaded) + return; + + // Reinitialising the list looks janky, but this is unlikely to be used in a setting where it's visible. + clearUsers(); + foreach (var u in RecentParticipants) + addUser(u); + } + } + + private void onParticipantsChanged(object sender, NotifyCollectionChangedEventArgs e) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + foreach (var added in e.NewItems.OfType()) + addUser(added); + break; + + case NotifyCollectionChangedAction.Remove: + foreach (var removed in e.OldItems.OfType()) + removeUser(removed); + break; + + case NotifyCollectionChangedAction.Reset: + clearUsers(); + break; + + case NotifyCollectionChangedAction.Replace: + case NotifyCollectionChangedAction.Move: + // Easiest is to just reinitialise the whole list. These are unlikely to ever be use cases. + clearUsers(); + foreach (var u in RecentParticipants) + addUser(u); + break; + } + } + + private void addUser(User user) + { + if (avatarFlow.Count < NumberOfAvatars) + avatarFlow.Add(new CircularAvatar { User = user }); + else + hiddenUsers.Count++; + } + + private void removeUser(User user) + { + if (avatarFlow.RemoveAll(a => a.User == user) > 0) + { + if (RecentParticipants.Count >= NumberOfAvatars) + { + avatarFlow.Add(new CircularAvatar { User = RecentParticipants.First(u => avatarFlow.All(a => a.User != u)) }); + hiddenUsers.Count--; + } + } + else + hiddenUsers.Count--; + } + + private void clearUsers() + { + avatarFlow.Clear(); + hiddenUsers.Count = 0; + } + + private class CircularAvatar : CompositeDrawable + { + public User User + { + get => avatar.User; + set => avatar.User = value; + } + + private readonly UpdateableAvatar avatar; + + public CircularAvatar() + { + Size = new Vector2(avatar_size); + + InternalChild = new CircularContainer + { + RelativeSizeAxes = Axes.Both, + Masking = true, + Child = avatar = new UpdateableAvatar(showUsernameTooltip: true) { RelativeSizeAxes = Axes.Both } + }; + } + } + + public class HiddenUserCount : CompositeDrawable + { + public int Count + { + get => count; + set + { + count = value; + countText.Text = $"+{count}"; + + if (count > 0) + Show(); + else + Hide(); + } + } + + private int count; + + private readonly SpriteText countText; + + public HiddenUserCount() + { + Size = new Vector2(avatar_size); + Alpha = 0; + + InternalChild = new CircularContainer + { + RelativeSizeAxes = Axes.Both, + Masking = true, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black + }, + countText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + } + }; + } + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index a1f90f2d89..9385c3cae8 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -91,6 +91,22 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } + private int numberOfAvatars = 3; + + public int NumberOfAvatars + { + get => numberOfAvatars; + set + { + numberOfAvatars = value; + + if (recentParticipantsList != null) + recentParticipantsList.NumberOfAvatars = value; + } + } + + private RecentParticipantsList recentParticipantsList; + public bool FilteringActive { get; set; } public DrawableRoom(Room room) @@ -228,6 +244,28 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } }, + new FillFlowContainer + { + Name = "Right content", + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Padding = new MarginPadding + { + Right = 10, + Vertical = 5 + }, + Children = new Drawable[] + { + recentParticipantsList = new RecentParticipantsList + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + NumberOfAvatars = NumberOfAvatars + } + } + } }, }, }, From 689cee832c6f8ea2574d8bac1debd817d4e44cf9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 13 Jul 2021 15:50:46 +0900 Subject: [PATCH 0351/2442] Fix 1px gaps in DrawableRoom background --- .../Lounge/Components/DrawableRoom.cs | 64 +++++++++++-------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 9385c3cae8..f7f846ecfb 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -141,41 +141,49 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components }, Children = new Drawable[] { - new Box + // This resolves 1px gaps due to applying the (parenting) corner radius and masking across multiple filling background sprites. + new BufferedContainer { RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"#27302E"), - }, - new Container - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fill, - Child = new OnlinePlayBackgroundSprite(BeatmapSetCoverType.List) { RelativeSizeAxes = Axes.Both } - }, - new GridContainer - { - RelativeSizeAxes = Axes.Both, - ColumnDimensions = new[] + Children = new Drawable[] { - new Dimension(GridSizeMode.Relative, 0.2f) - }, - Content = new[] - { - new Drawable[] + new Box { - new Box + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex(@"#27302E"), + }, + new Container + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fill, + Child = new OnlinePlayBackgroundSprite(BeatmapSetCoverType.List) { RelativeSizeAxes = Axes.Both } + }, + new GridContainer + { + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] { - RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"#27302E"), + new Dimension(GridSizeMode.Relative, 0.2f) }, - new Box + Content = new[] { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal(Color4Extensions.FromHex(@"#27302E"), Color4Extensions.FromHex(@"#27302E").Opacity(0.3f)) - }, - } + new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex(@"#27302E"), + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientHorizontal(Color4Extensions.FromHex(@"#27302E"), Color4Extensions.FromHex(@"#27302E").Opacity(0.3f)) + }, + } + } + }, } }, new Container From ab282b9e59ca187954ca18ba0082ffa9e4e5b8c1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 13 Jul 2021 16:00:42 +0900 Subject: [PATCH 0352/2442] Remove RoomInspector from the lounge --- .../Multiplayer/TestSceneLoungeRoomInfo.cs | 49 ---------- .../Lounge/Components/ParticipantInfo.cs | 88 ------------------ .../OnlinePlay/Lounge/Components/RoomInfo.cs | 86 ------------------ .../Lounge/Components/RoomInspector.cs | 91 ------------------- .../Lounge/Components/RoomsContainer.cs | 2 +- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 33 +++---- 6 files changed, 12 insertions(+), 337 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs delete mode 100644 osu.Game/Screens/OnlinePlay/Lounge/Components/ParticipantInfo.cs delete mode 100644 osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInfo.cs delete mode 100644 osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInspector.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs deleted file mode 100644 index 471d0b6c98..0000000000 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomInfo.cs +++ /dev/null @@ -1,49 +0,0 @@ -// 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 NUnit.Framework; -using osu.Framework.Graphics; -using osu.Game.Online.Rooms; -using osu.Game.Online.Rooms.RoomStatuses; -using osu.Game.Screens.OnlinePlay.Lounge.Components; -using osu.Game.Tests.Visual.OnlinePlay; -using osu.Game.Users; - -namespace osu.Game.Tests.Visual.Multiplayer -{ - public class TestSceneLoungeRoomInfo : OnlinePlayTestScene - { - [SetUp] - public new void Setup() => Schedule(() => - { - SelectedRoom.Value = new Room(); - - Child = new RoomInfo - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Width = 500 - }; - }); - - [Test] - public void TestNonSelectedRoom() - { - AddStep("set null room", () => SelectedRoom.Value.RoomID.Value = null); - } - - [Test] - public void TestOpenRoom() - { - AddStep("set open room", () => - { - SelectedRoom.Value.RoomID.Value = 0; - SelectedRoom.Value.Name.Value = "Room 0"; - SelectedRoom.Value.Host.Value = new User { Username = "peppy", Id = 2 }; - SelectedRoom.Value.EndDate.Value = DateTimeOffset.Now.AddMonths(1); - SelectedRoom.Value.Status.Value = new RoomStatusOpen(); - }); - } - } -} diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/ParticipantInfo.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/ParticipantInfo.cs deleted file mode 100644 index bc4506b78e..0000000000 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/ParticipantInfo.cs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Humanizer; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Users.Drawables; -using osuTK; - -namespace osu.Game.Screens.OnlinePlay.Lounge.Components -{ - public class ParticipantInfo : OnlinePlayComposite - { - public ParticipantInfo() - { - RelativeSizeAxes = Axes.X; - Height = 15f; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - OsuSpriteText summary; - Container flagContainer; - LinkFlowContainer hostText; - - InternalChildren = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(5f, 0f), - Children = new Drawable[] - { - flagContainer = new Container - { - Width = 22f, - RelativeSizeAxes = Axes.Y, - }, - hostText = new LinkFlowContainer - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both - } - }, - }, - new FillFlowContainer - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Colour = colours.Gray9, - Children = new[] - { - summary = new OsuSpriteText - { - Text = "0 participants", - } - }, - }, - }; - - Host.BindValueChanged(host => - { - hostText.Clear(); - flagContainer.Clear(); - - if (host.NewValue != null) - { - hostText.AddText("hosted by "); - hostText.AddUserLink(host.NewValue, s => s.Font = s.Font.With(Typeface.Torus, weight: FontWeight.Bold, italics: true)); - - flagContainer.Child = new UpdateableFlag(host.NewValue.Country) { RelativeSizeAxes = Axes.Both }; - } - }, true); - - ParticipantCount.BindValueChanged(count => summary.Text = "participant".ToQuantity(count.NewValue), true); - } - } -} diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInfo.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInfo.cs deleted file mode 100644 index 819e19ad1e..0000000000 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInfo.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Screens.OnlinePlay.Components; -using osuTK; - -namespace osu.Game.Screens.OnlinePlay.Lounge.Components -{ - public class RoomInfo : OnlinePlayComposite - { - private readonly List statusElements = new List(); - private readonly OsuTextFlowContainer roomName; - - public RoomInfo() - { - AutoSizeAxes = Axes.Y; - - RoomLocalUserInfo localUserInfo; - EndDateInfo statusInfo; - ModeTypeInfo typeInfo; - ParticipantInfo participantInfo; - - InternalChild = new FillFlowContainer - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.X, - Spacing = new Vector2(0, 10), - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - roomName = new OsuTextFlowContainer(t => t.Font = OsuFont.GetFont(size: 30)) - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - }, - participantInfo = new ParticipantInfo(), - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - statusInfo = new EndDateInfo(), - typeInfo = new ModeTypeInfo - { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight - } - } - }, - localUserInfo = new RoomLocalUserInfo(), - } - }; - - statusElements.AddRange(new Drawable[] - { - statusInfo, typeInfo, participantInfo, localUserInfo - }); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - if (RoomID.Value == null) - statusElements.ForEach(e => e.FadeOut()); - RoomID.BindValueChanged(id => - { - if (id.NewValue == null) - statusElements.ForEach(e => e.FadeOut(100)); - else - statusElements.ForEach(e => e.FadeIn(100)); - }, true); - RoomName.BindValueChanged(name => - { - roomName.Text = name.NewValue ?? "No room selected"; - }, true); - } - } -} diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInspector.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInspector.cs deleted file mode 100644 index c28354c753..0000000000 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomInspector.cs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Screens.OnlinePlay.Components; -using osuTK.Graphics; - -namespace osu.Game.Screens.OnlinePlay.Lounge.Components -{ - public class RoomInspector : OnlinePlayComposite - { - private const float transition_duration = 100; - - private readonly MarginPadding contentPadding = new MarginPadding { Horizontal = 20, Vertical = 10 }; - - [Resolved] - private BeatmapManager beatmaps { get; set; } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - OverlinedHeader participantsHeader; - - InternalChildren = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.25f - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 30 }, - Child = new GridContainer - { - RelativeSizeAxes = Axes.Both, - Content = new[] - { - new Drawable[] - { - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new RoomInfo - { - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding { Vertical = 60 }, - }, - participantsHeader = new OverlinedHeader("Recent Participants"), - new ParticipantsDisplay(Direction.Vertical) - { - RelativeSizeAxes = Axes.X, - Height = ParticipantsList.TILE_SIZE * 3, - Details = { BindTarget = participantsHeader.Details } - } - } - } - }, - new Drawable[] { new OverlinedPlaylistHeader(), }, - new Drawable[] - { - new DrawableRoomPlaylist(false, false) - { - RelativeSizeAxes = Axes.Both, - Items = { BindTarget = Playlist } - }, - }, - }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.AutoSize), - new Dimension(), - } - } - } - }; - } - } -} diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index 5f135a3e90..0c6c84e656 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -60,7 +60,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, - Spacing = new Vector2(2), + Spacing = new Vector2(10), } }; } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index f24577a8a5..9004fe8dcc 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -57,31 +57,20 @@ namespace osu.Game.Screens.OnlinePlay.Lounge content = new Container { RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + Child = new Container { - new Container + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Width = 0.55f, - Children = new Drawable[] + scrollContainer = new OsuScrollContainer { - scrollContainer = new OsuScrollContainer - { - RelativeSizeAxes = Axes.Both, - ScrollbarOverlapsContent = false, - Padding = new MarginPadding(10), - Child = roomsContainer = new RoomsContainer { JoinRequested = joinRequested } - }, - loadingLayer = new LoadingLayer(true), - } - }, - new RoomInspector - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - RelativeSizeAxes = Axes.Both, - Width = 0.45f, - }, + RelativeSizeAxes = Axes.Both, + ScrollbarOverlapsContent = false, + Padding = new MarginPadding(10), + Child = roomsContainer = new RoomsContainer { JoinRequested = joinRequested } + }, + loadingLayer = new LoadingLayer(true), + } }, }, filter = CreateFilterControl().With(d => From 0cb80e105b22c3f4dc8ba7ba24a78d4c10a01b77 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 13 Jul 2021 16:02:18 +0900 Subject: [PATCH 0353/2442] Renamespace classes --- osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs | 2 +- .../Visual/Multiplayer/TestSceneRecentParticipantsList.cs | 2 +- .../Screens/OnlinePlay/{ => Lounge}/Components/EndDateInfo.cs | 2 +- .../Screens/OnlinePlay/{ => Lounge}/Components/PillContainer.cs | 2 +- .../OnlinePlay/{ => Lounge}/Components/PlaylistCountPill.cs | 2 +- .../Screens/OnlinePlay/{ => Lounge}/Components/RankRangePill.cs | 2 +- .../{ => Lounge}/Components/RecentParticipantsList.cs | 2 +- .../OnlinePlay/{ => Lounge}/Components/RoomStatusPill.cs | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) rename osu.Game/Screens/OnlinePlay/{ => Lounge}/Components/EndDateInfo.cs (97%) rename osu.Game/Screens/OnlinePlay/{ => Lounge}/Components/PillContainer.cs (97%) rename osu.Game/Screens/OnlinePlay/{ => Lounge}/Components/PlaylistCountPill.cs (96%) rename osu.Game/Screens/OnlinePlay/{ => Lounge}/Components/RankRangePill.cs (97%) rename osu.Game/Screens/OnlinePlay/{ => Lounge}/Components/RecentParticipantsList.cs (99%) rename osu.Game/Screens/OnlinePlay/{ => Lounge}/Components/RoomStatusPill.cs (97%) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs index d6bbaabfa6..5e28b3f8e5 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs @@ -3,7 +3,7 @@ using NUnit.Framework; using osu.Framework.Graphics; -using osu.Game.Screens.OnlinePlay.Components; +using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Users; namespace osu.Game.Tests.Visual.Multiplayer diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs index ca8dc74759..c9959b3467 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs @@ -6,7 +6,7 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Online.Rooms; -using osu.Game.Screens.OnlinePlay.Components; +using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Tests.Visual.OnlinePlay; using osu.Game.Users; using osu.Game.Users.Drawables; diff --git a/osu.Game/Screens/OnlinePlay/Components/EndDateInfo.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/EndDateInfo.cs similarity index 97% rename from osu.Game/Screens/OnlinePlay/Components/EndDateInfo.cs rename to osu.Game/Screens/OnlinePlay/Lounge/Components/EndDateInfo.cs index 3f93279461..3207d373db 100644 --- a/osu.Game/Screens/OnlinePlay/Components/EndDateInfo.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/EndDateInfo.cs @@ -7,7 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Graphics; -namespace osu.Game.Screens.OnlinePlay.Components +namespace osu.Game.Screens.OnlinePlay.Lounge.Components { public class EndDateInfo : OnlinePlayComposite { diff --git a/osu.Game/Screens/OnlinePlay/Components/PillContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/PillContainer.cs similarity index 97% rename from osu.Game/Screens/OnlinePlay/Components/PillContainer.cs rename to osu.Game/Screens/OnlinePlay/Lounge/Components/PillContainer.cs index 10a14d16da..15a532eaf3 100644 --- a/osu.Game/Screens/OnlinePlay/Components/PillContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/PillContainer.cs @@ -7,7 +7,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osuTK.Graphics; -namespace osu.Game.Screens.OnlinePlay.Components +namespace osu.Game.Screens.OnlinePlay.Lounge.Components { /// /// Displays contents in a "pill". diff --git a/osu.Game/Screens/OnlinePlay/Components/PlaylistCountPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs similarity index 96% rename from osu.Game/Screens/OnlinePlay/Components/PlaylistCountPill.cs rename to osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs index 2d9e7c143c..8d8309fcaa 100644 --- a/osu.Game/Screens/OnlinePlay/Components/PlaylistCountPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs @@ -7,7 +7,7 @@ using osu.Framework.Graphics; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -namespace osu.Game.Screens.OnlinePlay.Components +namespace osu.Game.Screens.OnlinePlay.Lounge.Components { /// /// A pill that displays the playlist item count. diff --git a/osu.Game/Screens/OnlinePlay/Components/RankRangePill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RankRangePill.cs similarity index 97% rename from osu.Game/Screens/OnlinePlay/Components/RankRangePill.cs rename to osu.Game/Screens/OnlinePlay/Lounge/Components/RankRangePill.cs index d7bdf13f6a..8f685ba033 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RankRangePill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RankRangePill.cs @@ -11,7 +11,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Screens.OnlinePlay.Multiplayer; using osuTK; -namespace osu.Game.Screens.OnlinePlay.Components +namespace osu.Game.Screens.OnlinePlay.Lounge.Components { public class RankRangePill : MultiplayerRoomComposite { diff --git a/osu.Game/Screens/OnlinePlay/Components/RecentParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs similarity index 99% rename from osu.Game/Screens/OnlinePlay/Components/RecentParticipantsList.cs rename to osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs index d73387342a..bbea1ea1c3 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RecentParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs @@ -15,7 +15,7 @@ using osu.Game.Users.Drawables; using osuTK; using osuTK.Graphics; -namespace osu.Game.Screens.OnlinePlay.Components +namespace osu.Game.Screens.OnlinePlay.Lounge.Components { public class RecentParticipantsList : OnlinePlayComposite { diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomStatusPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs similarity index 97% rename from osu.Game/Screens/OnlinePlay/Components/RoomStatusPill.cs rename to osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs index d18eb1bd94..ae17a80705 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomStatusPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs @@ -11,7 +11,7 @@ using osu.Game.Online.Rooms; using osu.Game.Online.Rooms.RoomStatuses; using osuTK.Graphics; -namespace osu.Game.Screens.OnlinePlay.Components +namespace osu.Game.Screens.OnlinePlay.Lounge.Components { /// /// A pill that displays the room's current status. From 6409a518dbba46a9f5f30f88f6b560442c9daa9c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jul 2021 16:34:56 +0900 Subject: [PATCH 0354/2442] Focus password text box on popover display --- .../OnlinePlay/Lounge/Components/DrawableRoom.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index a95e0c2b16..b8c4b57378 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -357,11 +357,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components this.room = room; } + private OsuPasswordTextBox passwordTextbox; + [BackgroundDependencyLoader] private void load(OsuColour colours) { - OsuPasswordTextBox passwordTextbox; - Child = new FillFlowContainer { Margin = new MarginPadding(10), @@ -383,6 +383,13 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } }; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + GetContainingInputManager().ChangeFocus(passwordTextbox); + } } } } From b5d4b9444fbffcf2b2e202145a405d2559c7a5c7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 13 Jul 2021 16:51:29 +0900 Subject: [PATCH 0355/2442] wip --- .../Lounge/Components/FilterControl.cs | 70 ++++++++----------- .../Components/PlaylistsFilterControl.cs | 10 ++- 2 files changed, 35 insertions(+), 45 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterControl.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterControl.cs index 7fc1c670ca..1827efb4a2 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterControl.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterControl.cs @@ -5,19 +5,21 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Threading; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets; -using osuTK.Graphics; +using osuTK; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { public abstract class FilterControl : CompositeDrawable { protected const float VERTICAL_PADDING = 10; - protected const float HORIZONTAL_PADDING = 80; + protected const float HORIZONTAL_PADDING = 20; + + protected readonly FillFlowContainer Filters; [Resolved(CanBeNull = true)] private Bindable filter { get; set; } @@ -25,60 +27,50 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components [Resolved] private IBindable ruleset { get; set; } - private readonly Box tabStrip; private readonly SearchTextBox search; - private readonly PageTabControl tabs; + private readonly Dropdown statusDropdown; protected FilterControl() { - InternalChildren = new Drawable[] + InternalChild = new Container { - new Box + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.25f, + Top = VERTICAL_PADDING, + Horizontal = HORIZONTAL_PADDING }, - tabStrip = new Box + Children = new Drawable[] { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.X, - Height = 1, - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding + search = new FilterSearchTextBox { - Top = VERTICAL_PADDING, - Horizontal = HORIZONTAL_PADDING + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + RelativeSizeAxes = Axes.X, + Width = 0.6f, }, - Children = new Drawable[] + Filters = new FillFlowContainer { - search = new FilterSearchTextBox + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10), + Padding = new MarginPadding { Vertical = 30 }, + Child = statusDropdown = new SlimEnumDropdown { - RelativeSizeAxes = Axes.X, - }, - tabs = new PageTabControl - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.X, - }, - } + Anchor = Anchor.BottomRight, + Origin = Anchor.TopRight, + RelativeSizeAxes = Axes.None, + Width = 160, + } + }, } }; - - tabs.Current.Value = RoomStatusFilter.Open; - tabs.Current.TriggerChange(); } [BackgroundDependencyLoader] private void load(OsuColour colours) { filter ??= new Bindable(); - tabStrip.Colour = colours.Yellow; } protected override void LoadComplete() @@ -87,7 +79,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components search.Current.BindValueChanged(_ => updateFilterDebounced()); ruleset.BindValueChanged(_ => UpdateFilter()); - tabs.Current.BindValueChanged(_ => UpdateFilter(), true); + statusDropdown.Current.BindValueChanged(_ => UpdateFilter(), true); } private ScheduledDelegate scheduledFilterUpdate; @@ -106,7 +98,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components var criteria = CreateCriteria(); criteria.SearchString = search.Current.Value; - criteria.Status = tabs.Current.Value; + criteria.Status = statusDropdown.Current.Value; criteria.Ruleset = ruleset.Value; filter.Value = criteria; diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistsFilterControl.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistsFilterControl.cs index a463742097..e6309a8e06 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistsFilterControl.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistsFilterControl.cs @@ -9,18 +9,16 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { public class PlaylistsFilterControl : FilterControl { - private readonly Dropdown dropdown; + private readonly Dropdown categoryDropdown; public PlaylistsFilterControl() { - AddInternal(dropdown = new SlimEnumDropdown + Filters.Add(categoryDropdown = new SlimEnumDropdown { Anchor = Anchor.BottomRight, Origin = Anchor.TopRight, RelativeSizeAxes = Axes.None, Width = 160, - X = -HORIZONTAL_PADDING, - Y = -30 }); } @@ -28,14 +26,14 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { base.LoadComplete(); - dropdown.Current.BindValueChanged(_ => UpdateFilter()); + categoryDropdown.Current.BindValueChanged(_ => UpdateFilter()); } protected override FilterCriteria CreateCriteria() { var criteria = base.CreateCriteria(); - switch (dropdown.Current.Value) + switch (categoryDropdown.Current.Value) { case PlaylistsCategory.Normal: criteria.Category = "normal"; From 481e4dedb0d7c49e0a2470af5645ceba3cb2d521 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jul 2021 16:35:49 +0900 Subject: [PATCH 0356/2442] Move `PopoverContainer` to `OsuGameBase` --- osu.Game/OsuGameBase.cs | 7 ++++++- .../OnlinePlay/Lounge/Components/RoomsContainer.cs | 14 ++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 5878727ad8..4b5fa4f62e 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -13,6 +13,7 @@ using osu.Framework.Development; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Game.Beatmaps; @@ -341,7 +342,11 @@ namespace osu.Game globalBindings = new GlobalActionContainer(this) }; - MenuCursorContainer.Child = content = new OsuTooltipContainer(MenuCursorContainer.Cursor) { RelativeSizeAxes = Axes.Both }; + MenuCursorContainer.Child = new PopoverContainer + { + RelativeSizeAxes = Axes.Both, + Child = content = new OsuTooltipContainer(MenuCursorContainer.Cursor) { RelativeSizeAxes = Axes.Both } + }; base.Content.Add(CreateScalingContainer().WithChildren(mainContent)); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index 8ab80d947c..0910ff1a7a 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -11,7 +11,6 @@ using osu.Framework.Extensions; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Threading; @@ -51,21 +50,16 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - InternalChild = new PopoverContainer + InternalChild = new OsuContextMenuContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Child = new OsuContextMenuContainer + Child = roomFlow = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Child = roomFlow = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(2), - } + Direction = FillDirection.Vertical, + Spacing = new Vector2(2), } }; } From 60e17fc2b7b81998f4e2d5c1f30cdb26dc67b4d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jul 2021 17:12:35 +0900 Subject: [PATCH 0357/2442] Fix disconnected-from-server multiplayer exit sequence being blocked by confirmation dialog --- .../OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 4b8c4422ec..e1eebb7e41 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -310,7 +310,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public override bool OnExiting(IScreen next) { - if (client.Room == null) + // the room may not be left immediately after a disconnection due to async flow, + // so checking the IsConnected status is also required. + if (client.Room == null || !client.IsConnected.Value) { // room has not been created yet; exit immediately. return base.OnExiting(next); From 28ff92e34ec214f97d70d546ef7c8650ce757f1d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 13 Jul 2021 17:31:28 +0900 Subject: [PATCH 0358/2442] Add test --- .../Multiplayer/TestSceneMultiplayer.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 2bb3129f68..7673efb78f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -21,6 +21,7 @@ using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens; using osu.Game.Screens.OnlinePlay.Components; +using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; @@ -184,6 +185,26 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("play started", () => !multiplayerScreen.IsCurrentScreen()); } + [Test] + public void TestSubScreenExitedWhenDisconnectedFromMultiplayerServer() + { + createRoom(() => new Room + { + Name = { Value = "Test Room" }, + Playlist = + { + new PlaylistItem + { + Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + } + } + }); + + AddStep("disconnect", () => client.Disconnect()); + AddUntilStep("back in lounge", () => this.ChildrenOfType().FirstOrDefault()?.IsCurrentScreen() == true); + } + [Test] public void TestLeaveNavigation() { From 6da2a3d51fe441280af9536bca3fcf02662826ce Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Jul 2021 10:50:11 +0200 Subject: [PATCH 0359/2442] Add zero-length objects check and tests --- .../Checks/CheckZeroLengthObjectsTest.cs | 94 +++++++++++++++++++ .../Edit/Checks/CheckZeroLengthObjects.cs | 47 ++++++++++ 2 files changed, 141 insertions(+) create mode 100644 osu.Game.Tests/Editing/Checks/CheckZeroLengthObjectsTest.cs create mode 100644 osu.Game/Rulesets/Edit/Checks/CheckZeroLengthObjects.cs diff --git a/osu.Game.Tests/Editing/Checks/CheckZeroLengthObjectsTest.cs b/osu.Game.Tests/Editing/Checks/CheckZeroLengthObjectsTest.cs new file mode 100644 index 0000000000..93b20cd166 --- /dev/null +++ b/osu.Game.Tests/Editing/Checks/CheckZeroLengthObjectsTest.cs @@ -0,0 +1,94 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using Moq; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Checks; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Tests.Beatmaps; +using osuTK; + +namespace osu.Game.Tests.Editing.Checks +{ + [TestFixture] + public class CheckZeroLengthObjectsTest + { + private CheckZeroLengthObjects check; + + [SetUp] + public void Setup() + { + check = new CheckZeroLengthObjects(); + } + + [Test] + public void TestCircle() + { + assertOk(new List + { + new HitCircle { StartTime = 1000, Position = new Vector2(0, 0) } + }); + } + + [Test] + public void TestRegularSlider() + { + assertOk(new List + { + getSliderMock(1000).Object + }); + } + + [Test] + public void TestZeroLengthSlider() + { + assertZeroLength(new List + { + getSliderMock(0).Object + }); + } + + [Test] + public void TestNegativeLengthSlider() + { + assertZeroLength(new List + { + getSliderMock(-1000).Object + }); + } + + private Mock getSliderMock(double duration) + { + var mockSlider = new Mock(); + mockSlider.As().Setup(d => d.Duration).Returns(duration); + + return mockSlider; + } + + private void assertOk(List hitObjects) + { + Assert.That(check.Run(getContext(hitObjects)), Is.Empty); + } + + private void assertZeroLength(List hitObjects) + { + var issues = check.Run(getContext(hitObjects)).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.First().Template is CheckZeroLengthObjects.IssueTemplateZeroLength); + } + + private BeatmapVerifierContext getContext(List hitObjects) + { + var beatmap = new Beatmap { HitObjects = hitObjects }; + + return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap)); + } + } +} diff --git a/osu.Game/Rulesets/Edit/Checks/CheckZeroLengthObjects.cs b/osu.Game/Rulesets/Edit/Checks/CheckZeroLengthObjects.cs new file mode 100644 index 0000000000..b9be94736b --- /dev/null +++ b/osu.Game/Rulesets/Edit/Checks/CheckZeroLengthObjects.cs @@ -0,0 +1,47 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Game.Rulesets.Edit.Checks.Components; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Edit.Checks +{ + public class CheckZeroLengthObjects : ICheck + { + /// + /// The duration can be this low before being treated as having no length, in case of precision errors. Unit is milliseconds. + /// + private const double leniency = 0.5d; + + public CheckMetadata Metadata { get; } = new CheckMetadata(CheckCategory.Compose, "Zero-length hitobjects"); + + public IEnumerable PossibleTemplates => new IssueTemplate[] + { + new IssueTemplateZeroLength(this) + }; + + public IEnumerable Run(BeatmapVerifierContext context) + { + foreach (var hitObject in context.Beatmap.HitObjects) + { + if (!(hitObject is IHasDuration hasDuration)) + continue; + + if (hasDuration.Duration < leniency) + yield return new IssueTemplateZeroLength(this).Create(hitObject, hasDuration.Duration); + } + } + + public class IssueTemplateZeroLength : IssueTemplate + { + public IssueTemplateZeroLength(ICheck check) + : base(check, IssueType.Problem, "{0} has a duration of {1:0}.") + { + } + + public Issue Create(HitObject hitobject, double duration) => new Issue(hitobject, this, hitobject.GetType(), duration); + } + } +} From fec944830144eefcd2913a0d435ca507959a719f Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Jul 2021 10:50:41 +0200 Subject: [PATCH 0360/2442] Add too short sliders check and tests --- .../Editor/Checks/CheckTooShortSlidersTest.cs | 145 ++++++++++++++++++ .../Edit/Checks/CheckTooShortSliders.cs | 48 ++++++ 2 files changed, 193 insertions(+) create mode 100644 osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckTooShortSlidersTest.cs create mode 100644 osu.Game.Rulesets.Osu/Edit/Checks/CheckTooShortSliders.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckTooShortSlidersTest.cs b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckTooShortSlidersTest.cs new file mode 100644 index 0000000000..2eab5a4ce6 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckTooShortSlidersTest.cs @@ -0,0 +1,145 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Edit.Checks; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Tests.Beatmaps; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks +{ + [TestFixture] + public class CheckTooShortSlidersTest + { + private CheckTooShortSliders check; + + [SetUp] + public void Setup() + { + check = new CheckTooShortSliders(); + } + + [Test] + public void TestLongSlider() + { + Slider slider = new Slider + { + StartTime = 0, + RepeatCount = 0, + Path = new SliderPath(new[] + { + new PathControlPoint(new Vector2(0, 0)), + new PathControlPoint(new Vector2(100, 0)) + }) + }; + + slider.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + + assertOk(new List { slider }); + } + + [Test] + public void TestShortSlider() + { + Slider slider = new Slider + { + StartTime = 0, + RepeatCount = 0, + Path = new SliderPath(new[] + { + new PathControlPoint(new Vector2(0, 0)), + new PathControlPoint(new Vector2(25, 0)) + }) + }; + + slider.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + + assertOk(new List { slider }); + } + + [Test] + public void TestTooShortSliderExpert() + { + Slider slider = new Slider + { + StartTime = 0, + RepeatCount = 0, + Path = new SliderPath(new[] + { + new PathControlPoint(new Vector2(0, 0)), + new PathControlPoint(new Vector2(10, 0)) + }) + }; + + slider.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + + assertOk(new List { slider }, DifficultyRating.Expert); + } + + [Test] + public void TestTooShortSlider() + { + Slider slider = new Slider + { + StartTime = 0, + RepeatCount = 0, + Path = new SliderPath(new[] + { + new PathControlPoint(new Vector2(0, 0)), + new PathControlPoint(new Vector2(10, 0)) + }) + }; + + slider.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + + assertTooShort(new List { slider }); + } + + [Test] + public void TestTooShortSliderWithRepeats() + { + // Would be ok if we looked at the duration, but not if we look at the span duration. + Slider slider = new Slider + { + StartTime = 0, + RepeatCount = 2, + Path = new SliderPath(new[] + { + new PathControlPoint(new Vector2(0, 0)), + new PathControlPoint(new Vector2(10, 0)) + }) + }; + + slider.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + + assertTooShort(new List { slider }); + } + + private void assertOk(List hitObjects, DifficultyRating difficultyRating = DifficultyRating.Easy) + { + Assert.That(check.Run(getContext(hitObjects, difficultyRating)), Is.Empty); + } + + private void assertTooShort(List hitObjects, DifficultyRating difficultyRating = DifficultyRating.Easy) + { + var issues = check.Run(getContext(hitObjects, difficultyRating)).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.First().Template is CheckTooShortSliders.IssueTemplateTooShort); + } + + private BeatmapVerifierContext getContext(List hitObjects, DifficultyRating difficultyRating) + { + var beatmap = new Beatmap { HitObjects = hitObjects }; + + return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap), difficultyRating); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckTooShortSliders.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckTooShortSliders.cs new file mode 100644 index 0000000000..159498c479 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckTooShortSliders.cs @@ -0,0 +1,48 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Checks.Components; +using osu.Game.Rulesets.Osu.Objects; + +namespace osu.Game.Rulesets.Osu.Edit.Checks +{ + public class CheckTooShortSliders : ICheck + { + /// + /// The shortest acceptable duration between the head and tail of the slider (so ignoring repeats). + /// + private const double span_duration_threshold = 125; // 240 BPM 1/2 + + public CheckMetadata Metadata { get; } = new CheckMetadata(CheckCategory.Spread, "Too short sliders"); + + public IEnumerable PossibleTemplates => new IssueTemplate[] + { + new IssueTemplateTooShort(this) + }; + + public IEnumerable Run(BeatmapVerifierContext context) + { + if (context.InterpretedDifficulty > DifficultyRating.Easy) + yield break; + + foreach (var hitObject in context.Beatmap.HitObjects) + { + if (hitObject is Slider slider && slider.SpanDuration < span_duration_threshold) + yield return new IssueTemplateTooShort(this).Create(slider); + } + } + + public class IssueTemplateTooShort : IssueTemplate + { + public IssueTemplateTooShort(ICheck check) + : base(check, IssueType.Problem, "This slider is too short ({0:0} ms), expected at least {1:0} ms.") + { + } + + public Issue Create(Slider slider) => new Issue(slider, this, slider.SpanDuration, span_duration_threshold); + } + } +} From 53c0298b5ea9689eba4eb33dfc29f43224e63e50 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Jul 2021 10:51:40 +0200 Subject: [PATCH 0361/2442] Add too short spinners check and tests --- .../Checks/CheckTooShortSpinnersTest.cs | 116 ++++++++++++++++++ .../Edit/Checks/CheckTooShortSpinners.cs | 61 +++++++++ 2 files changed, 177 insertions(+) create mode 100644 osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckTooShortSpinnersTest.cs create mode 100644 osu.Game.Rulesets.Osu/Edit/Checks/CheckTooShortSpinners.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckTooShortSpinnersTest.cs b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckTooShortSpinnersTest.cs new file mode 100644 index 0000000000..6a3f168ee1 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckTooShortSpinnersTest.cs @@ -0,0 +1,116 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Edit.Checks; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks +{ + [TestFixture] + public class CheckTooShortSpinnersTest + { + private CheckTooShortSpinners check; + private BeatmapDifficulty difficulty; + + [SetUp] + public void Setup() + { + check = new CheckTooShortSpinners(); + difficulty = new BeatmapDifficulty(); + } + + [Test] + public void TestLongSpinner() + { + Spinner spinner = new Spinner { StartTime = 0, Duration = 4000 }; + spinner.ApplyDefaults(new ControlPointInfo(), difficulty); + + assertOk(new List { spinner }, difficulty); + } + + [Test] + public void TestShortSpinner() + { + Spinner spinner = new Spinner { StartTime = 0, Duration = 750 }; + spinner.ApplyDefaults(new ControlPointInfo(), difficulty); + + assertOk(new List { spinner }, difficulty); + } + + [Test] + public void TestVeryShortSpinner() + { + // Spinners at a certain duration only get 1000 points if approached by auto at a certain angle, making it difficult to determine. + Spinner spinner = new Spinner { StartTime = 0, Duration = 475 }; + spinner.ApplyDefaults(new ControlPointInfo(), difficulty); + + assertVeryShort(new List { spinner }, difficulty); + } + + [Test] + public void TestTooShortSpinner() + { + Spinner spinner = new Spinner { StartTime = 0, Duration = 400 }; + spinner.ApplyDefaults(new ControlPointInfo(), difficulty); + + assertTooShort(new List { spinner }, difficulty); + } + + [Test] + public void TestTooShortSpinnerVaryingOd() + { + const double duration = 450; + + var difficultyLowOd = new BeatmapDifficulty { OverallDifficulty = 1 }; + Spinner spinnerLowOd = new Spinner { StartTime = 0, Duration = duration }; + spinnerLowOd.ApplyDefaults(new ControlPointInfo(), difficultyLowOd); + + var difficultyHighOd = new BeatmapDifficulty { OverallDifficulty = 10 }; + Spinner spinnerHighOd = new Spinner { StartTime = 0, Duration = duration }; + spinnerHighOd.ApplyDefaults(new ControlPointInfo(), difficultyHighOd); + + assertOk(new List { spinnerLowOd }, difficultyLowOd); + assertTooShort(new List { spinnerHighOd }, difficultyHighOd); + } + + private void assertOk(List hitObjects, BeatmapDifficulty beatmapDifficulty) + { + Assert.That(check.Run(getContext(hitObjects, beatmapDifficulty)), Is.Empty); + } + + private void assertVeryShort(List hitObjects, BeatmapDifficulty beatmapDifficulty) + { + var issues = check.Run(getContext(hitObjects, beatmapDifficulty)).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.First().Template is CheckTooShortSpinners.IssueTemplateVeryShort); + } + + private void assertTooShort(List hitObjects, BeatmapDifficulty beatmapDifficulty) + { + var issues = check.Run(getContext(hitObjects, beatmapDifficulty)).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.First().Template is CheckTooShortSpinners.IssueTemplateTooShort); + } + + private BeatmapVerifierContext getContext(List hitObjects, BeatmapDifficulty beatmapDifficulty) + { + var beatmap = new Beatmap + { + HitObjects = hitObjects, + BeatmapInfo = new BeatmapInfo { BaseDifficulty = beatmapDifficulty } + }; + + return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap)); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckTooShortSpinners.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckTooShortSpinners.cs new file mode 100644 index 0000000000..0d0c3d9e69 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckTooShortSpinners.cs @@ -0,0 +1,61 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Checks.Components; +using osu.Game.Rulesets.Osu.Objects; + +namespace osu.Game.Rulesets.Osu.Edit.Checks +{ + public class CheckTooShortSpinners : ICheck + { + public CheckMetadata Metadata { get; } = new CheckMetadata(CheckCategory.Spread, "Too short spinners"); + + public IEnumerable PossibleTemplates => new IssueTemplate[] + { + new IssueTemplateTooShort(this) + }; + + public IEnumerable Run(BeatmapVerifierContext context) + { + double od = context.Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty; + + // These are meant to reflect the duration necessary for auto to score at least 1000 points on the spinner. + // It's difficult to eliminate warnings here, as auto achieving 1000 points depends on the approach angle on some spinners. + double warningThreshold = 500 + (od < 5 ? (5 - od) * -21.8 : (od - 5) * 20); // Anything above this is always ok. + double problemThreshold = 450 + (od < 5 ? (5 - od) * -17 : (od - 5) * 17); // Anything below this is never ok. + + foreach (var hitObject in context.Beatmap.HitObjects) + { + if (!(hitObject is Spinner spinner)) + continue; + + if (spinner.Duration < problemThreshold) + yield return new IssueTemplateTooShort(this).Create(spinner); + else if (spinner.Duration < warningThreshold) + yield return new IssueTemplateVeryShort(this).Create(spinner); + } + } + + public class IssueTemplateTooShort : IssueTemplate + { + public IssueTemplateTooShort(ICheck check) + : base(check, IssueType.Problem, "This spinner is too short. Auto cannot achieve 1000 points on this.") + { + } + + public Issue Create(Spinner spinner) => new Issue(spinner, this); + } + + public class IssueTemplateVeryShort : IssueTemplate + { + public IssueTemplateVeryShort(ICheck check) + : base(check, IssueType.Warning, "This spinner may be too short. Ensure auto can achieve 1000 points on this.") + { + } + + public Issue Create(Spinner spinner) => new Issue(spinner, this); + } + } +} From 3a5912e35eb03736be9cc6ca6883fa4617583e5c Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Jul 2021 10:53:25 +0200 Subject: [PATCH 0362/2442] Add new checks to verifiers --- osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs | 4 +++- osu.Game/Rulesets/Edit/BeatmapVerifier.cs | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs index 896e904f3f..221723e4cd 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs @@ -15,10 +15,12 @@ namespace osu.Game.Rulesets.Osu.Edit { // Compose new CheckOffscreenObjects(), + new CheckTooShortSpinners(), // Spread new CheckTimeDistanceEquality(), - new CheckLowDiffOverlaps() + new CheckLowDiffOverlaps(), + new CheckTooShortSliders(), }; public IEnumerable Run(BeatmapVerifierContext context) diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs index 706eec226c..81f4808789 100644 --- a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs +++ b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs @@ -27,7 +27,8 @@ namespace osu.Game.Rulesets.Edit // Compose new CheckUnsnappedObjects(), - new CheckConcurrentObjects() + new CheckConcurrentObjects(), + new CheckZeroLengthObjects(), }; public IEnumerable Run(BeatmapVerifierContext context) From e791669c40559122375e050642839aa363352af8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jul 2021 17:59:35 +0900 Subject: [PATCH 0363/2442] Fix multiplayer screen buttons showing no text when local user not available --- .../Match/MultiplayerReadyButton.cs | 20 +++++++------------ .../Match/MultiplayerSpectateButton.cs | 14 ++++--------- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index f2dd9a6f25..baf9570209 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -72,25 +71,20 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { var localUser = Client.LocalUser; - if (localUser == null) - return; + int newCountReady = Room?.Users.Count(u => u.State == MultiplayerUserState.Ready) ?? 0; + int newCountTotal = Room?.Users.Count(u => u.State != MultiplayerUserState.Spectating) ?? 0; - Debug.Assert(Room != null); - - int newCountReady = Room.Users.Count(u => u.State == MultiplayerUserState.Ready); - int newCountTotal = Room.Users.Count(u => u.State != MultiplayerUserState.Spectating); - - string countText = $"({newCountReady} / {newCountTotal} ready)"; - - switch (localUser.State) + switch (localUser?.State) { - case MultiplayerUserState.Idle: + default: button.Text = "Ready"; updateButtonColour(true); break; case MultiplayerUserState.Spectating: case MultiplayerUserState.Ready: + string countText = $"({newCountReady} / {newCountTotal} ready)"; + if (Room?.Host?.Equals(localUser) == true) { button.Text = $"Start match {countText}"; @@ -108,7 +102,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match bool enableButton = Client.Room?.State == MultiplayerRoomState.Open && !operationInProgress.Value; // When the local user is the host and spectating the match, the "start match" state should be enabled if any users are ready. - if (localUser.State == MultiplayerUserState.Spectating) + if (localUser?.State == MultiplayerUserState.Spectating) enableButton &= Room?.Host?.Equals(localUser) == true && newCountReady > 0; button.Enabled.Value = enableButton; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerSpectateButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerSpectateButton.cs index 04150902bc..db99c6a5d5 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerSpectateButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerSpectateButton.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -57,14 +56,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private void updateState() { - var localUser = Client.LocalUser; - - if (localUser == null) - return; - - Debug.Assert(Room != null); - - switch (localUser.State) + switch (Client.LocalUser?.State) { default: button.Text = "Spectate"; @@ -81,7 +73,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match break; } - button.Enabled.Value = Client.Room?.State != MultiplayerRoomState.Closed && !operationInProgress.Value; + button.Enabled.Value = Client.Room != null + && Client.Room.State != MultiplayerRoomState.Closed + && !operationInProgress.Value; } private class ButtonWithTrianglesExposed : TriangleButton From 6b663037e47b0b448d98449134248699dfce9d6d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jul 2021 19:37:02 +0900 Subject: [PATCH 0364/2442] Use `switch` for pattern matching --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index f919ecf839..7794ef8072 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -70,13 +70,15 @@ namespace osu.Game.Rulesets.Osu.Mods // Move hit objects back into the playfield if they are outside of it Vector2 shift = Vector2.Zero; - if (hitObject is HitCircle circle) + switch (hitObject) { - shift = clampHitCircleToPlayfield(circle, current); - } - else if (hitObject is Slider slider) - { - shift = clampSliderToPlayfield(slider, current); + case HitCircle circle: + shift = clampHitCircleToPlayfield(circle, current); + break; + + case Slider slider: + shift = clampSliderToPlayfield(slider, current); + break; } if (shift != Vector2.Zero) From 4314946e10726756eb7a2df717fbbab131a14e19 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jul 2021 19:37:17 +0900 Subject: [PATCH 0365/2442] Reorganise functions to order more logically (hitcircle before slider methods) --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 34 +++++++++++----------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 7794ef8072..1a2e5d92b4 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -142,6 +142,23 @@ namespace osu.Game.Rulesets.Osu.Mods current.PositionRandomised = previous.EndPositionRandomised + posRelativeToPrev; } + /// + /// Move the randomised position of a hit circle so that it fits inside the playfield. + /// + /// The deviation from the original randomised position in order to fit within the playfield. + private Vector2 clampHitCircleToPlayfield(HitCircle circle, RandomObjectInfo objectInfo) + { + var previousPosition = objectInfo.PositionRandomised; + objectInfo.EndPositionRandomised = objectInfo.PositionRandomised = clampToPlayfieldWithPadding( + objectInfo.PositionRandomised, + (float)circle.Radius + ); + + circle.Position = objectInfo.PositionRandomised; + + return objectInfo.PositionRandomised - previousPosition; + } + /// /// Moves the and all necessary nested s into the if they aren't already. /// @@ -254,23 +271,6 @@ namespace osu.Game.Rulesets.Osu.Mods } } - /// - /// Move the randomised position of a hit circle so that it fits inside the playfield. - /// - /// The deviation from the original randomised position in order to fit within the playfield. - private Vector2 clampHitCircleToPlayfield(HitCircle circle, RandomObjectInfo objectInfo) - { - var previousPosition = objectInfo.PositionRandomised; - objectInfo.EndPositionRandomised = objectInfo.PositionRandomised = clampToPlayfieldWithPadding( - objectInfo.PositionRandomised, - (float)circle.Radius - ); - - circle.Position = objectInfo.PositionRandomised; - - return objectInfo.PositionRandomised - previousPosition; - } - /// /// Clamp a position to playfield, keeping a specified distance from the edges. /// From e7b78b1ea5299dae24c07a16243f24ba4888d170 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Jul 2021 20:26:05 +0900 Subject: [PATCH 0366/2442] Adjust transform logic to hopefully be a bit easier to parse --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 25 ++++++++++++---------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 27c352070a..8f9ff81808 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -31,6 +31,7 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Skinning; using osuTK; +using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Mods { @@ -143,22 +144,24 @@ namespace osu.Game.Rulesets.Osu.Mods { if (!(drawable is DrawableHitCircle circle)) return; - var h = (OsuHitObject)drawable.HitObject; + double startTime = circle.HitObject.StartTime; + double preempt = circle.HitObject.TimePreempt; - using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) + using (drawable.BeginAbsoluteSequence(startTime - preempt)) { + // initial state drawable.ScaleTo(0.5f) - .Then().ScaleTo(1f, h.TimePreempt); + .FadeColour(OsuColour.Gray(0.5f)); - var colour = drawable.Colour; - - drawable.FadeColour(OsuColour.Gray(0.45f)) - .Then().Delay(h.TimePreempt - controlPointInfo.TimingPointAt(h.StartTime).BeatLength - undim_duration) - .FadeColour(colour, undim_duration); - - // Remove approach circles - circle.ApproachCircle.Hide(); + // scale to final size + drawable.ScaleTo(1f, preempt); } + + using (drawable.BeginAbsoluteSequence(startTime - controlPointInfo.TimingPointAt(startTime).BeatLength - undim_duration)) + drawable.FadeColour(Color4.White, undim_duration); + + // Remove approach circles + circle.ApproachCircle.Hide(); } #endregion From 9bec53bfa8922bc7de66278a1f0c30e1ba65eb00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 20 Jun 2021 19:31:24 +0200 Subject: [PATCH 0367/2442] Implement osu!-side popover --- .../UserInterface/TestSceneOsuPopover.cs | 103 ++++++++++++++++++ .../Graphics/UserInterfaceV2/OsuPopover.cs | 44 ++++++++ 2 files changed, 147 insertions(+) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneOsuPopover.cs create mode 100644 osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuPopover.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuPopover.cs new file mode 100644 index 0000000000..1848cf6a5e --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuPopover.cs @@ -0,0 +1,103 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// 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.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Overlays; +using osuTK; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneOsuPopover : OsuGridTestScene + { + public TestSceneOsuPopover() + : base(1, 2) + { + Cell(0, 0).Child = new PopoverContainer + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = @"No OverlayColourProvider", + Font = OsuFont.Default.With(size: 40) + }, + new TriangleButtonWithPopover() + } + }; + + Cell(0, 1).Child = new ColourProvidingContainer(OverlayColourScheme.Orange) + { + RelativeSizeAxes = Axes.Both, + Child = new PopoverContainer + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = @"With OverlayColourProvider (orange)", + Font = OsuFont.Default.With(size: 40) + }, + new TriangleButtonWithPopover() + } + } + }; + } + + private class TriangleButtonWithPopover : TriangleButton, IHasPopover + { + public TriangleButtonWithPopover() + { + Width = 100; + Height = 30; + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + Text = @"open"; + Action = this.ShowPopover; + } + + public Popover GetPopover() => new OsuPopover + { + Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(10), + Children = new Drawable[] + { + new OsuSpriteText + { + Text = @"sample text" + }, + new OsuTextBox + { + Width = 150, + Height = 30 + } + } + } + }; + } + + private class ColourProvidingContainer : Container + { + [Cached] + private OverlayColourProvider provider { get; } + + public ColourProvidingContainer(OverlayColourScheme colourScheme) + { + provider = new OverlayColourProvider(colourScheme); + } + } + } +} diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs b/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs new file mode 100644 index 0000000000..33165ea8b5 --- /dev/null +++ b/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs @@ -0,0 +1,44 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using JetBrains.Annotations; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Overlays; +using osuTK; + +namespace osu.Game.Graphics.UserInterfaceV2 +{ + public class OsuPopover : Popover + { + private const float fade_duration = 250; + + public OsuPopover(bool withPadding = true) + { + Content.Padding = withPadding ? new MarginPadding(20) : new MarginPadding(); + Body.Masking = true; + Body.CornerRadius = 10; + Body.Margin = new MarginPadding(10); + Body.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Offset = new Vector2(0, 2), + Radius = 5, + Colour = Colour4.Black.Opacity(0.3f) + }; + } + + [BackgroundDependencyLoader(true)] + private void load([CanBeNull] OverlayColourProvider colourProvider, OsuColour osuColour) + { + Background.Colour = Arrow.Colour = colourProvider?.Background4 ?? osuColour.GreySeafoamDarker; + } + + protected override Drawable CreateArrow() => Empty(); + + protected override void PopIn() => this.FadeIn(fade_duration, Easing.OutQuint); + protected override void PopOut() => this.FadeOut(fade_duration, Easing.OutQuint); + } +} From b4961cd12e096e4068beb857844bc5bded30eb11 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 14 Jul 2021 03:18:13 +0300 Subject: [PATCH 0368/2442] Revert "Remove duplicated colour definitions" This reverts commit 9869986c59482f4c64b718e1bceb17d0ebc5d9aa. --- osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs | 3 +-- osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs | 3 +-- osu.Game/Graphics/OsuColour.cs | 4 ++++ osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs | 5 +++-- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs index b871f8bcc7..8a6cfaf688 100644 --- a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs @@ -3,7 +3,6 @@ using osu.Framework.Bindables; using osu.Game.Graphics; -using osu.Game.Overlays; using osuTK.Graphics; namespace osu.Game.Beatmaps.ControlPoints @@ -26,7 +25,7 @@ namespace osu.Game.Beatmaps.ControlPoints MaxValue = 10 }; - public override Color4 GetRepresentingColour(OsuColour colours) => OverlayColourProvider.Lime.Colour1; + public override Color4 GetRepresentingColour(OsuColour colours) => colours.Lime1; /// /// The speed multiplier at this control point. diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs index 22808671b0..ec20328fab 100644 --- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs @@ -4,7 +4,6 @@ using osu.Framework.Bindables; using osu.Game.Beatmaps.Timing; using osu.Game.Graphics; -using osu.Game.Overlays; using osuTK.Graphics; namespace osu.Game.Beatmaps.ControlPoints @@ -21,7 +20,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// private const double default_beat_length = 60000.0 / 60.0; - public override Color4 GetRepresentingColour(OsuColour colours) => OverlayColourProvider.Orange.Colour1; + public override Color4 GetRepresentingColour(OsuColour colours) => colours.Orange1; public static readonly TimingControlPoint DEFAULT = new TimingControlPoint { diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index 3533ab177c..a44c28eaa6 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -198,6 +198,10 @@ namespace osu.Game.Graphics public readonly Color4 GrayE = Color4Extensions.FromHex(@"eee"); public readonly Color4 GrayF = Color4Extensions.FromHex(@"fff"); + // in latest editor design logic, need to figure out where these sit... + public readonly Color4 Lime1 = Color4Extensions.FromHex(@"b2ff66"); + public readonly Color4 Orange1 = Color4Extensions.FromHex(@"ffd966"); + // Content Background public readonly Color4 B5 = Color4Extensions.FromHex(@"222a28"); diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs index 91d6460ea6..421806eea8 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers.Markdown; using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; namespace osu.Game.Overlays.Wiki.Markdown { @@ -65,7 +66,7 @@ namespace osu.Game.Overlays.Wiki.Markdown public string Text { get; set; } [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) + private void load(OverlayColourProvider colourProvider, OsuColour colour) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; @@ -80,7 +81,7 @@ namespace osu.Game.Overlays.Wiki.Markdown }, textFlow = parentFlowComponent.CreateTextFlow().With(t => { - t.Colour = OverlayColourProvider.Orange.Colour1; + t.Colour = colour.Orange1; t.Padding = new MarginPadding { Vertical = 10, From 80636be7678cbc001dc0b02e59aac84fbd80c573 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 14 Jul 2021 03:22:00 +0300 Subject: [PATCH 0369/2442] Link `Lime1` and `Orange1` to their `OverlayColourProvider`'s alternative --- osu.Game/Graphics/OsuColour.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index a44c28eaa6..c0bc8fdb76 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -3,6 +3,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Game.Beatmaps; +using osu.Game.Overlays; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osuTK.Graphics; @@ -198,8 +199,14 @@ namespace osu.Game.Graphics public readonly Color4 GrayE = Color4Extensions.FromHex(@"eee"); public readonly Color4 GrayF = Color4Extensions.FromHex(@"fff"); - // in latest editor design logic, need to figure out where these sit... + /// + /// Equivalent to 's . + /// public readonly Color4 Lime1 = Color4Extensions.FromHex(@"b2ff66"); + + /// + /// Equivalent to 's . + /// public readonly Color4 Orange1 = Color4Extensions.FromHex(@"ffd966"); // Content Background From ed296462911591152d0ec2f2fe558ab6edc62adb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Jul 2021 12:32:14 +0900 Subject: [PATCH 0370/2442] Remove `IApplicableToDifficulty.ReadFromDifficulty` This was added specifically for `ModDifficultyAdjust`, but turned out to be more of a headache than we expected. We have since removed usage and would hope that this is not required by any other mods. Opting for complete removal rather than obsoletion, as we discovered this was already broken in multiple cases, with fixes being quite logically complex. If you happen to be a ruleset developer relying on this, open an issue and we'll talk you through a better approach (or check what `ModDifficultyAdjust` is doing now for an example). --- osu.Game/OsuGame.cs | 18 ------------------ .../Rulesets/Mods/IApplicableToDifficulty.cs | 7 ------- 2 files changed, 25 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index c25b520892..8119df43ac 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -525,16 +525,11 @@ namespace osu.Game private void beatmapChanged(ValueChangedEvent beatmap) { beatmap.OldValue?.CancelAsyncLoad(); - - updateModDefaults(); - beatmap.NewValue?.BeginAsyncLoad(); } private void modsChanged(ValueChangedEvent> mods) { - updateModDefaults(); - // a lease may be taken on the mods bindable, at which point we can't really ensure valid mods. if (SelectedMods.Disabled) return; @@ -546,19 +541,6 @@ namespace osu.Game } } - private void updateModDefaults() - { - BeatmapDifficulty baseDifficulty = Beatmap.Value.BeatmapInfo.BaseDifficulty; - - if (baseDifficulty != null && SelectedMods.Value.Any(m => m is IApplicableToDifficulty)) - { - var adjustedDifficulty = baseDifficulty.Clone(); - - foreach (var mod in SelectedMods.Value.OfType()) - mod.ReadFromDifficulty(adjustedDifficulty); - } - } - #endregion private PerformFromMenuRunner performFromMainMenuTask; diff --git a/osu.Game/Rulesets/Mods/IApplicableToDifficulty.cs b/osu.Game/Rulesets/Mods/IApplicableToDifficulty.cs index 34198da722..42b520ab26 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToDifficulty.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToDifficulty.cs @@ -10,13 +10,6 @@ namespace osu.Game.Rulesets.Mods /// public interface IApplicableToDifficulty : IApplicableMod { - /// - /// Called when a beatmap is changed. Can be used to read default values. - /// Any changes made will not be preserved. - /// - /// The difficulty to read from. - void ReadFromDifficulty(BeatmapDifficulty difficulty); - /// /// Called post beatmap conversion. Can be used to apply changes to difficulty attributes. /// From 7f432665e52774a5bef21cf930ff2ac1ac0527f6 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 14 Jul 2021 14:38:38 +0900 Subject: [PATCH 0371/2442] Preserve Y position of hit objects in osu!catch --- .../Beatmaps/CatchBeatmapConverter.cs | 11 +++++---- .../Objects/CatchHitObject.cs | 24 ++++++++++++++++--- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 5 +--- .../Objects/Legacy/Catch/ConvertHit.cs | 9 +++++-- .../Legacy/Catch/ConvertHitObjectParser.cs | 4 ++-- .../Objects/Legacy/Catch/ConvertSlider.cs | 9 +++++-- 6 files changed, 45 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs index 34964fc4ae..7774a7da09 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs @@ -23,7 +23,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps protected override IEnumerable ConvertHitObject(HitObject obj, IBeatmap beatmap, CancellationToken cancellationToken) { - var positionData = obj as IHasXPosition; + var xPositionData = obj as IHasXPosition; + var yPositionData = obj as IHasYPosition; var comboData = obj as IHasCombo; switch (obj) @@ -36,10 +37,11 @@ namespace osu.Game.Rulesets.Catch.Beatmaps Path = curveData.Path, NodeSamples = curveData.NodeSamples, RepeatCount = curveData.RepeatCount, - X = positionData?.X ?? 0, + X = xPositionData?.X ?? 0, NewCombo = comboData?.NewCombo ?? false, ComboOffset = comboData?.ComboOffset ?? 0, - LegacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0 + LegacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0, + LegacyConvertedY = yPositionData?.Y ?? CatchHitObject.DEFAULT_LEGACY_CONVERT_Y }.Yield(); case IHasDuration endTime: @@ -59,7 +61,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps Samples = obj.Samples, NewCombo = comboData?.NewCombo ?? false, ComboOffset = comboData?.ComboOffset ?? 0, - X = positionData?.X ?? 0 + X = xPositionData?.X ?? 0, + LegacyConvertedY = yPositionData?.Y ?? CatchHitObject.DEFAULT_LEGACY_CONVERT_Y }.Yield(); } } diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index 0b8c0e28a7..f979e3e0ca 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -9,10 +9,11 @@ using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; +using osuTK; namespace osu.Game.Rulesets.Catch.Objects { - public abstract class CatchHitObject : HitObject, IHasXPosition, IHasComboInformation + public abstract class CatchHitObject : HitObject, IHasPosition, IHasComboInformation { public const float OBJECT_RADIUS = 64; @@ -31,8 +32,6 @@ namespace osu.Game.Rulesets.Catch.Objects set => OriginalXBindable.Value = value; } - float IHasXPosition.X => OriginalXBindable.Value; - public readonly Bindable XOffsetBindable = new Bindable(); /// @@ -131,5 +130,24 @@ namespace osu.Game.Rulesets.Catch.Objects } protected override HitWindows CreateHitWindows() => HitWindows.Empty; + + #region Hit object conversion + + // The half of the height of the osu! playfield. + public const float DEFAULT_LEGACY_CONVERT_Y = 192; + + /// + /// The Y position of the hit object is not used in the normal osu!catch gameplay. + /// It is preserved to maximize the backward compatibility with the legacy editor, in which the mappers use the Y position to organize the patterns. + /// + public float LegacyConvertedY { get; set; } = DEFAULT_LEGACY_CONVERT_Y; + + float IHasXPosition.X => OriginalX; + + float IHasYPosition.Y => LegacyConvertedY; + + Vector2 IHasPosition.Position => new Vector2(OriginalX, LegacyConvertedY); + + #endregion } } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index acbf57d25f..f14f6ec10c 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -251,11 +251,8 @@ namespace osu.Game.Beatmaps.Formats switch (beatmap.BeatmapInfo.RulesetID) { case 0: - position = ((IHasPosition)hitObject).Position; - break; - case 2: - position.X = ((IHasXPosition)hitObject).X; + position = ((IHasPosition)hitObject).Position; break; case 3: diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs index 19722fb796..12b4812824 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs @@ -2,15 +2,20 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Objects.Types; +using osuTK; namespace osu.Game.Rulesets.Objects.Legacy.Catch { /// /// Legacy osu!catch Hit-type, used for parsing Beatmaps. /// - internal sealed class ConvertHit : ConvertHitObject, IHasCombo, IHasXPosition + internal sealed class ConvertHit : ConvertHitObject, IHasPosition, IHasCombo { - public float X { get; set; } + public float X => Position.X; + + public float Y => Position.Y; + + public Vector2 Position { get; set; } public bool NewCombo { get; set; } diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs index c10c8dc30f..c29179f749 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch return new ConvertHit { - X = position.X, + Position = position, NewCombo = newCombo, ComboOffset = comboOffset }; @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch return new ConvertSlider { - X = position.X, + Position = position, NewCombo = FirstObject || newCombo, ComboOffset = comboOffset, Path = new SliderPath(controlPoints, length), diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs index 56790629b4..fb1afed3b4 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs @@ -2,15 +2,20 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Objects.Types; +using osuTK; namespace osu.Game.Rulesets.Objects.Legacy.Catch { /// /// Legacy osu!catch Slider-type, used for parsing Beatmaps. /// - internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasXPosition, IHasCombo + internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasPosition, IHasCombo { - public float X { get; set; } + public float X => Position.X; + + public float Y => Position.Y; + + public Vector2 Position { get; set; } public bool NewCombo { get; set; } From 0e89bafd178f5d180357ec0ae8102f8462e226b2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 14 Jul 2021 16:48:53 +0900 Subject: [PATCH 0372/2442] Add special category pill + secondary background --- .../Multiplayer/TestSceneDrawableRoom.cs | 8 +- .../Lounge/Components/DrawableRoom.cs | 266 ++++++++++-------- .../Lounge/Components/PillContainer.cs | 7 +- .../Components/RoomSpecialCategoryPill.cs | 49 ++++ .../Screens/OnlinePlay/OnlinePlayComposite.cs | 3 + 5 files changed, 209 insertions(+), 124 deletions(-) create mode 100644 osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index 0970085c25..ee84a6b6cc 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -47,10 +47,16 @@ namespace osu.Game.Tests.Visual.Multiplayer }), createDrawableRoom(new Room { - Name = { Value = "Room 4" }, + Name = { Value = "Room 4 (realtime)" }, Status = { Value = new RoomStatusOpen() }, Category = { Value = RoomCategory.Realtime }, }), + createDrawableRoom(new Room + { + Name = { Value = "Room 4 (spotlight)" }, + Status = { Value = new RoomStatusOpen() }, + Category = { Value = RoomCategory.Spotlight }, + }), } }; } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index f7f846ecfb..d36f0275df 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -105,7 +105,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } + private readonly Bindable roomCategory = new Bindable(); + private RecentParticipantsList recentParticipantsList; + private RoomSpecialCategoryPill specialCategoryPill; + private Drawable spotlightGlow; public bool FilteringActive { get; set; } @@ -126,156 +130,169 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { new Container { - Name = @"Room content", RelativeSizeAxes = Axes.Both, - Child = new Container + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Masking = true, - CornerRadius = corner_radius, - EdgeEffect = new EdgeEffectParameters + new OnlinePlayBackgroundSprite(BeatmapSetCoverType.List) { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(40), - Radius = 5, + RelativeSizeAxes = Axes.Both }, - Children = new Drawable[] + new Container { - // This resolves 1px gaps due to applying the (parenting) corner radius and masking across multiple filling background sprites. - new BufferedContainer + Name = @"Room content", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = 20, Vertical = -0.5f }, + Child = new Container { RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = corner_radius, Children = new Drawable[] { - new Box + // This resolves internal 1px gaps due to applying the (parenting) corner radius and masking across multiple filling background sprites. + new BufferedContainer { RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"#27302E"), + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex(@"#27302E"), + }, + new OnlinePlayBackgroundSprite(BeatmapSetCoverType.List) + { + RelativeSizeAxes = Axes.Both + }, + new GridContainer + { + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.Relative, 0.2f) + }, + Content = new[] + { + new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex(@"#27302E"), + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientHorizontal(Color4Extensions.FromHex(@"#27302E"), Color4Extensions.FromHex(@"#27302E").Opacity(0.3f)) + }, + } + } + }, + spotlightGlow = new Box + { + RelativeSizeAxes = Axes.Y, + Width = 50, + Colour = ColourInfo.GradientHorizontal(colours.Pink.Opacity(0.5f), colours.Pink.Opacity(0)) + } + }, }, new Container { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, + Name = @"Left details", RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fill, - Child = new OnlinePlayBackgroundSprite(BeatmapSetCoverType.List) { RelativeSizeAxes = Axes.Both } - }, - new GridContainer - { - RelativeSizeAxes = Axes.Both, - ColumnDimensions = new[] + Padding = new MarginPadding { - new Dimension(GridSizeMode.Relative, 0.2f) + Left = 20, + Vertical = 5 }, - Content = new[] + Children = new Drawable[] { - new Drawable[] + new FillFlowContainer { - new Box + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(4), + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"#27302E"), - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal(Color4Extensions.FromHex(@"#27302E"), Color4Extensions.FromHex(@"#27302E").Opacity(0.3f)) - }, - } - } - }, - } - }, - new Container - { - Name = @"Left details", - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding - { - Left = 20, - Vertical = 5 - }, - Children = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(4), - Children = new Drawable[] - { - new RoomStatusPill - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft + new RoomStatusPill + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft + }, + specialCategoryPill = new RoomSpecialCategoryPill + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft + }, + new EndDateInfo + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft + }, + } }, - new EndDateInfo - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft - }, - } - }, - new FillFlowContainer - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new RoomNameText(), - new RoomHostText() - } - }, - new FillFlowContainer - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(4), - Children = new Drawable[] - { - new PlaylistCountPill + new FillFlowContainer { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new RoomNameText(), + new RoomHostText() + } }, - new StarRatingRangeDisplay + new FillFlowContainer { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Scale = new Vector2(0.85f) + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(4), + Children = new Drawable[] + { + new PlaylistCountPill + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + new StarRatingRangeDisplay + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Scale = new Vector2(0.85f) + } + } } } - } - } - }, - new FillFlowContainer - { - Name = "Right content", - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - Padding = new MarginPadding - { - Right = 10, - Vertical = 5 - }, - Children = new Drawable[] - { - recentParticipantsList = new RecentParticipantsList + }, + new FillFlowContainer { + Name = "Right content", Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, - NumberOfAvatars = NumberOfAvatars + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Padding = new MarginPadding + { + Right = 10, + Vertical = 5 + }, + Children = new Drawable[] + { + recentParticipantsList = new RecentParticipantsList + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + NumberOfAvatars = NumberOfAvatars + } + } } - } - } + }, + }, }, - }, + } }, new StatusColouredContainer(transition_duration) { @@ -315,6 +332,21 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components this.FadeInFromZero(transition_duration); else Alpha = 0; + + roomCategory.BindTo(Room.Category); + roomCategory.BindValueChanged(c => + { + if (c.NewValue == RoomCategory.Spotlight) + { + specialCategoryPill.Show(); + spotlightGlow.Show(); + } + else + { + specialCategoryPill.Hide(); + spotlightGlow.Hide(); + } + }, true); } protected override bool ShouldBeConsideredForInput(Drawable child) => state == SelectionState.Selected; diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/PillContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/PillContainer.cs index 15a532eaf3..ca153f7e9a 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/PillContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/PillContainer.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 osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -16,7 +15,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { private const float padding = 8; - public Drawable Background { get; private set; } + public readonly Drawable Background; protected override Container Content { get; } = new Container { @@ -29,11 +28,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { AutoSizeAxes = Axes.X; Height = 16; - } - [BackgroundDependencyLoader] - private void load() - { InternalChild = new CircularContainer { Anchor = Anchor.Centre, diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs new file mode 100644 index 0000000000..6cdbeb2af4 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs @@ -0,0 +1,49 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osuTK.Graphics; + +namespace osu.Game.Screens.OnlinePlay.Lounge.Components +{ + public class RoomSpecialCategoryPill : OnlinePlayComposite + { + private SpriteText text; + + public RoomSpecialCategoryPill() + { + AutoSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + InternalChild = new PillContainer + { + Background = + { + Colour = colours.Pink, + Alpha = 1 + }, + Child = text = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 12), + Colour = Color4.Black + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Category.BindValueChanged(c => text.Text = c.NewValue.ToString(), true); + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index eb0b23f13f..ffaeb8fc97 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -35,6 +35,9 @@ namespace osu.Game.Screens.OnlinePlay [Resolved(typeof(Room))] protected BindableList Playlist { get; private set; } + [Resolved(typeof(Room))] + protected Bindable Category { get; private set; } + [Resolved(typeof(Room))] protected BindableList RecentParticipants { get; private set; } From e0c61c24b1529654bf9a891384b8ed3660d84940 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 14 Jul 2021 16:51:20 +0900 Subject: [PATCH 0373/2442] Remove spotlights glow --- .../Lounge/Components/DrawableRoom.cs | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index d36f0275df..63dffe432b 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Game.Beatmaps; @@ -109,7 +108,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private RecentParticipantsList recentParticipantsList; private RoomSpecialCategoryPill specialCategoryPill; - private Drawable spotlightGlow; public bool FilteringActive { get; set; } @@ -153,7 +151,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components new BufferedContainer { RelativeSizeAxes = Axes.Both, - Children = new[] + Children = new Drawable[] { new Box { @@ -188,12 +186,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } }, - spotlightGlow = new Box - { - RelativeSizeAxes = Axes.Y, - Width = 50, - Colour = ColourInfo.GradientHorizontal(colours.Pink.Opacity(0.5f), colours.Pink.Opacity(0)) - } }, }, new Container @@ -336,16 +328,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components roomCategory.BindTo(Room.Category); roomCategory.BindValueChanged(c => { + // Todo: Tournament category... if (c.NewValue == RoomCategory.Spotlight) - { specialCategoryPill.Show(); - spotlightGlow.Show(); - } else - { specialCategoryPill.Hide(); - spotlightGlow.Hide(); - } }, true); } From da3b40a4ddd8081e4d626dac0f44d2ce417e5548 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 14 Jul 2021 16:56:39 +0900 Subject: [PATCH 0374/2442] Add default background to panel, reduce nesting --- .../Lounge/Components/DrawableRoom.cs | 248 +++++++++--------- 1 file changed, 127 insertions(+), 121 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 63dffe432b..1e8ea86eb7 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -126,165 +126,171 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { Children = new Drawable[] { - new Container + // This resolves internal 1px gaps due to applying the (parenting) corner radius and masking across multiple filling background sprites. + new BufferedContainer { RelativeSizeAxes = Axes.Both, Children = new Drawable[] { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex(@"#27302E"), + }, new OnlinePlayBackgroundSprite(BeatmapSetCoverType.List) { RelativeSizeAxes = Axes.Both }, - new Container + } + }, + new Container + { + Name = @"Room content", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = 20, Vertical = -0.5f }, + Child = new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = corner_radius, + Children = new Drawable[] { - Name = @"Room content", - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = 20, Vertical = -0.5f }, - Child = new Container + // This resolves internal 1px gaps due to applying the (parenting) corner radius and masking across multiple filling background sprites. + new BufferedContainer { RelativeSizeAxes = Axes.Both, - Masking = true, - CornerRadius = corner_radius, Children = new Drawable[] { - // This resolves internal 1px gaps due to applying the (parenting) corner radius and masking across multiple filling background sprites. - new BufferedContainer + new Box { RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"#27302E"), - }, - new OnlinePlayBackgroundSprite(BeatmapSetCoverType.List) - { - RelativeSizeAxes = Axes.Both - }, - new GridContainer - { - RelativeSizeAxes = Axes.Both, - ColumnDimensions = new[] - { - new Dimension(GridSizeMode.Relative, 0.2f) - }, - Content = new[] - { - new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"#27302E"), - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal(Color4Extensions.FromHex(@"#27302E"), Color4Extensions.FromHex(@"#27302E").Opacity(0.3f)) - }, - } - } - }, - }, + Colour = Color4Extensions.FromHex(@"#27302E"), }, - new Container + new OnlinePlayBackgroundSprite(BeatmapSetCoverType.List) + { + RelativeSizeAxes = Axes.Both + }, + new GridContainer { - Name = @"Left details", RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding + ColumnDimensions = new[] { - Left = 20, - Vertical = 5 + new Dimension(GridSizeMode.Relative, 0.2f) }, + Content = new[] + { + new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex(@"#27302E"), + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientHorizontal(Color4Extensions.FromHex(@"#27302E"), Color4Extensions.FromHex(@"#27302E").Opacity(0.3f)) + }, + } + } + }, + }, + }, + new Container + { + Name = @"Left details", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding + { + Left = 20, + Vertical = 5 + }, + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(4), Children = new Drawable[] { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(4), - Children = new Drawable[] - { - new RoomStatusPill - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft - }, - specialCategoryPill = new RoomSpecialCategoryPill - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft - }, - new EndDateInfo - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft - }, - } - }, - new FillFlowContainer + new RoomStatusPill { Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new RoomNameText(), - new RoomHostText() - } + Origin = Anchor.CentreLeft }, - new FillFlowContainer + specialCategoryPill = new RoomSpecialCategoryPill { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(4), - Children = new Drawable[] - { - new PlaylistCountPill - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - }, - new StarRatingRangeDisplay - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Scale = new Vector2(0.85f) - } - } - } + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft + }, + new EndDateInfo + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft + }, } }, new FillFlowContainer { - Name = "Right content", - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - Padding = new MarginPadding - { - Right = 10, - Vertical = 5 - }, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, Children = new Drawable[] { - recentParticipantsList = new RecentParticipantsList + new RoomNameText(), + new RoomHostText() + } + }, + new FillFlowContainer + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(4), + Children = new Drawable[] + { + new PlaylistCountPill { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - NumberOfAvatars = NumberOfAvatars + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + new StarRatingRangeDisplay + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Scale = new Vector2(0.85f) } } } - }, + } }, + new FillFlowContainer + { + Name = "Right content", + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Padding = new MarginPadding + { + Right = 10, + Vertical = 5 + }, + Children = new Drawable[] + { + recentParticipantsList = new RecentParticipantsList + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + NumberOfAvatars = NumberOfAvatars + } + } + } }, - } + }, }, new StatusColouredContainer(transition_duration) { From f6b81b76e88cc4c6b4307719150ba42cc93fbecc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 14 Jul 2021 17:46:32 +0900 Subject: [PATCH 0375/2442] Add shadow --- .../OnlinePlay/Lounge/Components/DrawableRoom.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 1e8ea86eb7..441b33abf0 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Game.Beatmaps; @@ -117,8 +118,15 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components RelativeSizeAxes = Axes.X; Height = height; - CornerRadius = corner_radius + SELECTION_BORDER_WIDTH / 2; + Masking = true; + CornerRadius = corner_radius + SELECTION_BORDER_WIDTH / 2; + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(40), + Radius = 5, + }; } [BackgroundDependencyLoader] From 0bfaf11d515b45c12ca6c1196ffd2a46bca71b4a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 14 Jul 2021 17:46:45 +0900 Subject: [PATCH 0376/2442] Remove/fix paddings in lounge --- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 54 ++++++++++++------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 9004fe8dcc..4930c432d8 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -52,32 +52,49 @@ namespace osu.Game.Screens.OnlinePlay.Lounge RoomsContainer roomsContainer; OsuScrollContainer scrollContainer; - InternalChildren = new Drawable[] + InternalChild = content = new Container { - content = new Container + RelativeSizeAxes = Axes.Both, + Child = new GridContainer { RelativeSizeAxes = Axes.Both, - Child = new Container + RowDimensions = new[] { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.Absolute, 10) + }, + Content = new[] + { + new Drawable[] { - scrollContainer = new OsuScrollContainer + filter = CreateFilterControl().With(d => + { + d.RelativeSizeAxes = Axes.X; + d.Height = 80; + d.Depth = -1; + }), + }, + null, + new Drawable[] + { + new Container { RelativeSizeAxes = Axes.Both, - ScrollbarOverlapsContent = false, - Padding = new MarginPadding(10), - Child = roomsContainer = new RoomsContainer { JoinRequested = joinRequested } + Children = new Drawable[] + { + scrollContainer = new OsuScrollContainer + { + RelativeSizeAxes = Axes.Both, + ScrollbarOverlapsContent = false, + Padding = new MarginPadding(10), + Child = roomsContainer = new RoomsContainer { JoinRequested = joinRequested } + }, + loadingLayer = new LoadingLayer(true), + } }, - loadingLayer = new LoadingLayer(true), } - }, + } }, - filter = CreateFilterControl().With(d => - { - d.RelativeSizeAxes = Axes.X; - d.Height = 80; - }) }; // scroll selected room into view on selection. @@ -109,9 +126,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge content.Padding = new MarginPadding { - Top = filter.DrawHeight, - Left = WaveOverlayContainer.WIDTH_PADDING - DrawableRoom.SELECTION_BORDER_WIDTH + HORIZONTAL_OVERFLOW_PADDING, - Right = WaveOverlayContainer.WIDTH_PADDING + HORIZONTAL_OVERFLOW_PADDING, + Left = WaveOverlayContainer.WIDTH_PADDING, + Right = WaveOverlayContainer.WIDTH_PADDING, }; } From c64230315f083a9b4e5c9c2bfaca668846d4991c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 14 Jul 2021 18:10:59 +0900 Subject: [PATCH 0377/2442] Adjust layouts --- .../Lounge/Components/FilterControl.cs | 18 +++++-------- .../Components/PlaylistsFilterControl.cs | 2 +- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 27 ++++++------------- 3 files changed, 16 insertions(+), 31 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterControl.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterControl.cs index 1827efb4a2..36fba68a0c 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterControl.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterControl.cs @@ -16,9 +16,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { public abstract class FilterControl : CompositeDrawable { - protected const float VERTICAL_PADDING = 10; - protected const float HORIZONTAL_PADDING = 20; - protected readonly FillFlowContainer Filters; [Resolved(CanBeNull = true)] @@ -32,14 +29,14 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components protected FilterControl() { - InternalChild = new Container + RelativeSizeAxes = Axes.X; + Height = 70; + + InternalChild = new FillFlowContainer { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding - { - Top = VERTICAL_PADDING, - Horizontal = HORIZONTAL_PADDING - }, + Direction = FillDirection.Vertical, + Spacing = new Vector2(10), Children = new Drawable[] { search = new FilterSearchTextBox @@ -54,10 +51,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components RelativeSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, Spacing = new Vector2(10), - Padding = new MarginPadding { Vertical = 30 }, Child = statusDropdown = new SlimEnumDropdown { - Anchor = Anchor.BottomRight, + Anchor = Anchor.TopRight, Origin = Anchor.TopRight, RelativeSizeAxes = Axes.None, Width = 160, diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistsFilterControl.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistsFilterControl.cs index e6309a8e06..bbf34d3893 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistsFilterControl.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistsFilterControl.cs @@ -15,7 +15,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { Filters.Add(categoryDropdown = new SlimEnumDropdown { - Anchor = Anchor.BottomRight, + Anchor = Anchor.TopRight, Origin = Anchor.TopRight, RelativeSizeAxes = Axes.None, Width = 160, diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 4930c432d8..492473e550 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -55,24 +55,25 @@ namespace osu.Game.Screens.OnlinePlay.Lounge InternalChild = content = new Container { RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding + { + Top = 20, + Left = WaveOverlayContainer.WIDTH_PADDING, + Right = WaveOverlayContainer.WIDTH_PADDING, + }, Child = new GridContainer { RelativeSizeAxes = Axes.Both, RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.Absolute, 10) + new Dimension(GridSizeMode.Absolute, 20) }, Content = new[] { new Drawable[] { - filter = CreateFilterControl().With(d => - { - d.RelativeSizeAxes = Axes.X; - d.Height = 80; - d.Depth = -1; - }), + filter = CreateFilterControl().With(d => d.Depth = -1), }, null, new Drawable[] @@ -86,7 +87,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { RelativeSizeAxes = Axes.Both, ScrollbarOverlapsContent = false, - Padding = new MarginPadding(10), Child = roomsContainer = new RoomsContainer { JoinRequested = joinRequested } }, loadingLayer = new LoadingLayer(true), @@ -120,17 +120,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge } } - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - content.Padding = new MarginPadding - { - Left = WaveOverlayContainer.WIDTH_PADDING, - Right = WaveOverlayContainer.WIDTH_PADDING, - }; - } - protected override void OnFocus(FocusEvent e) { filter.TakeFocus(); From 3e6b9bd48d595d3753c5f52c35f23802eb7bfbf1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 14 Jul 2021 18:24:30 +0900 Subject: [PATCH 0378/2442] Add filter background --- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 70 +++++++++++-------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 492473e550..068bc34a6c 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -8,6 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.Screens; using osu.Game.Graphics.Containers; @@ -17,6 +18,7 @@ using osu.Game.Overlays; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Users; +using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay.Lounge { @@ -52,49 +54,59 @@ namespace osu.Game.Screens.OnlinePlay.Lounge RoomsContainer roomsContainer; OsuScrollContainer scrollContainer; - InternalChild = content = new Container + InternalChildren = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding + new Box { - Top = 20, - Left = WaveOverlayContainer.WIDTH_PADDING, - Right = WaveOverlayContainer.WIDTH_PADDING, + RelativeSizeAxes = Axes.X, + Height = 100, + Colour = Color4.Black, + Alpha = 0.5f, }, - Child = new GridContainer + content = new Container { RelativeSizeAxes = Axes.Both, - RowDimensions = new[] + Padding = new MarginPadding { - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.Absolute, 20) + Top = 20, + Left = WaveOverlayContainer.WIDTH_PADDING, + Right = WaveOverlayContainer.WIDTH_PADDING, }, - Content = new[] + Child = new GridContainer { - new Drawable[] + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] { - filter = CreateFilterControl().With(d => d.Depth = -1), + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.Absolute, 20) }, - null, - new Drawable[] + Content = new[] { - new Container + new Drawable[] { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - scrollContainer = new OsuScrollContainer - { - RelativeSizeAxes = Axes.Both, - ScrollbarOverlapsContent = false, - Child = roomsContainer = new RoomsContainer { JoinRequested = joinRequested } - }, - loadingLayer = new LoadingLayer(true), - } + filter = CreateFilterControl().With(d => d.Depth = -1), }, + null, + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + scrollContainer = new OsuScrollContainer + { + RelativeSizeAxes = Axes.Both, + ScrollbarOverlapsContent = false, + Child = roomsContainer = new RoomsContainer { JoinRequested = joinRequested } + }, + loadingLayer = new LoadingLayer(true), + } + }, + } } - } - }, + }, + } }; // scroll selected room into view on selection. From dfe7cc40a9a39d113e9f1362124e54871dccc3f7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 14 Jul 2021 18:55:01 +0900 Subject: [PATCH 0379/2442] Move create room button into the lounge --- .../Multiplayer/TestSceneMultiplayer.cs | 6 +-- .../Navigation/TestSceneScreenNavigation.cs | 4 +- .../Lounge/Components/DrawableRoom.cs | 4 +- .../Lounge/Components/FilterControl.cs | 4 +- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 41 +++++++++++++++++-- .../CreateMultiplayerMatchButton.cs | 1 + .../OnlinePlay/Multiplayer/Multiplayer.cs | 11 ----- .../Multiplayer/MultiplayerLoungeSubScreen.cs | 19 +++++++-- .../Screens/OnlinePlay/OnlinePlayScreen.cs | 30 -------------- .../Playlists/CreatePlaylistsRoomButton.cs | 1 + .../Screens/OnlinePlay/Playlists/Playlists.cs | 9 ---- .../Playlists/PlaylistsLoungeSubScreen.cs | 13 ++++++ 12 files changed, 79 insertions(+), 64 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 7673efb78f..2808127bb8 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -249,10 +249,8 @@ namespace osu.Game.Tests.Visual.Multiplayer private void createRoom(Func room) { - AddStep("open room", () => - { - multiplayerScreen.OpenNewRoom(room()); - }); + AddUntilStep("wait for lounge", () => multiplayerScreen.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); + AddStep("open room", () => multiplayerScreen.ChildrenOfType().Single().OpenNewRoom(room())); AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); AddWaitStep("wait for transition", 2); diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 52401d32e5..72d01ddd2a 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -16,6 +16,7 @@ using osu.Game.Overlays.Toolbar; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.OnlinePlay.Components; +using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; using osu.Game.Screens.Select; @@ -316,7 +317,8 @@ namespace osu.Game.Tests.Visual.Navigation PushAndConfirm(() => multiplayer = new TestMultiplayer()); - AddStep("open room", () => multiplayer.OpenNewRoom()); + AddUntilStep("wait for lounge", () => multiplayer.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); + AddStep("open room", () => multiplayer.ChildrenOfType().Single().OpenNewRoom()); AddStep("press back button", () => Game.ChildrenOfType().First().Action()); AddWaitStep("wait two frames", 2); } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 441b33abf0..9bfec72942 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -39,7 +39,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private Drawable selectionBox; [Resolved(canBeNull: true)] - private OnlinePlayScreen parentScreen { get; set; } + private LoungeSubScreen loungeScreen { get; set; } [Resolved] private BeatmapManager beatmaps { get; set; } @@ -408,7 +408,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { new OsuMenuItem("Create copy", MenuItemType.Standard, () => { - parentScreen?.OpenNewRoom(Room.CreateCopy()); + loungeScreen?.OpenNewRoom(Room.CreateCopy()); }) }; } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterControl.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterControl.cs index 36fba68a0c..e2f02fca68 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterControl.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterControl.cs @@ -48,7 +48,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components }, Filters = new FillFlowContainer { - RelativeSizeAxes = Axes.Both, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, Spacing = new Vector2(10), Child = statusDropdown = new SlimEnumDropdown diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 068bc34a6c..115dddafec 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -18,6 +18,7 @@ using osu.Game.Overlays; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Users; +using osuTK; using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay.Lounge @@ -29,11 +30,17 @@ namespace osu.Game.Screens.OnlinePlay.Lounge protected override UserActivity InitialActivity => new UserActivity.SearchingForLobby(); + protected Container Buttons { get; } = new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + AutoSizeAxes = Axes.Both + }; + private readonly IBindable initialRoomsReceived = new Bindable(); private readonly IBindable operationInProgress = new Bindable(); private FilterControl filter; - private Container content; private LoadingLayer loadingLayer; [Resolved] @@ -63,7 +70,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge Colour = Color4.Black, Alpha = 0.5f, }, - content = new Container + new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding @@ -84,7 +91,21 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { new Drawable[] { - filter = CreateFilterControl().With(d => d.Depth = -1), + new Container + { + RelativeSizeAxes = Axes.X, + Height = 70, + Depth = -1, + Children = new Drawable[] + { + filter = CreateFilterControl(), + Buttons.WithChild(CreateNewRoomButton().With(d => + { + d.Size = new Vector2(150, 25); + d.Action = () => OpenNewRoom(); + })) + } + } }, null, new Drawable[] @@ -216,6 +237,20 @@ namespace osu.Game.Screens.OnlinePlay.Lounge protected abstract FilterControl CreateFilterControl(); + /// + /// Creates and opens the newly-created room. + /// + /// An optional template to use when creating the room. + public void OpenNewRoom(Room room = null) => Open(room ?? CreateNewRoom()); + + protected abstract OsuButton CreateNewRoomButton(); + + /// + /// Creates a new room. + /// + /// The created . + protected abstract Room CreateNewRoom(); + protected abstract RoomSubScreen CreateRoomSubScreen(Room room); } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs index cc51b5b691..edf846f0f8 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs @@ -23,6 +23,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private void load() { Triangles.TriangleScale = 1.5f; + SpriteText.Font = SpriteText.Font.With(size: 14); Text = "Create room"; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs index dbac826954..45928505bb 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs @@ -4,9 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Logging; using osu.Framework.Screens; -using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; -using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Lounge; @@ -54,19 +52,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer Logger.Log($"Polling adjusted (listing: {multiplayerRoomManager.TimeBetweenListingPolls.Value}, selection: {multiplayerRoomManager.TimeBetweenSelectionPolls.Value})"); } - protected override Room CreateNewRoom() => - new Room - { - Name = { Value = $"{API.LocalUser}'s awesome room" }, - Category = { Value = RoomCategory.Realtime } - }; - protected override string ScreenTitle => "Multiplayer"; protected override RoomManager CreateRoomManager() => new MultiplayerRoomManager(); protected override LoungeSubScreen CreateLounge() => new MultiplayerLoungeSubScreen(); - - protected override OsuButton CreateNewMultiplayerGameButton() => new CreateMultiplayerMatchButton(); } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs index 4d20652465..dd5290f127 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs @@ -3,6 +3,8 @@ using osu.Framework.Allocation; using osu.Framework.Logging; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Lounge; @@ -13,13 +15,24 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { public class MultiplayerLoungeSubScreen : LoungeSubScreen { - protected override FilterControl CreateFilterControl() => new MultiplayerFilterControl(); - - protected override RoomSubScreen CreateRoomSubScreen(Room room) => new MultiplayerMatchSubScreen(room); + [Resolved] + private IAPIProvider api { get; set; } [Resolved] private MultiplayerClient client { get; set; } + protected override FilterControl CreateFilterControl() => new MultiplayerFilterControl(); + + protected override OsuButton CreateNewRoomButton() => new CreateMultiplayerMatchButton(); + + protected override Room CreateNewRoom() => new Room + { + Name = { Value = $"{api.LocalUser}'s awesome room" }, + Category = { Value = RoomCategory.Realtime } + }; + + protected override RoomSubScreen CreateRoomSubScreen(Room room) => new MultiplayerMatchSubScreen(room); + public override void Open(Room room) { if (!client.IsConnected.Value) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index ceee002c6e..3a229ecd4a 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -12,7 +12,6 @@ using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.UserInterface; using osu.Game.Input; using osu.Game.Online.API; using osu.Game.Online.Rooms; @@ -23,7 +22,6 @@ using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Users; -using osuTK; namespace osu.Game.Screens.OnlinePlay { @@ -38,7 +36,6 @@ namespace osu.Game.Screens.OnlinePlay private readonly MultiplayerWaveContainer waves; - private readonly OsuButton createButton; private readonly LoungeSubScreen loungeSubScreen; private readonly ScreenStack screenStack; @@ -132,18 +129,6 @@ namespace osu.Game.Screens.OnlinePlay } }, new Header(ScreenTitle, screenStack), - createButton = CreateNewMultiplayerGameButton().With(button => - { - button.Anchor = Anchor.TopRight; - button.Origin = Anchor.TopRight; - button.Size = new Vector2(150, Header.HEIGHT - 20); - button.Margin = new MarginPadding - { - Top = 10, - Right = 10 + HORIZONTAL_OVERFLOW_PADDING, - }; - button.Action = () => OpenNewRoom(); - }), RoomManager = CreateRoomManager(), ongoingOperationTracker = new OngoingOperationTracker() } @@ -278,18 +263,6 @@ namespace osu.Game.Screens.OnlinePlay logo.Delay(WaveContainer.DISAPPEAR_DURATION / 2).FadeOut(); } - /// - /// Creates and opens the newly-created room. - /// - /// An optional template to use when creating the room. - public void OpenNewRoom(Room room = null) => loungeSubScreen.Open(room ?? CreateNewRoom()); - - /// - /// Creates a new room. - /// - /// The created . - protected abstract Room CreateNewRoom(); - private void screenPushed(IScreen lastScreen, IScreen newScreen) { subScreenChanged(lastScreen, newScreen); @@ -325,7 +298,6 @@ namespace osu.Game.Screens.OnlinePlay ((IBindable)Activity).BindTo(newOsuScreen.Activity); UpdatePollingRate(isIdle.Value); - createButton.FadeTo(newScreen is LoungeSubScreen ? 1 : 0, 200); } protected IScreen CurrentSubScreen => screenStack.CurrentScreen; @@ -336,8 +308,6 @@ namespace osu.Game.Screens.OnlinePlay protected abstract LoungeSubScreen CreateLounge(); - protected abstract OsuButton CreateNewMultiplayerGameButton(); - private class MultiplayerWaveContainer : WaveContainer { protected override bool StartHidden => true; diff --git a/osu.Game/Screens/OnlinePlay/Playlists/CreatePlaylistsRoomButton.cs b/osu.Game/Screens/OnlinePlay/Playlists/CreatePlaylistsRoomButton.cs index fcb773f8be..a2c649f10b 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/CreatePlaylistsRoomButton.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/CreatePlaylistsRoomButton.cs @@ -12,6 +12,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private void load() { Triangles.TriangleScale = 1.5f; + SpriteText.Font = SpriteText.Font.With(size: 14); Text = "Create playlist"; } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs b/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs index 5b132c97fd..6a78e24ba1 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs @@ -3,8 +3,6 @@ using osu.Framework.Logging; using osu.Framework.Screens; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.OnlinePlay.Match; @@ -46,17 +44,10 @@ namespace osu.Game.Screens.OnlinePlay.Playlists Logger.Log($"Polling adjusted (listing: {playlistsManager.TimeBetweenListingPolls.Value}, selection: {playlistsManager.TimeBetweenSelectionPolls.Value})"); } - protected override Room CreateNewRoom() - { - return new Room { Name = { Value = $"{API.LocalUser}'s awesome playlist" } }; - } - protected override string ScreenTitle => "Playlists"; protected override RoomManager CreateRoomManager() => new PlaylistsRoomManager(); protected override LoungeSubScreen CreateLounge() => new PlaylistsLoungeSubScreen(); - - protected override OsuButton CreateNewMultiplayerGameButton() => new CreatePlaylistsRoomButton(); } } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs index bfbff4240c..21113af828 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs @@ -1,6 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.OnlinePlay.Lounge.Components; @@ -10,8 +13,18 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { public class PlaylistsLoungeSubScreen : LoungeSubScreen { + [Resolved] + private IAPIProvider api { get; set; } + protected override FilterControl CreateFilterControl() => new PlaylistsFilterControl(); + protected override OsuButton CreateNewRoomButton() => new CreatePlaylistsRoomButton(); + + protected override Room CreateNewRoom() + { + return new Room { Name = { Value = $"{api.LocalUser}'s awesome playlist" } }; + } + protected override RoomSubScreen CreateRoomSubScreen(Room room) => new PlaylistsRoomSubScreen(room); } } From fdfd82aec42256ca1f44ab3b576c5c410640316d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Jul 2021 23:23:44 +0900 Subject: [PATCH 0380/2442] Add elastic scale on appear --- osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs b/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs index 33165ea8b5..fff18e615f 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs @@ -14,10 +14,12 @@ namespace osu.Game.Graphics.UserInterfaceV2 public class OsuPopover : Popover { private const float fade_duration = 250; + private const double scale_duration = 500; public OsuPopover(bool withPadding = true) { Content.Padding = withPadding ? new MarginPadding(20) : new MarginPadding(); + Body.Masking = true; Body.CornerRadius = 10; Body.Margin = new MarginPadding(10); @@ -38,7 +40,16 @@ namespace osu.Game.Graphics.UserInterfaceV2 protected override Drawable CreateArrow() => Empty(); - protected override void PopIn() => this.FadeIn(fade_duration, Easing.OutQuint); - protected override void PopOut() => this.FadeOut(fade_duration, Easing.OutQuint); + protected override void PopIn() + { + this.ScaleTo(1, scale_duration, Easing.OutElasticHalf); + this.FadeIn(fade_duration, Easing.OutQuint); + } + + protected override void PopOut() + { + this.ScaleTo(0.7f, scale_duration, Easing.OutQuint); + this.FadeOut(fade_duration, Easing.OutQuint); + } } } From c6116676ebb9e090a82717537e09027399009a9c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Jul 2021 23:23:48 +0900 Subject: [PATCH 0381/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index cd57d7478e..171a0862a1 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index eb7a0141c7..b2f1d6507f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 2e5fab758d..dc15df6ea6 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 339fab75a83a56df8124e5794170d9caaf7976bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Jul 2021 23:27:03 +0900 Subject: [PATCH 0382/2442] Rename colour variable in line with other usages --- osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs b/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs index fff18e615f..c07a5de1e4 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs @@ -33,9 +33,9 @@ namespace osu.Game.Graphics.UserInterfaceV2 } [BackgroundDependencyLoader(true)] - private void load([CanBeNull] OverlayColourProvider colourProvider, OsuColour osuColour) + private void load([CanBeNull] OverlayColourProvider colourProvider, OsuColour colours) { - Background.Colour = Arrow.Colour = colourProvider?.Background4 ?? osuColour.GreySeafoamDarker; + Background.Colour = Arrow.Colour = colourProvider?.Background4 ?? colours.GreySeafoamDarker; } protected override Drawable CreateArrow() => Empty(); From cc09a8b5badf002dedaa80bd0b57cb94e9abff75 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Jul 2021 23:55:46 +0900 Subject: [PATCH 0383/2442] Update to use `OsuPopover` implementation --- osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index b8c4b57378..7f4ea0908d 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -23,6 +23,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Input.Bindings; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Components; @@ -345,7 +346,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } - public class PasswordEntryPopover : BasicPopover + public class PasswordEntryPopover : OsuPopover { private readonly Room room; From 9d693c75cfad98bd011bf0f75427f21372994c38 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Jul 2021 23:56:52 +0900 Subject: [PATCH 0384/2442] Add `Schedule` to restore password text box focus behaviour --- osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 7f4ea0908d..84c20a6acc 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -389,7 +389,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { base.LoadComplete(); - GetContainingInputManager().ChangeFocus(passwordTextbox); + Schedule(() => GetContainingInputManager().ChangeFocus(passwordTextbox)); } } } From 1041a5fb940ad95775853c5ed56fe41309ba1c8e Mon Sep 17 00:00:00 2001 From: Evan Boehs <51836263+boehs@users.noreply.github.com> Date: Wed, 14 Jul 2021 14:39:45 -0400 Subject: [PATCH 0385/2442] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e95c12cfdc..016bd7d922 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ We are accepting bug reports (please report with as much detail as possible and - Detailed release changelogs are available on the [official osu! site](https://osu.ppy.sh/home/changelog/lazer). - You can learn more about our approach to [project management](https://github.com/ppy/osu/wiki/Project-management). -- Read peppy's [latest blog post](https://blog.ppy.sh/a-definitive-lazer-faq/) exploring where the project is currently and the roadmap going forward. +- Read peppy's [blog post](https://blog.ppy.sh/a-definitive-lazer-faq/) exploring where the project is currently and the roadmap going forward. ## Running osu! From 687c9aa33d44de87989fd5f9f1a9359c5bcc2880 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 12:37:52 +0900 Subject: [PATCH 0386/2442] Add tooltip and keywords for "high precision" setting --- osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index e87572e2ca..eefe160b94 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -47,7 +47,9 @@ namespace osu.Game.Overlays.Settings.Sections.Input new SettingsCheckbox { LabelText = "High precision mouse", - Current = relativeMode + TooltipText = "Attempts to bypass any operation system mouse acceleration. On windows, this is equivalent to what used to be known as \"Raw Input\".", + Current = relativeMode, + Keywords = new[] { "raw", "input", "relative", "cursor" } }, new SensitivitySetting { From 5b91111edabdfa7e1b7b2123c5322d73ffdd48ab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 12:50:34 +0900 Subject: [PATCH 0387/2442] Update `SettingsSubsection.Header` to accept a `LocalisableString` --- .../ManiaSettingsSubsection.cs | 2 +- .../UI/OsuSettingsSubsection.cs | 3 ++- osu.Game/Localisation/MouseSettingsStrings.cs | 19 +++++++++++++++++++ .../KeyBinding/GlobalKeyBindingsSection.cs | 11 ++++++----- .../KeyBinding/VariantBindingsSubsection.cs | 3 ++- .../Sections/Audio/AudioDevicesSettings.cs | 2 +- .../Settings/Sections/Audio/OffsetSettings.cs | 2 +- .../Settings/Sections/Audio/VolumeSettings.cs | 3 ++- .../Sections/Debug/GeneralSettings.cs | 3 ++- .../Settings/Sections/Debug/MemorySettings.cs | 3 ++- .../Sections/Gameplay/GeneralSettings.cs | 3 ++- .../Sections/Gameplay/ModsSettings.cs | 3 ++- .../Sections/General/LanguageSettings.cs | 3 ++- .../Sections/General/UpdateSettings.cs | 3 ++- .../Sections/Graphics/DetailSettings.cs | 3 ++- .../Sections/Graphics/LayoutSettings.cs | 2 +- .../Sections/Graphics/RendererSettings.cs | 3 ++- .../Sections/Input/BindingSettings.cs | 3 ++- .../Settings/Sections/Input/MouseSettings.cs | 3 ++- .../Settings/Sections/Input/TabletSettings.cs | 3 ++- .../Settings/Sections/InputSection.cs | 3 ++- .../Sections/Maintenance/GeneralSettings.cs | 3 ++- .../Online/AlertsAndPrivacySettings.cs | 3 ++- .../Sections/Online/IntegrationSettings.cs | 3 ++- .../Settings/Sections/Online/WebSettings.cs | 3 ++- .../Sections/UserInterface/GeneralSettings.cs | 2 +- .../UserInterface/MainMenuSettings.cs | 3 ++- .../UserInterface/SongSelectSettings.cs | 2 +- .../Overlays/Settings/SettingsSubsection.cs | 7 ++++--- 29 files changed, 75 insertions(+), 34 deletions(-) create mode 100644 osu.Game/Localisation/MouseSettingsStrings.cs diff --git a/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs b/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs index f89750a96e..36fa336d0c 100644 --- a/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs +++ b/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mania { public class ManiaSettingsSubsection : RulesetSettingsSubsection { - protected override string Header => "osu!mania"; + protected override LocalisableString Header => "osu!mania"; public ManiaSettingsSubsection(ManiaRuleset ruleset) : base(ruleset) diff --git a/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs index 705ba3e929..a4c0381d16 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.UI; @@ -11,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.UI { public class OsuSettingsSubsection : RulesetSettingsSubsection { - protected override string Header => "osu!"; + protected override LocalisableString Header => "osu!"; public OsuSettingsSubsection(Ruleset ruleset) : base(ruleset) diff --git a/osu.Game/Localisation/MouseSettingsStrings.cs b/osu.Game/Localisation/MouseSettingsStrings.cs new file mode 100644 index 0000000000..0260def409 --- /dev/null +++ b/osu.Game/Localisation/MouseSettingsStrings.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class MouseSettingsStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.MouseSettings"; + + /// + /// "Mouse" + /// + public static LocalisableString Mouse => new TranslatableString(getKey(@"mouse"), @"Mouse"); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs index c905397e77..6ea4209cce 100644 --- a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs +++ b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Game.Input.Bindings; using osu.Game.Overlays.Settings; @@ -28,7 +29,7 @@ namespace osu.Game.Overlays.KeyBinding private class DefaultBindingsSubsection : KeyBindingsSubsection { - protected override string Header => string.Empty; + protected override LocalisableString Header => string.Empty; public DefaultBindingsSubsection(GlobalActionContainer manager) : base(null) @@ -39,7 +40,7 @@ namespace osu.Game.Overlays.KeyBinding private class SongSelectKeyBindingSubsection : KeyBindingsSubsection { - protected override string Header => "Song Select"; + protected override LocalisableString Header => "Song Select"; public SongSelectKeyBindingSubsection(GlobalActionContainer manager) : base(null) @@ -50,7 +51,7 @@ namespace osu.Game.Overlays.KeyBinding private class InGameKeyBindingsSubsection : KeyBindingsSubsection { - protected override string Header => "In Game"; + protected override LocalisableString Header => "In Game"; public InGameKeyBindingsSubsection(GlobalActionContainer manager) : base(null) @@ -61,7 +62,7 @@ namespace osu.Game.Overlays.KeyBinding private class AudioControlKeyBindingsSubsection : KeyBindingsSubsection { - protected override string Header => "Audio"; + protected override LocalisableString Header => "Audio"; public AudioControlKeyBindingsSubsection(GlobalActionContainer manager) : base(null) @@ -72,7 +73,7 @@ namespace osu.Game.Overlays.KeyBinding private class EditorKeyBindingsSubsection : KeyBindingsSubsection { - protected override string Header => "Editor"; + protected override LocalisableString Header => "Editor"; public EditorKeyBindingsSubsection(GlobalActionContainer manager) : base(null) diff --git a/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs b/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs index 861d59c8f4..7618a42282 100644 --- a/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs +++ b/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs @@ -1,13 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Localisation; using osu.Game.Rulesets; namespace osu.Game.Overlays.KeyBinding { public class VariantBindingsSubsection : KeyBindingsSubsection { - protected override string Header { get; } + protected override LocalisableString Header { get; } public VariantBindingsSubsection(RulesetInfo ruleset, int variant) : base(variant) diff --git a/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs index b31e7dc45b..d64f176468 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs @@ -13,7 +13,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio { public class AudioDevicesSettings : SettingsSubsection { - protected override string Header => "Devices"; + protected override LocalisableString Header => "Devices"; [Resolved] private AudioManager audio { get; set; } diff --git a/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs index 1ae297f2a9..7f2e377c83 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs @@ -11,7 +11,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio { public class OffsetSettings : SettingsSubsection { - protected override string Header => "Offset Adjustment"; + protected override LocalisableString Header => "Offset Adjustment"; [BackgroundDependencyLoader] private void load(OsuConfigManager config) diff --git a/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs index c172a76ab9..8f88b03471 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs @@ -4,13 +4,14 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Configuration; namespace osu.Game.Overlays.Settings.Sections.Audio { public class VolumeSettings : SettingsSubsection { - protected override string Header => "Volume"; + protected override LocalisableString Header => "Volume"; [BackgroundDependencyLoader] private void load(AudioManager audio, OsuConfigManager config) diff --git a/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs index 4a9c9bd8a2..2b868cab85 100644 --- a/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Framework.Screens; using osu.Game.Screens.Import; @@ -11,7 +12,7 @@ namespace osu.Game.Overlays.Settings.Sections.Debug { public class GeneralSettings : SettingsSubsection { - protected override string Header => "General"; + protected override LocalisableString Header => "General"; [BackgroundDependencyLoader(true)] private void load(FrameworkDebugConfigManager config, FrameworkConfigManager frameworkConfig, OsuGame game) diff --git a/osu.Game/Overlays/Settings/Sections/Debug/MemorySettings.cs b/osu.Game/Overlays/Settings/Sections/Debug/MemorySettings.cs index db64c9a8ac..bf7fb351c0 100644 --- a/osu.Game/Overlays/Settings/Sections/Debug/MemorySettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Debug/MemorySettings.cs @@ -4,13 +4,14 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Framework.Platform; namespace osu.Game.Overlays.Settings.Sections.Debug { public class MemorySettings : SettingsSubsection { - protected override string Header => "Memory"; + protected override LocalisableString Header => "Memory"; [BackgroundDependencyLoader] private void load(FrameworkDebugConfigManager config, GameHost host) diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index 0b5ec4f338..353292606f 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -4,6 +4,7 @@ using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Configuration; using osu.Game.Rulesets.Scoring; @@ -11,7 +12,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay { public class GeneralSettings : SettingsSubsection { - protected override string Header => "General"; + protected override LocalisableString Header => "General"; [BackgroundDependencyLoader] private void load(OsuConfigManager config) diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs index 2b2fb9cef7..ec9ddde2da 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs @@ -4,13 +4,14 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Localisation; using osu.Game.Configuration; namespace osu.Game.Overlays.Settings.Sections.Gameplay { public class ModsSettings : SettingsSubsection { - protected override string Header => "Mods"; + protected override LocalisableString Header => "Mods"; public override IEnumerable FilterTerms => base.FilterTerms.Concat(new[] { "mod" }); diff --git a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs index dfcdb8e340..c6c752e2fd 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Extensions; using osu.Game.Localisation; @@ -15,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections.General private SettingsDropdown languageSelection; private Bindable frameworkLocale; - protected override string Header => "Language"; + protected override LocalisableString Header => "Language"; [BackgroundDependencyLoader] private void load(FrameworkConfigManager frameworkConfig) diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index c213313559..dd20e1d7ef 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Game.Configuration; @@ -19,7 +20,7 @@ namespace osu.Game.Overlays.Settings.Sections.General [Resolved(CanBeNull = true)] private UpdateManager updateManager { get; set; } - protected override string Header => "Updates"; + protected override LocalisableString Header => "Updates"; private SettingsButton checkForUpdatesButton; diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs index 30caa45995..f889cfca0f 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs @@ -3,13 +3,14 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Configuration; namespace osu.Game.Overlays.Settings.Sections.Graphics { public class DetailSettings : SettingsSubsection { - protected override string Header => "Detail Settings"; + protected override LocalisableString Header => "Detail Settings"; [BackgroundDependencyLoader] private void load(OsuConfigManager config) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 669753d2cb..91208cb78a 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 { public class LayoutSettings : SettingsSubsection { - protected override string Header => "Layout"; + protected override LocalisableString Header => "Layout"; private FillFlowContainer> scalingSettings; diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index 70225ff6b8..2210c7911e 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Framework.Platform; using osu.Game.Configuration; @@ -11,7 +12,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { public class RendererSettings : SettingsSubsection { - protected override string Header => "Renderer"; + protected override LocalisableString Header => "Renderer"; private SettingsEnumDropdown frameLimiterDropdown; diff --git a/osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs index 79c73863cf..e68cd606e5 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs @@ -2,12 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; +using osu.Framework.Localisation; namespace osu.Game.Overlays.Settings.Sections.Input { public class BindingSettings : SettingsSubsection { - protected override string Header => "Shortcut and gameplay bindings"; + protected override LocalisableString Header => "Shortcut and gameplay bindings"; public BindingSettings(KeyBindingPanel keyConfig) { diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index eefe160b94..b39507da62 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -10,6 +10,7 @@ using osu.Framework.Localisation; using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Input; +using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Input { @@ -17,7 +18,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input { private readonly MouseHandler mouseHandler; - protected override string Header => "Mouse"; + protected override LocalisableString Header => MouseSettingsStrings.Mouse; private Bindable handlerSensitivity; diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index d770c18878..e9ed3378a2 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Handlers.Tablet; +using osu.Framework.Localisation; using osu.Framework.Platform; using osu.Framework.Threading; using osu.Game.Graphics.Sprites; @@ -52,7 +53,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input private OsuSpriteText noTabletMessage; - protected override string Header => "Tablet"; + protected override LocalisableString Header => "Tablet"; public TabletSettings(ITabletHandler tabletHandler) { diff --git a/osu.Game/Overlays/Settings/Sections/InputSection.cs b/osu.Game/Overlays/Settings/Sections/InputSection.cs index 6e99891794..366f39388a 100644 --- a/osu.Game/Overlays/Settings/Sections/InputSection.cs +++ b/osu.Game/Overlays/Settings/Sections/InputSection.cs @@ -9,6 +9,7 @@ using osu.Framework.Input.Handlers.Joystick; using osu.Framework.Input.Handlers.Midi; using osu.Framework.Input.Handlers.Mouse; using osu.Framework.Input.Handlers.Tablet; +using osu.Framework.Localisation; using osu.Framework.Platform; using osu.Game.Overlays.Settings.Sections.Input; @@ -100,7 +101,7 @@ namespace osu.Game.Overlays.Settings.Sections }; } - protected override string Header => handler.Description; + protected override LocalisableString Header => handler.Description; } } } diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index a38ca81e23..b9a408b1f8 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Collections; using osu.Game.Database; @@ -17,7 +18,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { public class GeneralSettings : SettingsSubsection { - protected override string Header => "General"; + protected override LocalisableString Header => "General"; private TriangleButton importBeatmapsButton; private TriangleButton importScoresButton; diff --git a/osu.Game/Overlays/Settings/Sections/Online/AlertsAndPrivacySettings.cs b/osu.Game/Overlays/Settings/Sections/Online/AlertsAndPrivacySettings.cs index b0f6400d4f..3a2de2ee36 100644 --- a/osu.Game/Overlays/Settings/Sections/Online/AlertsAndPrivacySettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Online/AlertsAndPrivacySettings.cs @@ -3,13 +3,14 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Configuration; namespace osu.Game.Overlays.Settings.Sections.Online { public class AlertsAndPrivacySettings : SettingsSubsection { - protected override string Header => "Alerts and Privacy"; + protected override LocalisableString Header => "Alerts and Privacy"; [BackgroundDependencyLoader] private void load(OsuConfigManager config) diff --git a/osu.Game/Overlays/Settings/Sections/Online/IntegrationSettings.cs b/osu.Game/Overlays/Settings/Sections/Online/IntegrationSettings.cs index d2867962c0..f2012f0d9c 100644 --- a/osu.Game/Overlays/Settings/Sections/Online/IntegrationSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Online/IntegrationSettings.cs @@ -3,13 +3,14 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Configuration; namespace osu.Game.Overlays.Settings.Sections.Online { public class IntegrationSettings : SettingsSubsection { - protected override string Header => "Integrations"; + protected override LocalisableString Header => "Integrations"; [BackgroundDependencyLoader] private void load(OsuConfigManager config) diff --git a/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs b/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs index 59bcbe4d89..89e7b096f3 100644 --- a/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs @@ -3,13 +3,14 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Configuration; namespace osu.Game.Overlays.Settings.Sections.Online { public class WebSettings : SettingsSubsection { - protected override string Header => "Web"; + protected override LocalisableString Header => "Web"; [BackgroundDependencyLoader] private void load(OsuConfigManager config) diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs index a6eb008623..4b26645ef3 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs @@ -11,7 +11,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface { public class GeneralSettings : SettingsSubsection { - protected override string Header => "General"; + protected override LocalisableString Header => "General"; [BackgroundDependencyLoader] private void load(OsuConfigManager config) diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs index 5f703ed5a4..81bbcbb54a 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Configuration; using osu.Game.Online.API; using osu.Game.Users; @@ -12,7 +13,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface { public class MainMenuSettings : SettingsSubsection { - protected override string Header => "Main Menu"; + protected override LocalisableString Header => "Main Menu"; private IBindable user; diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs index 2470c0a6c5..587155eb0d 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs @@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface private Bindable minStars; private Bindable maxStars; - protected override string Header => "Song Select"; + protected override LocalisableString Header => "Song Select"; [BackgroundDependencyLoader] private void load(OsuConfigManager config) diff --git a/osu.Game/Overlays/Settings/SettingsSubsection.cs b/osu.Game/Overlays/Settings/SettingsSubsection.cs index 6abf6283b9..df32424b67 100644 --- a/osu.Game/Overlays/Settings/SettingsSubsection.cs +++ b/osu.Game/Overlays/Settings/SettingsSubsection.cs @@ -8,6 +8,7 @@ using osu.Game.Graphics.Sprites; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Localisation; using osu.Framework.Testing; using osu.Game.Graphics; @@ -20,10 +21,10 @@ namespace osu.Game.Overlays.Settings protected readonly FillFlowContainer FlowContent; - protected abstract string Header { get; } + protected abstract LocalisableString Header { get; } public IEnumerable FilterableChildren => Children.OfType(); - public virtual IEnumerable FilterTerms => new[] { Header }; + public virtual IEnumerable FilterTerms => new[] { Header.ToString() }; public bool MatchingFilter { @@ -54,7 +55,7 @@ namespace osu.Game.Overlays.Settings { new OsuSpriteText { - Text = Header.ToUpperInvariant(), + Text = Header.ToString().ToUpper(), // TODO: Add localisation support after https://github.com/ppy/osu-framework/pull/4603 is merged. Margin = new MarginPadding { Vertical = 30, Left = SettingsPanel.CONTENT_MARGINS, Right = SettingsPanel.CONTENT_MARGINS }, Font = OsuFont.GetFont(weight: FontWeight.Bold), }, From d58534dce60161c0207d27c160306dc1267021d1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 13:01:21 +0900 Subject: [PATCH 0388/2442] Update `CommonStrings` to latest localiser tools output --- osu.Game/Localisation/CommonStrings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Localisation/CommonStrings.cs b/osu.Game/Localisation/CommonStrings.cs index 50e01f06fc..ad87986554 100644 --- a/osu.Game/Localisation/CommonStrings.cs +++ b/osu.Game/Localisation/CommonStrings.cs @@ -14,6 +14,6 @@ namespace osu.Game.Localisation /// public static LocalisableString Cancel => new TranslatableString(getKey(@"cancel"), @"Cancel"); - private static string getKey(string key) => $"{prefix}:{key}"; + private static string getKey(string key) => $@"{prefix}:{key}"; } } From 3f3adfe2979ac732bc2689b63d9066450a12833f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 13:07:35 +0900 Subject: [PATCH 0389/2442] Add localisation support for `MouseSettings` --- osu.Game/Localisation/MouseSettingsStrings.cs | 42 ++++++++++++++++++- .../Settings/Sections/Input/MouseSettings.cs | 18 ++++---- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/osu.Game/Localisation/MouseSettingsStrings.cs b/osu.Game/Localisation/MouseSettingsStrings.cs index 0260def409..9b1f7fe4c5 100644 --- a/osu.Game/Localisation/MouseSettingsStrings.cs +++ b/osu.Game/Localisation/MouseSettingsStrings.cs @@ -14,6 +14,46 @@ namespace osu.Game.Localisation /// public static LocalisableString Mouse => new TranslatableString(getKey(@"mouse"), @"Mouse"); + /// + /// "Not applicable in full screen mode" + /// + public static LocalisableString NotApplicableFullscreen => new TranslatableString(getKey(@"not_applicable_full_screen"), @"Not applicable in full screen mode"); + + /// + /// "High precision mouse" + /// + public static LocalisableString HighPrecisionMouse => new TranslatableString(getKey(@"high_precision_mouse"), @"High precision mouse"); + + /// + /// "Attempts to bypass any operation system mouse acceleration. On windows, this is equivalent to what used to be known as "Raw Input"." + /// + public static LocalisableString HighPrecisionMouseTooltip => new TranslatableString(getKey(@"high_precision_mouse_tooltip"), @"Attempts to bypass any operation system mouse acceleration. On windows, this is equivalent to what used to be known as ""Raw Input""."); + + /// + /// "Confine mouse cursor to window" + /// + public static LocalisableString ConfineMouseMode => new TranslatableString(getKey(@"confine_mouse_mode"), @"Confine mouse cursor to window"); + + /// + /// "Disable mouse wheel during gameplay" + /// + public static LocalisableString DisableMouseWheel => new TranslatableString(getKey(@"disable_mouse_wheel"), @"Disable mouse wheel during gameplay"); + + /// + /// "Disable mouse buttons during gameplay" + /// + public static LocalisableString DisableMouseButtons => new TranslatableString(getKey(@"disable_mouse_buttons"), @"Disable mouse buttons during gameplay"); + + /// + /// "Enable high precision mouse to adjust sensitivity" + /// + public static LocalisableString EnableHighPrecisionForSensitivityAdjust => new TranslatableString(getKey(@"enable_high_precision_for_sensitivity_adjust"), @"Enable high precision mouse to adjust sensitivity"); + + /// + /// "Cursor sensitivity" + /// + public static LocalisableString CursorSensitivity => new TranslatableString(getKey(@"cursor_sensitivity"), @"Cursor sensitivity"); + private static string getKey(string key) => $@"{prefix}:{key}"; } -} \ No newline at end of file +} diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index b39507da62..753096a207 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -47,29 +47,29 @@ namespace osu.Game.Overlays.Settings.Sections.Input { new SettingsCheckbox { - LabelText = "High precision mouse", - TooltipText = "Attempts to bypass any operation system mouse acceleration. On windows, this is equivalent to what used to be known as \"Raw Input\".", + LabelText = MouseSettingsStrings.HighPrecisionMouse, + TooltipText = MouseSettingsStrings.HighPrecisionMouseTooltip, Current = relativeMode, - Keywords = new[] { "raw", "input", "relative", "cursor" } + Keywords = new[] { @"raw", @"input", @"relative", @"cursor" } }, new SensitivitySetting { - LabelText = "Cursor sensitivity", + LabelText = MouseSettingsStrings.CursorSensitivity, Current = localSensitivity }, confineMouseModeSetting = new SettingsEnumDropdown { - LabelText = "Confine mouse cursor to window", + LabelText = MouseSettingsStrings.ConfineMouseMode, Current = osuConfig.GetBindable(OsuSetting.ConfineMouseMode) }, new SettingsCheckbox { - LabelText = "Disable mouse wheel during gameplay", + LabelText = MouseSettingsStrings.DisableMouseWheel, Current = osuConfig.GetBindable(OsuSetting.MouseDisableWheel) }, new SettingsCheckbox { - LabelText = "Disable mouse buttons during gameplay", + LabelText = MouseSettingsStrings.DisableMouseButtons, Current = osuConfig.GetBindable(OsuSetting.MouseDisableButtons) }, }; @@ -99,7 +99,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input if (isFullscreen) { confineMouseModeSetting.Current.Disabled = true; - confineMouseModeSetting.TooltipText = "Not applicable in full screen mode"; + confineMouseModeSetting.TooltipText = MouseSettingsStrings.NotApplicableFullscreen; } else { @@ -120,7 +120,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input private class SensitivitySlider : OsuSliderBar { - public override LocalisableString TooltipText => Current.Disabled ? "enable high precision mouse to adjust sensitivity" : $"{base.TooltipText}x"; + public override LocalisableString TooltipText => Current.Disabled ? MouseSettingsStrings.EnableHighPrecisionForSensitivityAdjust : $"{base.TooltipText}x"; } } } From 8cc2d2e79e6679c3c541b6999bc1b8dbdd332f1d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 13:51:28 +0900 Subject: [PATCH 0390/2442] Update beat synced container tests to be usable --- .../TestSceneBeatSyncedContainer.cs | 82 ++++++++++++------- 1 file changed, 52 insertions(+), 30 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs index 82b7e65c4f..c952163ba5 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs @@ -5,18 +5,18 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Audio.Track; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osu.Game.Overlays; +using osu.Game.Rulesets.Osu; using osuTK.Graphics; namespace osu.Game.Tests.Visual.UserInterface @@ -24,37 +24,55 @@ namespace osu.Game.Tests.Visual.UserInterface [TestFixture] public class TestSceneBeatSyncedContainer : OsuTestScene { - private readonly NowPlayingOverlay np; + private BeatContainer beatContainer; + private DecoupleableInterpolatingFramedClock decoupledClock; - public TestSceneBeatSyncedContainer() + [SetUpSteps] + public void SetUpSteps() { - Clock = new FramedClock(); - Clock.ProcessFrame(); - - AddRange(new Drawable[] + AddStep("Set beatmap", () => { - new BeatContainer - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - }, - np = new NowPlayingOverlay - { - Origin = Anchor.TopRight, - Anchor = Anchor.TopRight, - } + Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); }); + + AddStep("Create beat sync container", () => + { + Children = new Drawable[] + { + beatContainer = new BeatContainer + { + Clock = decoupledClock = new DecoupleableInterpolatingFramedClock + { + IsCoupled = false, + }, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + }, + }; + + decoupledClock.ChangeSource(Beatmap.Value.Track); + }); + + AddStep("Start playback", () => decoupledClock.Start()); } - protected override void LoadComplete() + [Test] + public void TestFirstBeatAtFirstTimingPoint() { - base.LoadComplete(); - np.ToggleVisibility(); + AddStep("Set time before zero", () => + { + decoupledClock.Seek(-1000); + }); + + AddStep("bind event", () => + { + beatContainer.NewBeat = (i, point, effectControlPoint, channelAmplitudes) => { }; + }); } private class BeatContainer : BeatSyncedContainer { - private const int flash_layer_heigth = 150; + private const int flash_layer_height = 150; private readonly InfoString timingPointCount; private readonly InfoString currentTimingPoint; @@ -64,12 +82,10 @@ namespace osu.Game.Tests.Visual.UserInterface private readonly InfoString adjustedBeatLength; private readonly InfoString timeUntilNextBeat; private readonly InfoString timeSinceLastBeat; + private readonly InfoString currentTime; private readonly Box flashLayer; - [Resolved] - private MusicController musicController { get; set; } - public BeatContainer() { RelativeSizeAxes = Axes.X; @@ -82,7 +98,7 @@ namespace osu.Game.Tests.Visual.UserInterface Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Bottom = flash_layer_heigth }, + Margin = new MarginPadding { Bottom = flash_layer_height }, Children = new Drawable[] { new Box @@ -98,6 +114,7 @@ namespace osu.Game.Tests.Visual.UserInterface Direction = FillDirection.Vertical, Children = new Drawable[] { + currentTime = new InfoString(@"Current time"), timingPointCount = new InfoString(@"Timing points amount"), currentTimingPoint = new InfoString(@"Current timing point"), beatCount = new InfoString(@"Beats amount (in the current timing point)"), @@ -116,7 +133,7 @@ namespace osu.Game.Tests.Visual.UserInterface Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, RelativeSizeAxes = Axes.X, - Height = flash_layer_heigth, + Height = flash_layer_height, Children = new Drawable[] { new Box @@ -164,7 +181,7 @@ namespace osu.Game.Tests.Visual.UserInterface if (timingPoints.Count == 0) return 0; if (timingPoints[^1] == current) - return (int)Math.Ceiling((musicController.CurrentTrack.Length - current.Time) / current.BeatLength); + return (int)Math.Ceiling((Clock.CurrentTime - current.Time) / current.BeatLength); return (int)Math.Ceiling((getNextTimingPoint(current).Time - current.Time) / current.BeatLength); } @@ -174,8 +191,11 @@ namespace osu.Game.Tests.Visual.UserInterface base.Update(); timeUntilNextBeat.Value = TimeUntilNextBeat; timeSinceLastBeat.Value = TimeSinceLastBeat; + currentTime.Value = Clock.CurrentTime; } + public Action NewBeat; + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) { base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); @@ -187,7 +207,9 @@ namespace osu.Game.Tests.Visual.UserInterface beatsPerMinute.Value = 60000 / timingPoint.BeatLength; adjustedBeatLength.Value = timingPoint.BeatLength; - flashLayer.FadeOutFromOne(timingPoint.BeatLength); + flashLayer.FadeOutFromOne(timingPoint.BeatLength / 4); + + NewBeat?.Invoke(beatIndex, timingPoint, effectPoint, amplitudes); } } @@ -200,7 +222,7 @@ namespace osu.Game.Tests.Visual.UserInterface public double Value { - set => valueText.Text = $"{value:G}"; + set => valueText.Text = $"{value:0.##}"; } public InfoString(string header) From a3129ad00e22dc44c9f4965f8ef0702b22ce6a91 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 14:30:38 +0900 Subject: [PATCH 0391/2442] Refactor `BeatSyncedContainer` to support `GameplayClock` --- .../TestSceneBeatSyncedContainer.cs | 35 ++++++----- .../Containers/BeatSyncedContainer.cs | 62 ++++++++++++++----- 2 files changed, 66 insertions(+), 31 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs index c952163ba5..f58f9fcaaf 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs @@ -11,12 +11,12 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Testing; -using osu.Framework.Timing; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Osu; +using osu.Game.Screens.Play; using osuTK.Graphics; namespace osu.Game.Tests.Visual.UserInterface @@ -25,7 +25,8 @@ namespace osu.Game.Tests.Visual.UserInterface public class TestSceneBeatSyncedContainer : OsuTestScene { private BeatContainer beatContainer; - private DecoupleableInterpolatingFramedClock decoupledClock; + + private MasterGameplayClockContainer gameplayClockContainer; [SetUpSteps] public void SetUpSteps() @@ -39,21 +40,18 @@ namespace osu.Game.Tests.Visual.UserInterface { Children = new Drawable[] { - beatContainer = new BeatContainer + gameplayClockContainer = new MasterGameplayClockContainer(Beatmap.Value, 0) { - Clock = decoupledClock = new DecoupleableInterpolatingFramedClock + Child = beatContainer = new BeatContainer { - IsCoupled = false, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, }, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - }, + } }; - - decoupledClock.ChangeSource(Beatmap.Value.Track); }); - AddStep("Start playback", () => decoupledClock.Start()); + AddStep("Start playback", () => gameplayClockContainer.Start()); } [Test] @@ -61,7 +59,7 @@ namespace osu.Game.Tests.Visual.UserInterface { AddStep("Set time before zero", () => { - decoupledClock.Seek(-1000); + gameplayClockContainer.Seek(-1000); }); AddStep("bind event", () => @@ -150,8 +148,13 @@ namespace osu.Game.Tests.Visual.UserInterface } } }; + } - Beatmap.ValueChanged += delegate + protected override void LoadComplete() + { + base.LoadComplete(); + + Beatmap.BindValueChanged(_ => { timingPointCount.Value = 0; currentTimingPoint.Value = 0; @@ -161,7 +164,7 @@ namespace osu.Game.Tests.Visual.UserInterface adjustedBeatLength.Value = 0; timeUntilNextBeat.Value = 0; timeSinceLastBeat.Value = 0; - }; + }, true); } private List timingPoints => Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.ToList(); @@ -181,7 +184,7 @@ namespace osu.Game.Tests.Visual.UserInterface if (timingPoints.Count == 0) return 0; if (timingPoints[^1] == current) - return (int)Math.Ceiling((Clock.CurrentTime - current.Time) / current.BeatLength); + return (int)Math.Ceiling((BeatSyncClock.CurrentTime - current.Time) / current.BeatLength); return (int)Math.Ceiling((getNextTimingPoint(current).Time - current.Time) / current.BeatLength); } @@ -191,7 +194,7 @@ namespace osu.Game.Tests.Visual.UserInterface base.Update(); timeUntilNextBeat.Value = TimeUntilNextBeat; timeSinceLastBeat.Value = TimeSinceLastBeat; - currentTime.Value = Clock.CurrentTime; + currentTime.Value = BeatSyncClock.CurrentTime; } public Action NewBeat; diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index e2a0c09a6b..cb26406d64 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -1,19 +1,29 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; +using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Screens.Play; namespace osu.Game.Graphics.Containers { + /// + /// A container which fires a callback when a new beat is reached. + /// Consumes a parent or (whichever is first available). + /// + /// + /// This container does not set its own clock to the source used for beat matching. + /// This means that if the beat source clock is playing faster or slower, animations may unexpectedly overlap. + /// Make sure this container's Clock is also set to the expected source (or within a parent element which provides this). + /// public class BeatSyncedContainer : Container { - protected readonly IBindable Beatmap = new Bindable(); - private int lastBeat; private TimingControlPoint lastTimingPoint; @@ -45,15 +55,45 @@ namespace osu.Game.Graphics.Containers protected bool IsBeatSyncedWithTrack { get; private set; } + protected virtual void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) + { + } + + [Resolved] + protected IBindable Beatmap { get; private set; } + + [Resolved(canBeNull: true)] + protected GameplayClock GameplayClock { get; private set; } + + protected IClock BeatSyncClock + { + get + { + if (GameplayClock != null) + return GameplayClock; + + if (Beatmap.Value.TrackLoaded) + return Beatmap.Value.Track; + + return null; + } + } + protected override void Update() { ITrack track = null; IBeatmap beatmap = null; double currentTrackTime = 0; + TimingControlPoint timingPoint = null; EffectControlPoint effectPoint = null; + var clock = BeatSyncClock; + + if (clock == null) + return; + if (Beatmap.Value.TrackLoaded && Beatmap.Value.BeatmapLoaded) { track = Beatmap.Value.Track; @@ -62,7 +102,7 @@ namespace osu.Game.Graphics.Containers if (track != null && beatmap != null && track.IsRunning && track.Length > 0) { - currentTrackTime = track.CurrentTime + EarlyActivationMilliseconds; + currentTrackTime = clock.CurrentTime + EarlyActivationMilliseconds; timingPoint = beatmap.ControlPointInfo.TimingPointAt(currentTrackTime); effectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTrackTime); @@ -70,13 +110,15 @@ namespace osu.Game.Graphics.Containers IsBeatSyncedWithTrack = timingPoint?.BeatLength > 0; - if (timingPoint == null || !IsBeatSyncedWithTrack) + if (!IsBeatSyncedWithTrack) { - currentTrackTime = Clock.CurrentTime; + currentTrackTime = clock.CurrentTime; timingPoint = TimingControlPoint.DEFAULT; effectPoint = EffectControlPoint.DEFAULT; } + Debug.Assert(timingPoint != null); + double beatLength = timingPoint.BeatLength / Divisor; while (beatLength < MinimumBeatLength) @@ -103,15 +145,5 @@ namespace osu.Game.Graphics.Containers lastBeat = beatIndex; lastTimingPoint = timingPoint; } - - [BackgroundDependencyLoader] - private void load(IBindable beatmap) - { - Beatmap.BindTo(beatmap); - } - - protected virtual void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) - { - } } } From cab8b941322b2731605778a914badf9388e5b67c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 14:36:37 +0900 Subject: [PATCH 0392/2442] Add failing test --- .../TestSceneBeatSyncedContainer.cs | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs index f58f9fcaaf..ac1f88ad33 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Testing; +using osu.Framework.Utils; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -57,15 +58,28 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestFirstBeatAtFirstTimingPoint() { - AddStep("Set time before zero", () => - { - gameplayClockContainer.Seek(-1000); - }); + int? lastBeatIndex = null; + double? lastBpm = null; AddStep("bind event", () => { - beatContainer.NewBeat = (i, point, effectControlPoint, channelAmplitudes) => { }; + beatContainer.NewBeat = (i, timingControlPoint, effectControlPoint, channelAmplitudes) => + { + lastBeatIndex = i; + lastBpm = timingControlPoint.BPM; + }; }); + + AddStep("Set time before zero", () => + { + lastBeatIndex = null; + lastBpm = null; + gameplayClockContainer.Seek(-1000); + }); + + AddUntilStep("wait for trigger", () => lastBpm != null); + AddAssert("bpm is from beatmap", () => lastBpm != null&&Precision.AlmostEquals(lastBpm.Value, 128)); + AddAssert("beat index is less than zero", () => lastBeatIndex < 0); } private class BeatContainer : BeatSyncedContainer From 5ecf6511e6543300fdba03197b5ec53583db54be Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 14:37:08 +0900 Subject: [PATCH 0393/2442] Fix default timing points being used if "track" is not running --- osu.Game/Graphics/Containers/BeatSyncedContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index cb26406d64..6d5f4d63ac 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -100,7 +100,7 @@ namespace osu.Game.Graphics.Containers beatmap = Beatmap.Value.Beatmap; } - if (track != null && beatmap != null && track.IsRunning && track.Length > 0) + if (track != null && beatmap != null && clock.IsRunning && track.Length > 0) { currentTrackTime = clock.CurrentTime + EarlyActivationMilliseconds; From c47ff1919c0075cb64b7e1539885f26b6adc012a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 14:56:49 +0900 Subject: [PATCH 0394/2442] Fix regression in idle behaviour and refactor further --- .../Containers/BeatSyncedContainer.cs | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 6d5f4d63ac..929a300831 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -21,6 +21,8 @@ namespace osu.Game.Graphics.Containers /// This container does not set its own clock to the source used for beat matching. /// This means that if the beat source clock is playing faster or slower, animations may unexpectedly overlap. /// Make sure this container's Clock is also set to the expected source (or within a parent element which provides this). + /// + /// This container will also trigger beat events when the beat matching clock is paused at 's BPM. /// public class BeatSyncedContainer : Container { @@ -53,6 +55,9 @@ namespace osu.Game.Graphics.Containers /// public double MinimumBeatLength { get; set; } + /// + /// Whether this container is currently tracking a beatmap's timing data. + /// protected bool IsBeatSyncedWithTrack { get; private set; } protected virtual void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) @@ -84,35 +89,36 @@ namespace osu.Game.Graphics.Containers ITrack track = null; IBeatmap beatmap = null; - double currentTrackTime = 0; + TimingControlPoint timingPoint; + EffectControlPoint effectPoint; - TimingControlPoint timingPoint = null; - EffectControlPoint effectPoint = null; - - var clock = BeatSyncClock; + IClock clock = BeatSyncClock; if (clock == null) return; + double currentTrackTime = clock.CurrentTime + EarlyActivationMilliseconds; + if (Beatmap.Value.TrackLoaded && Beatmap.Value.BeatmapLoaded) { track = Beatmap.Value.Track; beatmap = Beatmap.Value.Beatmap; } - if (track != null && beatmap != null && clock.IsRunning && track.Length > 0) + IsBeatSyncedWithTrack = beatmap != null && clock.IsRunning && track?.Length > 0; + + if (IsBeatSyncedWithTrack) { - currentTrackTime = clock.CurrentTime + EarlyActivationMilliseconds; + Debug.Assert(beatmap != null); timingPoint = beatmap.ControlPointInfo.TimingPointAt(currentTrackTime); effectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTrackTime); } - - IsBeatSyncedWithTrack = timingPoint?.BeatLength > 0; - - if (!IsBeatSyncedWithTrack) + else { - currentTrackTime = clock.CurrentTime; + // this may be the case where the beat syncing clock has been paused. + // we still want to show an idle animation, so use this container's time instead. + currentTrackTime = Clock.CurrentTime; timingPoint = TimingControlPoint.DEFAULT; effectPoint = EffectControlPoint.DEFAULT; } From 77bfe700e091fd16af9b27edddfd987ec0094579 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 14:59:57 +0900 Subject: [PATCH 0395/2442] Add test coverage of idle beat --- .../TestSceneBeatSyncedContainer.cs | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs index ac1f88ad33..3ca6bf782a 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs @@ -78,10 +78,33 @@ namespace osu.Game.Tests.Visual.UserInterface }); AddUntilStep("wait for trigger", () => lastBpm != null); - AddAssert("bpm is from beatmap", () => lastBpm != null&&Precision.AlmostEquals(lastBpm.Value, 128)); + AddAssert("bpm is from beatmap", () => lastBpm != null && Precision.AlmostEquals(lastBpm.Value, 128)); AddAssert("beat index is less than zero", () => lastBeatIndex < 0); } + [Test] + public void TestIdleBeatOnPausedClock() + { + double? lastBpm = null; + + AddStep("bind event", () => + { + beatContainer.NewBeat = (i, timingControlPoint, effectControlPoint, channelAmplitudes) => lastBpm = timingControlPoint.BPM; + }); + + AddUntilStep("wait for trigger", () => lastBpm != null); + AddAssert("bpm is from beatmap", () => lastBpm != null && Precision.AlmostEquals(lastBpm.Value, 128)); + + AddStep("pause gameplay clock", () => + { + lastBpm = null; + gameplayClockContainer.Stop(); + }); + + AddUntilStep("wait for trigger", () => lastBpm != null); + AddAssert("bpm is from beatmap", () => lastBpm != null && Precision.AlmostEquals(lastBpm.Value, 60)); + } + private class BeatContainer : BeatSyncedContainer { private const int flash_layer_height = 150; From 98a1f40a982fe52d6482877c2850ab8cc13ab309 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 15:10:14 +0900 Subject: [PATCH 0396/2442] Ensure `EarlyActivationMilliseconds` is applied even in idle state --- osu.Game/Graphics/Containers/BeatSyncedContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 929a300831..55f694f17e 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -97,7 +97,7 @@ namespace osu.Game.Graphics.Containers if (clock == null) return; - double currentTrackTime = clock.CurrentTime + EarlyActivationMilliseconds; + double currentTrackTime = clock.CurrentTime; if (Beatmap.Value.TrackLoaded && Beatmap.Value.BeatmapLoaded) { @@ -123,7 +123,7 @@ namespace osu.Game.Graphics.Containers effectPoint = EffectControlPoint.DEFAULT; } - Debug.Assert(timingPoint != null); + currentTrackTime += EarlyActivationMilliseconds; double beatLength = timingPoint.BeatLength / Divisor; From d0fc25888683d7fc75e4da27c111acc77ad08364 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 15:11:42 +0900 Subject: [PATCH 0397/2442] Remove unused `OsuLogo.BeatMatching` --- osu.Game/Screens/Menu/OsuLogo.cs | 4 ---- osu.Game/Screens/OsuScreen.cs | 1 - 2 files changed, 5 deletions(-) diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index 283be913b0..a9376325cd 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -72,8 +72,6 @@ namespace osu.Game.Screens.Menu set => colourAndTriangles.FadeTo(value ? 1 : 0, transition_length, Easing.OutQuint); } - public bool BeatMatching = true; - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => logoContainer.ReceivePositionalInputAt(screenSpacePos); public bool Ripple @@ -272,8 +270,6 @@ namespace osu.Game.Screens.Menu { base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); - if (!BeatMatching) return; - lastBeatIndex = beatIndex; var beatLength = timingPoint.BeatLength; diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index aeb51813e4..c3b2612e79 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -242,7 +242,6 @@ namespace osu.Game.Screens logo.Anchor = Anchor.TopLeft; logo.Origin = Anchor.Centre; logo.RelativePositionAxes = Axes.Both; - logo.BeatMatching = true; logo.Triangles = true; logo.Ripple = true; } From 3197f599bb2a9d5a1f516416293d5ac0ab0e636b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 16:01:46 +0900 Subject: [PATCH 0398/2442] Add failing test showing `OnNewBeat` can execute far away from an actual beat --- .../TestSceneBeatSyncedContainer.cs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs index 3ca6bf782a..5bfaf35bf1 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs @@ -56,7 +56,34 @@ namespace osu.Game.Tests.Visual.UserInterface } [Test] - public void TestFirstBeatAtFirstTimingPoint() + public void TestSeekBackDoesntPlayMidBeat() + { + int? lastBeatIndex = null; + double? lastActuationTime = null; + TimingControlPoint lastTimingPoint = null; + + AddStep("bind event", () => + { + beatContainer.NewBeat = (i, timingControlPoint, effectControlPoint, channelAmplitudes) => + { + lastActuationTime = gameplayClockContainer.CurrentTime; + lastTimingPoint = timingControlPoint; + lastBeatIndex = i; + }; + }); + + AddStep("Set time before zero", () => + { + lastBeatIndex = null; + gameplayClockContainer.Seek(-1000); + }); + + AddUntilStep("wait for trigger", () => lastBeatIndex != null); + AddAssert("trigger is near beat length", () => lastActuationTime != null && lastBeatIndex != null && Precision.AlmostEquals(lastTimingPoint.Time + lastBeatIndex.Value * lastTimingPoint.BeatLength, lastActuationTime.Value, 32)); + } + + [Test] + public void TestNegativeBeatsStillUsingBeatmapTiming() { int? lastBeatIndex = null; double? lastBpm = null; From b6996d647e9cf62232412e350eb4cc3d98f51680 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 16:13:13 +0900 Subject: [PATCH 0399/2442] Add ability to disable mistimed event firings --- .../TestSceneBeatSyncedContainer.cs | 31 ++++++++++++++----- .../Containers/BeatSyncedContainer.cs | 23 ++++++++++++-- 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs index 5bfaf35bf1..2c5433e4aa 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.UserInterface [TestFixture] public class TestSceneBeatSyncedContainer : OsuTestScene { - private BeatContainer beatContainer; + private TestBeatSyncedContainer beatContainer; private MasterGameplayClockContainer gameplayClockContainer; @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.UserInterface { gameplayClockContainer = new MasterGameplayClockContainer(Beatmap.Value, 0) { - Child = beatContainer = new BeatContainer + Child = beatContainer = new TestBeatSyncedContainer { Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, @@ -55,13 +55,16 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("Start playback", () => gameplayClockContainer.Start()); } - [Test] - public void TestSeekBackDoesntPlayMidBeat() + [TestCase(false)] + [TestCase(true)] + public void TestDisallowMistimedEventFiring(bool allowMistimed) { int? lastBeatIndex = null; double? lastActuationTime = null; TimingControlPoint lastTimingPoint = null; + AddStep("set mistimed to disallow", () => beatContainer.AllowMistimedEventFiring = allowMistimed); + AddStep("bind event", () => { beatContainer.NewBeat = (i, timingControlPoint, effectControlPoint, channelAmplitudes) => @@ -79,7 +82,15 @@ namespace osu.Game.Tests.Visual.UserInterface }); AddUntilStep("wait for trigger", () => lastBeatIndex != null); - AddAssert("trigger is near beat length", () => lastActuationTime != null && lastBeatIndex != null && Precision.AlmostEquals(lastTimingPoint.Time + lastBeatIndex.Value * lastTimingPoint.BeatLength, lastActuationTime.Value, 32)); + + if (!allowMistimed) + { + AddAssert("trigger is near beat length", () => lastActuationTime != null && lastBeatIndex != null && Precision.AlmostEquals(lastTimingPoint.Time + lastBeatIndex.Value * lastTimingPoint.BeatLength, lastActuationTime.Value, BeatSyncedContainer.MISTIMED_ALLOWANCE)); + } + else + { + AddAssert("trigger is not near beat length", () => lastActuationTime != null && lastBeatIndex != null && !Precision.AlmostEquals(lastTimingPoint.Time + lastBeatIndex.Value * lastTimingPoint.BeatLength, lastActuationTime.Value, BeatSyncedContainer.MISTIMED_ALLOWANCE)); + } } [Test] @@ -132,10 +143,16 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("bpm is from beatmap", () => lastBpm != null && Precision.AlmostEquals(lastBpm.Value, 60)); } - private class BeatContainer : BeatSyncedContainer + private class TestBeatSyncedContainer : BeatSyncedContainer { private const int flash_layer_height = 150; + public new bool AllowMistimedEventFiring + { + get => base.AllowMistimedEventFiring; + set => base.AllowMistimedEventFiring = value; + } + private readonly InfoString timingPointCount; private readonly InfoString currentTimingPoint; private readonly InfoString beatCount; @@ -148,7 +165,7 @@ namespace osu.Game.Tests.Visual.UserInterface private readonly Box flashLayer; - public BeatContainer() + public TestBeatSyncedContainer() { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 55f694f17e..78ba716cca 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -1,6 +1,7 @@ // 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.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Audio.Track; @@ -35,6 +36,19 @@ namespace osu.Game.Graphics.Containers /// protected double EarlyActivationMilliseconds; + /// + /// While this container automatically applied an animation delay (meaning any animations inside a implementation will + /// always be correctly timed), the event itself can potentially fire away from the related beat. + /// + /// By setting this to false, cases where the event is to be fired more than from the related beat will be skipped. + /// + protected bool AllowMistimedEventFiring = true; + + /// + /// The maximum deviance from the actual beat that an can fire when is set to false. + /// + public const double MISTIMED_ALLOWANCE = 16; + /// /// The time in milliseconds until the next beat. /// @@ -145,8 +159,13 @@ namespace osu.Game.Graphics.Containers if (timingPoint == lastTimingPoint && beatIndex == lastBeat) return; - using (BeginDelayedSequence(-TimeSinceLastBeat)) - OnNewBeat(beatIndex, timingPoint, effectPoint, track?.CurrentAmplitudes ?? ChannelAmplitudes.Empty); + // as this event is sometimes used for sound triggers where `BeginDelayedSequence` has no effect, avoid firing it if too far away from the beat. + // this can happen after a seek operation. + if (AllowMistimedEventFiring || Math.Abs(TimeSinceLastBeat) < MISTIMED_ALLOWANCE) + { + using (BeginDelayedSequence(-TimeSinceLastBeat)) + OnNewBeat(beatIndex, timingPoint, effectPoint, track?.CurrentAmplitudes ?? ChannelAmplitudes.Empty); + } lastBeat = beatIndex; lastTimingPoint = timingPoint; From c38590f1ff11469e024fa94b56837b8a3b92a0bc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 16:42:07 +0900 Subject: [PATCH 0400/2442] Use a slightly more appropriate metronome sound --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 8f9ff81808..3e08cfcd7a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -353,23 +353,24 @@ namespace osu.Game.Rulesets.Osu.Mods Divisor = 1; } + [BackgroundDependencyLoader] + private void load() + { + InternalChildren = new Drawable[] + { + sample = new PausableSkinnableSound(new SampleInfo("Gameplay/catch-banana")) + }; + } + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) { base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); if (!IsBeatSyncedWithTrack) return; + sample.Frequency.Value = beatIndex % (int)timingPoint.TimeSignature == 0 ? 1 : 0.5f; sample?.Play(); } - - [BackgroundDependencyLoader] - private void load() - { - InternalChildren = new Drawable[] - { - sample = new PausableSkinnableSound(new SampleInfo("Gameplay/nightcore-hat")) // todo: use another sample - }; - } } #endregion From ea87869753a7b99a94f1b9fbb1e3a42a5bacc4a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 16:50:55 +0900 Subject: [PATCH 0401/2442] Fix metronome playing during intro time --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 3e08cfcd7a..5fa3bc4520 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -341,15 +341,18 @@ namespace osu.Game.Rulesets.Osu.Mods public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { - drawableRuleset.Overlays.Add(new TargetBeatContainer()); + drawableRuleset.Overlays.Add(new TargetBeatContainer(drawableRuleset.Beatmap.HitObjects.First().StartTime)); } public class TargetBeatContainer : BeatSyncedContainer { + private readonly double firstHitTime; + private PausableSkinnableSound sample; - public TargetBeatContainer() + public TargetBeatContainer(double firstHitTime) { + this.firstHitTime = firstHitTime; Divisor = 1; } @@ -368,8 +371,15 @@ namespace osu.Game.Rulesets.Osu.Mods if (!IsBeatSyncedWithTrack) return; - sample.Frequency.Value = beatIndex % (int)timingPoint.TimeSignature == 0 ? 1 : 0.5f; - sample?.Play(); + int timeSignature = (int)timingPoint.TimeSignature; + + // play metronome from one measure before the first object. + // TODO: Use BeatSyncClock from https://github.com/ppy/osu/pull/13894. + if (Clock.CurrentTime < firstHitTime - timingPoint.BeatLength * timeSignature) + return; + + sample.Frequency.Value = beatIndex % timeSignature == 0 ? 1 : 0.5f; + sample.Play(); } } From efdc8fa8a6cfdebabf5b97ab663e910c9eb3d0f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Jul 2021 19:08:12 +0900 Subject: [PATCH 0402/2442] Fix incorrect step name Co-authored-by: Henry Lin --- .../Visual/UserInterface/TestSceneBeatSyncedContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs index 2c5433e4aa..6bd5c7bcd8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs @@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.UserInterface double? lastActuationTime = null; TimingControlPoint lastTimingPoint = null; - AddStep("set mistimed to disallow", () => beatContainer.AllowMistimedEventFiring = allowMistimed); + AddStep($"set mistimed to {(allowMistimed ? "allowed" : "disallowed")}", () => beatContainer.AllowMistimedEventFiring = allowMistimed); AddStep("bind event", () => { From fa8e5013c5b51487230d4fd11d7b96f73712bb3e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 16 Jul 2021 18:22:34 +0900 Subject: [PATCH 0403/2442] Adjust mania speed range --- osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index e497646a13..13b107457e 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -33,12 +33,12 @@ namespace osu.Game.Rulesets.Mania.UI /// /// The minimum time range. This occurs at a of 40. /// - public const double MIN_TIME_RANGE = 340; + public const double MIN_TIME_RANGE = 290; /// /// The maximum time range. This occurs at a of 1. /// - public const double MAX_TIME_RANGE = 13720; + public const double MAX_TIME_RANGE = 11485; protected new ManiaPlayfield Playfield => (ManiaPlayfield)base.Playfield; From 8541e73fc1675742c7e720f988a93706662e9315 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 16 Jul 2021 16:26:15 +0700 Subject: [PATCH 0404/2442] use text flow in markdown image caption --- .../Overlays/Wiki/Markdown/WikiMarkdownImageBlock.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImageBlock.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImageBlock.cs index 1a4f6087c7..501e00bc00 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImageBlock.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImageBlock.cs @@ -14,7 +14,7 @@ namespace osu.Game.Overlays.Wiki.Markdown public class WikiMarkdownImageBlock : FillFlowContainer { [Resolved] - private IMarkdownTextComponent parentTextComponent { get; set; } + private IMarkdownTextFlowComponent parentFlowComponent { get; set; } private readonly LinkInline linkInline; @@ -31,16 +31,20 @@ namespace osu.Game.Overlays.Wiki.Markdown [BackgroundDependencyLoader] private void load() { + MarkdownTextFlowContainer textFlow; + Children = new Drawable[] { new BlockMarkdownImage(linkInline), - parentTextComponent.CreateSpriteText().With(t => + textFlow = parentFlowComponent.CreateTextFlow().With(t => { - t.Text = linkInline.Title; t.Anchor = Anchor.TopCentre; t.Origin = Anchor.TopCentre; + t.TextAnchor = Anchor.TopCentre; }), }; + + textFlow.AddText(linkInline.Title); } private class BlockMarkdownImage : WikiMarkdownImage From 3ac58d6838c573bc9de9fe739e471f99e08bce59 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 16 Jul 2021 19:32:31 +0900 Subject: [PATCH 0405/2442] Fix min/max values not being passed to inner time range --- osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index 13b107457e..614a7b00c7 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Mania.UI protected override ScrollVisualisationMethod VisualisationMethod => scrollMethod; private readonly Bindable configDirection = new Bindable(); - private readonly Bindable configTimeRange = new BindableDouble(); + private readonly BindableDouble configTimeRange = new BindableDouble(); // Stores the current speed adjustment active in gameplay. private readonly Track speedAdjustmentTrack = new TrackVirtual(0); @@ -103,6 +103,8 @@ namespace osu.Game.Rulesets.Mania.UI configDirection.BindValueChanged(direction => Direction.Value = (ScrollingDirection)direction.NewValue, true); Config.BindWith(ManiaRulesetSetting.ScrollTime, configTimeRange); + TimeRange.MinValue = configTimeRange.MinValue; + TimeRange.MaxValue = configTimeRange.MaxValue; } protected override void AdjustScrollSpeed(int amount) From 05234d6c30c76b2a768bb8b63ded2c958a44ed3c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 16 Jul 2021 19:32:39 +0900 Subject: [PATCH 0406/2442] Fix mania hitobjects not appearing early enough --- .../Objects/Drawables/DrawableManiaHitObject.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index 3ec68bfb56..5aff4e200b 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -22,6 +22,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables protected readonly IBindable Direction = new Bindable(); + // Leaving the default (10s) makes hitobjects not appear, as this offset is used for the initial state transforms. + // Calculated as DrawableManiaRuleset.MAX_TIME_RANGE + some additional allowance for velocity < 1. + protected override double InitialLifetimeOffset => 30000; + [Resolved(canBeNull: true)] private ManiaPlayfield playfield { get; set; } From b3d89254a96774ae0095aff468950d6141458247 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Jul 2021 23:03:53 +0900 Subject: [PATCH 0407/2442] Bump `LocalisationAnalyser` for new key generation --- 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 b2f1d6507f..152ba55e08 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -31,7 +31,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 7df7137c8867cd2761dbd59b08b166eba9bed8c6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Jul 2021 23:03:44 +0900 Subject: [PATCH 0408/2442] Add localisation support for remaining input settings sections --- .../Localisation/BindingSettingsStrings.cs | 29 +++++++++ osu.Game/Localisation/CommonStrings.cs | 17 +++++- .../Localisation/TabletSettingsStrings.cs | 59 +++++++++++++++++++ .../Sections/Input/BindingSettings.cs | 7 ++- .../Sections/Input/RotationPresetButtons.cs | 2 +- .../Settings/Sections/Input/TabletSettings.cs | 25 ++++---- 6 files changed, 122 insertions(+), 17 deletions(-) create mode 100644 osu.Game/Localisation/BindingSettingsStrings.cs create mode 100644 osu.Game/Localisation/TabletSettingsStrings.cs diff --git a/osu.Game/Localisation/BindingSettingsStrings.cs b/osu.Game/Localisation/BindingSettingsStrings.cs new file mode 100644 index 0000000000..ad4a650a1f --- /dev/null +++ b/osu.Game/Localisation/BindingSettingsStrings.cs @@ -0,0 +1,29 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class BindingSettingsStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.BindingSettings"; + + /// + /// "Shortcut and gameplay bindings" + /// + public static LocalisableString ShortcutAndGameplayBindings => new TranslatableString(getKey(@"shortcut_and_gameplay_bindings"), @"Shortcut and gameplay bindings"); + + /// + /// "Configure" + /// + public static LocalisableString Configure => new TranslatableString(getKey(@"configure"), @"Configure"); + + /// + /// "change global shortcut keys and gameplay bindings" + /// + public static LocalisableString ChangeBindingsButton => new TranslatableString(getKey(@"change_bindings_button"), @"change global shortcut keys and gameplay bindings"); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} diff --git a/osu.Game/Localisation/CommonStrings.cs b/osu.Game/Localisation/CommonStrings.cs index ad87986554..bf488d2590 100644 --- a/osu.Game/Localisation/CommonStrings.cs +++ b/osu.Game/Localisation/CommonStrings.cs @@ -14,6 +14,21 @@ namespace osu.Game.Localisation /// public static LocalisableString Cancel => new TranslatableString(getKey(@"cancel"), @"Cancel"); + /// + /// "Enabled" + /// + public static LocalisableString Enabled => new TranslatableString(getKey(@"enabled"), @"Enabled"); + + /// + /// "Width" + /// + public static LocalisableString Width => new TranslatableString(getKey(@"width"), @"Width"); + + /// + /// "Height" + /// + public static LocalisableString Height => new TranslatableString(getKey(@"height"), @"Height"); + private static string getKey(string key) => $@"{prefix}:{key}"; } -} +} \ No newline at end of file diff --git a/osu.Game/Localisation/TabletSettingsStrings.cs b/osu.Game/Localisation/TabletSettingsStrings.cs new file mode 100644 index 0000000000..5bdca09e4a --- /dev/null +++ b/osu.Game/Localisation/TabletSettingsStrings.cs @@ -0,0 +1,59 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class TabletSettingsStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.TabletSettings"; + + /// + /// "Tablet" + /// + public static LocalisableString Tablet => new TranslatableString(getKey(@"tablet"), @"Tablet"); + + /// + /// "No tablet detected!" + /// + public static LocalisableString NoTabletDetected => new TranslatableString(getKey(@"no_tablet_detected"), @"No tablet detected!"); + + /// + /// "Reset to full area" + /// + public static LocalisableString ResetToFullArea => new TranslatableString(getKey(@"reset_to_full_area"), @"Reset to full area"); + + /// + /// "Conform to current game aspect ratio" + /// + public static LocalisableString ConformToCurrentGameAspectRatio => new TranslatableString(getKey(@"conform_to_current_game_aspect_ratio"), @"Conform to current game aspect ratio"); + + /// + /// "X Offset" + /// + public static LocalisableString XOffset => new TranslatableString(getKey(@"x_offset"), @"X Offset"); + + /// + /// "Y Offset" + /// + public static LocalisableString YOffset => new TranslatableString(getKey(@"y_offset"), @"Y Offset"); + + /// + /// "Rotation" + /// + public static LocalisableString Rotation => new TranslatableString(getKey(@"rotation"), @"Rotation"); + + /// + /// "Aspect Ratio" + /// + public static LocalisableString AspectRatio => new TranslatableString(getKey(@"aspect_ratio"), @"Aspect Ratio"); + + /// + /// "Lock aspect ratio" + /// + public static LocalisableString LockAspectRatio => new TranslatableString(getKey(@"lock_aspect_ratio"), @"Lock aspect ratio"); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs index e68cd606e5..3227decc46 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/BindingSettings.cs @@ -3,12 +3,13 @@ using osu.Framework.Graphics; using osu.Framework.Localisation; +using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Input { public class BindingSettings : SettingsSubsection { - protected override LocalisableString Header => "Shortcut and gameplay bindings"; + protected override LocalisableString Header => BindingSettingsStrings.ShortcutAndGameplayBindings; public BindingSettings(KeyBindingPanel keyConfig) { @@ -16,8 +17,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input { new SettingsButton { - Text = "Configure", - TooltipText = "change global shortcut keys and gameplay bindings", + Text = BindingSettingsStrings.Configure, + TooltipText = BindingSettingsStrings.ChangeBindingsButton, Action = keyConfig.ToggleVisibility }, }; diff --git a/osu.Game/Overlays/Settings/Sections/Input/RotationPresetButtons.cs b/osu.Game/Overlays/Settings/Sections/Input/RotationPresetButtons.cs index 3e8da9f7d0..26610628d5 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/RotationPresetButtons.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/RotationPresetButtons.cs @@ -36,7 +36,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input RelativeSizeAxes = Axes.X, Height = height, Width = 0.25f, - Text = $"{presetRotation}º", + Text = $@"{presetRotation}º", Action = () => tabletHandler.Rotation.Value = presetRotation, }); } diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index e9ed3378a2..c7342c251d 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -11,6 +11,7 @@ using osu.Framework.Platform; using osu.Framework.Threading; using osu.Game.Graphics.Sprites; using osuTK; +using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Input { @@ -53,7 +54,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input private OsuSpriteText noTabletMessage; - protected override LocalisableString Header => "Tablet"; + protected override LocalisableString Header => TabletSettingsStrings.Tablet; public TabletSettings(ITabletHandler tabletHandler) { @@ -67,14 +68,14 @@ namespace osu.Game.Overlays.Settings.Sections.Input { new SettingsCheckbox { - LabelText = "Enabled", + LabelText = CommonStrings.Enabled, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Current = tabletHandler.Enabled }, noTabletMessage = new OsuSpriteText { - Text = "No tablet detected!", + Text = TabletSettingsStrings.NoTabletDetected, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Padding = new MarginPadding { Horizontal = SettingsPanel.CONTENT_MARGINS } @@ -95,7 +96,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input }, new DangerousSettingsButton { - Text = "Reset to full area", + Text = TabletSettingsStrings.ResetToFullArea, Action = () => { aspectLock.Value = false; @@ -106,7 +107,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input }, new SettingsButton { - Text = "Conform to current game aspect ratio", + Text = TabletSettingsStrings.ConformToCurrentGameAspectRatio, Action = () => { forceAspectRatio((float)host.Window.ClientSize.Width / host.Window.ClientSize.Height); @@ -115,43 +116,43 @@ namespace osu.Game.Overlays.Settings.Sections.Input new SettingsSlider { TransferValueOnCommit = true, - LabelText = "X Offset", + LabelText = TabletSettingsStrings.XOffset, Current = offsetX }, new SettingsSlider { TransferValueOnCommit = true, - LabelText = "Y Offset", + LabelText = TabletSettingsStrings.YOffset, Current = offsetY }, new SettingsSlider { TransferValueOnCommit = true, - LabelText = "Rotation", + LabelText = TabletSettingsStrings.Rotation, Current = rotation }, new RotationPresetButtons(tabletHandler), new SettingsSlider { TransferValueOnCommit = true, - LabelText = "Aspect Ratio", + LabelText = TabletSettingsStrings.AspectRatio, Current = aspectRatio }, new SettingsCheckbox { - LabelText = "Lock aspect ratio", + LabelText = TabletSettingsStrings.LockAspectRatio, Current = aspectLock }, new SettingsSlider { TransferValueOnCommit = true, - LabelText = "Width", + LabelText = CommonStrings.Width, Current = sizeX }, new SettingsSlider { TransferValueOnCommit = true, - LabelText = "Height", + LabelText = CommonStrings.Height, Current = sizeY }, } From 3e8a13bfbfba8ab70203435e0ed51c4e6aca6907 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Fri, 16 Jul 2021 16:16:34 +0200 Subject: [PATCH 0409/2442] Allow interacting with timeline objects outside of drawable bounds --- .../Timeline/TimelineBlueprintContainer.cs | 48 ++++++++++++------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index a642768574..cb733ab17d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -31,18 +31,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [Resolved(CanBeNull = true)] private Timeline timeline { get; set; } - [Resolved] - private OsuColour colours { get; set; } - private DragEvent lastDragEvent; private Bindable placement; private SelectionBlueprint placementBlueprint; - private SelectableAreaBackground backgroundBox; + // We want children to be able to be clicked and dragged + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; - // we only care about checking vertical validity. - // this allows selecting and dragging selections before time=0. - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) + // This drawable itself should still check whether the mouse is over it + private bool shouldHandleInputAt(Vector2 screenSpacePos) { float localY = ToLocalSpace(screenSpacePos).Y; return DrawRectangle.Top <= localY && DrawRectangle.Bottom >= localY; @@ -61,7 +58,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [BackgroundDependencyLoader] private void load() { - AddInternal(backgroundBox = new SelectableAreaBackground + AddInternal(new SelectableAreaBackground { Colour = Color4.Black, Depth = float.MaxValue, @@ -100,16 +97,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override Container> CreateSelectionBlueprintContainer() => new TimelineSelectionBlueprintContainer { RelativeSizeAxes = Axes.Both }; - protected override bool OnHover(HoverEvent e) + protected override bool OnDragStart(DragStartEvent e) { - backgroundBox.FadeColour(colours.BlueLighter, 120, Easing.OutQuint); - return base.OnHover(e); - } + if (!shouldHandleInputAt(e.ScreenSpaceMouseDownPosition)) + return false; - protected override void OnHoverLost(HoverLostEvent e) - { - backgroundBox.FadeColour(Color4.Black, 600, Easing.OutQuint); - base.OnHoverLost(e); + return base.OnDragStart(e); } protected override void OnDrag(DragEvent e) @@ -184,7 +177,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { return new TimelineHitObjectBlueprint(item) { - OnDragHandled = handleScrollViaDrag + OnDragHandled = handleScrollViaDrag, }; } @@ -212,6 +205,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private class SelectableAreaBackground : CompositeDrawable { + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) + { + float localY = ToLocalSpace(screenSpacePos).Y; + return DrawRectangle.Top <= localY && DrawRectangle.Bottom >= localY; + } + [BackgroundDependencyLoader] private void load() { @@ -235,6 +234,21 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } }); } + + [Resolved] + private OsuColour colours { get; set; } + + protected override bool OnHover(HoverEvent e) + { + this.FadeColour(colours.BlueLighter, 120, Easing.OutQuint); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + this.FadeColour(Color4.Black, 600, Easing.OutQuint); + base.OnHoverLost(e); + } } internal class TimelineSelectionHandler : EditorSelectionHandler, IKeyBindingHandler From bda16f0fbc7c818ad22821e31625c8150b80877b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Jul 2021 23:51:31 +0900 Subject: [PATCH 0410/2442] Fix progression lines getting stuck after removing a match in ladder editor screen --- .../Screens/Ladder/Components/DrawableTournamentMatch.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentMatch.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentMatch.cs index 1c805bb42e..6937c69dbf 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentMatch.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentMatch.cs @@ -303,6 +303,15 @@ namespace osu.Game.Tournament.Screens.Ladder.Components Match.LosersProgression.Value = null; ladderInfo.Matches.Remove(Match); + + foreach (var m in ladderInfo.Matches) + { + if (m.Progression.Value == Match) + m.Progression.Value = null; + + if (m.LosersProgression.Value == Match) + m.LosersProgression.Value = null; + } } } } From e35cff99c79490faa9d7789aa84031763c6dc4e3 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Fri, 16 Jul 2021 17:21:43 +0200 Subject: [PATCH 0411/2442] Pass on mouseDown input to timeline if no selection modification is made with that input --- .../Timeline/TimelineBlueprintContainer.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index cb733ab17d..abd7f5923c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -97,6 +97,19 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override Container> CreateSelectionBlueprintContainer() => new TimelineSelectionBlueprintContainer { RelativeSizeAxes = Axes.Both }; + protected override bool OnMouseDown(MouseDownEvent e) + { + int selectionCount = SelectedItems.Count; + + // We let BlueprintContainer attempt a HitObject selection + // If it fails, we'll pass it this input back to the timeline so it can be dragged + // We know it failed if the selection count is unchanged after the selection attempt + if (base.OnMouseDown(e) && selectionCount != SelectedItems.Count) + return true; + + return false; + } + protected override bool OnDragStart(DragStartEvent e) { if (!shouldHandleInputAt(e.ScreenSpaceMouseDownPosition)) From 83ebbb7f8e990fe78f2311ad9d75970959979f9a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 00:21:58 +0900 Subject: [PATCH 0412/2442] Allow the schedule screen to show even when a current match is not selected --- .../Screens/TestSceneScheduleScreen.cs | 6 + .../Screens/Schedule/ScheduleScreen.cs | 106 +++++++++--------- 2 files changed, 60 insertions(+), 52 deletions(-) diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneScheduleScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneScheduleScreen.cs index 0da8d1eb4a..bd1bacd549 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneScheduleScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneScheduleScreen.cs @@ -28,6 +28,12 @@ namespace osu.Game.Tournament.Tests.Screens setMatchDate(TimeSpan.FromHours(3)); } + [Test] + public void TestNoCurrentMatch() + { + AddStep("Set null current match", () => Ladder.CurrentMatch.Value = null); + } + private void setMatchDate(TimeSpan relativeTime) // Humanizer cannot handle negative timespans. => AddStep($"start time is {relativeTime}", () => diff --git a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs index c1d8c8ddd3..e08be65465 100644 --- a/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs +++ b/osu.Game.Tournament/Screens/Schedule/ScheduleScreen.cs @@ -96,19 +96,18 @@ namespace osu.Game.Tournament.Screens.Schedule } }, }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); - currentMatch.BindValueChanged(matchChanged); currentMatch.BindTo(ladder.CurrentMatch); + currentMatch.BindValueChanged(matchChanged, true); } private void matchChanged(ValueChangedEvent match) { - if (match.NewValue == null) - { - mainContainer.Clear(); - return; - } - var upcoming = ladder.Matches.Where(p => !p.Completed.Value && p.Team1.Value != null && p.Team2.Value != null && Math.Abs(p.Date.Value.DayOfYear - DateTimeOffset.UtcNow.DayOfYear) < 4); var conditionals = ladder .Matches.Where(p => !p.Completed.Value && (p.Team1.Value == null || p.Team2.Value == null) && Math.Abs(p.Date.Value.DayOfYear - DateTimeOffset.UtcNow.DayOfYear) < 4) @@ -117,6 +116,8 @@ namespace osu.Game.Tournament.Screens.Schedule upcoming = upcoming.Concat(conditionals); upcoming = upcoming.OrderBy(p => p.Date.Value).Take(8); + ScheduleContainer comingUpNext; + mainContainer.Child = new FillFlowContainer { RelativeSizeAxes = Axes.Both, @@ -153,57 +154,58 @@ namespace osu.Game.Tournament.Screens.Schedule } } }, - new ScheduleContainer("coming up next") + comingUpNext = new ScheduleContainer("coming up next") { RelativeSizeAxes = Axes.Both, Height = 0.25f, - Children = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(30), - Children = new Drawable[] - { - new ScheduleMatch(match.NewValue, false) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - }, - new TournamentSpriteTextWithBackground(match.NewValue.Round.Value?.Name.Value) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Scale = new Vector2(0.5f) - }, - new TournamentSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Text = match.NewValue.Team1.Value?.FullName + " vs " + match.NewValue.Team2.Value?.FullName, - Font = OsuFont.Torus.With(size: 24, weight: FontWeight.SemiBold) - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Children = new Drawable[] - { - new ScheduleMatchDate(match.NewValue.Date.Value) - { - Font = OsuFont.Torus.With(size: 24, weight: FontWeight.Regular) - } - } - }, - } - }, - } } } }; + + if (match.NewValue != null) + { + comingUpNext.Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(30), + Children = new Drawable[] + { + new ScheduleMatch(match.NewValue, false) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + new TournamentSpriteTextWithBackground(match.NewValue.Round.Value?.Name.Value) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Scale = new Vector2(0.5f) + }, + new TournamentSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Text = match.NewValue.Team1.Value?.FullName + " vs " + match.NewValue.Team2.Value?.FullName, + Font = OsuFont.Torus.With(size: 24, weight: FontWeight.SemiBold) + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Children = new Drawable[] + { + new ScheduleMatchDate(match.NewValue.Date.Value) + { + Font = OsuFont.Torus.With(size: 24, weight: FontWeight.Regular) + } + } + }, + } + }; + } } public class ScheduleMatch : DrawableTournamentMatch From 71f74f0e9860bcca7054df6bf40ec67cabdd3e5e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Jul 2021 23:34:28 +0900 Subject: [PATCH 0413/2442] Add warning message to screens which require a current match to be selected --- .../Screens/MapPool/MapPoolScreen.cs | 40 +++++++++---------- .../Screens/TournamentMatchScreen.cs | 34 ++++++++++++++++ 2 files changed, 54 insertions(+), 20 deletions(-) create mode 100644 osu.Game.Tournament/Screens/TournamentMatchScreen.cs diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index 2c4fed8d86..d4292c5492 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -21,12 +21,10 @@ using osuTK.Input; namespace osu.Game.Tournament.Screens.MapPool { - public class MapPoolScreen : TournamentScreen + public class MapPoolScreen : TournamentMatchScreen { private readonly FillFlowContainer> mapFlows; - private readonly Bindable currentMatch = new Bindable(); - [Resolved(canBeNull: true)] private TournamentSceneManager sceneManager { get; set; } @@ -96,7 +94,7 @@ namespace osu.Game.Tournament.Screens.MapPool Action = reset }, new ControlPanel.Spacer(), - } + }, } }; } @@ -104,15 +102,12 @@ namespace osu.Game.Tournament.Screens.MapPool [BackgroundDependencyLoader] private void load(MatchIPCInfo ipc) { - currentMatch.BindValueChanged(matchChanged); - currentMatch.BindTo(LadderInfo.CurrentMatch); - ipc.Beatmap.BindValueChanged(beatmapChanged); } private void beatmapChanged(ValueChangedEvent beatmap) { - if (currentMatch.Value == null || currentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) < 2) + if (CurrentMatch.Value == null || CurrentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) < 2) return; // if bans have already been placed, beatmap changes result in a selection being made autoamtically @@ -137,12 +132,12 @@ namespace osu.Game.Tournament.Screens.MapPool { const TeamColour roll_winner = TeamColour.Red; //todo: draw from match - var nextColour = (currentMatch.Value.PicksBans.LastOrDefault()?.Team ?? roll_winner) == TeamColour.Red ? TeamColour.Blue : TeamColour.Red; + var nextColour = (CurrentMatch.Value.PicksBans.LastOrDefault()?.Team ?? roll_winner) == TeamColour.Red ? TeamColour.Blue : TeamColour.Red; - if (pickType == ChoiceType.Ban && currentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) >= 2) + if (pickType == ChoiceType.Ban && CurrentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) >= 2) setMode(pickColour, ChoiceType.Pick); else - setMode(nextColour, currentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) >= 2 ? ChoiceType.Pick : ChoiceType.Ban); + setMode(nextColour, CurrentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) >= 2 ? ChoiceType.Pick : ChoiceType.Ban); } protected override bool OnMouseDown(MouseDownEvent e) @@ -156,11 +151,11 @@ namespace osu.Game.Tournament.Screens.MapPool addForBeatmap(map.Beatmap.OnlineBeatmapID.Value); else { - var existing = currentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == map.Beatmap.OnlineBeatmapID); + var existing = CurrentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == map.Beatmap.OnlineBeatmapID); if (existing != null) { - currentMatch.Value.PicksBans.Remove(existing); + CurrentMatch.Value.PicksBans.Remove(existing); setNextMode(); } } @@ -173,7 +168,7 @@ namespace osu.Game.Tournament.Screens.MapPool private void reset() { - currentMatch.Value.PicksBans.Clear(); + CurrentMatch.Value.PicksBans.Clear(); setNextMode(); } @@ -181,18 +176,18 @@ namespace osu.Game.Tournament.Screens.MapPool private void addForBeatmap(int beatmapId) { - if (currentMatch.Value == null) + if (CurrentMatch.Value == null) return; - if (currentMatch.Value.Round.Value.Beatmaps.All(b => b.BeatmapInfo.OnlineBeatmapID != beatmapId)) + if (CurrentMatch.Value.Round.Value.Beatmaps.All(b => b.BeatmapInfo.OnlineBeatmapID != beatmapId)) // don't attempt to add if the beatmap isn't in our pool return; - if (currentMatch.Value.PicksBans.Any(p => p.BeatmapID == beatmapId)) + if (CurrentMatch.Value.PicksBans.Any(p => p.BeatmapID == beatmapId)) // don't attempt to add if already exists. return; - currentMatch.Value.PicksBans.Add(new BeatmapChoice + CurrentMatch.Value.PicksBans.Add(new BeatmapChoice { Team = pickColour, Type = pickType, @@ -201,17 +196,22 @@ namespace osu.Game.Tournament.Screens.MapPool setNextMode(); - if (pickType == ChoiceType.Pick && currentMatch.Value.PicksBans.Any(i => i.Type == ChoiceType.Pick)) + if (pickType == ChoiceType.Pick && CurrentMatch.Value.PicksBans.Any(i => i.Type == ChoiceType.Pick)) { scheduledChange?.Cancel(); scheduledChange = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(GameplayScreen)); }, 10000); } } - private void matchChanged(ValueChangedEvent match) + protected override void CurrentMatchChanged(ValueChangedEvent match) { + base.CurrentMatchChanged(match); + mapFlows.Clear(); + if (match.NewValue == null) + return; + int totalRows = 0; if (match.NewValue.Round.Value != null) diff --git a/osu.Game.Tournament/Screens/TournamentMatchScreen.cs b/osu.Game.Tournament/Screens/TournamentMatchScreen.cs new file mode 100644 index 0000000000..5f00036653 --- /dev/null +++ b/osu.Game.Tournament/Screens/TournamentMatchScreen.cs @@ -0,0 +1,34 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osu.Game.Tournament.Models; + +namespace osu.Game.Tournament.Screens +{ + public abstract class TournamentMatchScreen : TournamentScreen + { + protected readonly Bindable CurrentMatch = new Bindable(); + private WarningBox noMatchWarning; + + protected override void LoadComplete() + { + base.LoadComplete(); + + CurrentMatch.BindTo(LadderInfo.CurrentMatch); + CurrentMatch.BindValueChanged(CurrentMatchChanged, true); + } + + protected virtual void CurrentMatchChanged(ValueChangedEvent match) + { + if (match.NewValue == null) + { + AddInternal(noMatchWarning = new WarningBox("Choose a match first from the brackets screen")); + return; + } + + noMatchWarning?.Expire(); + noMatchWarning = null; + } + } +} From 0a13e033eaa6907126fa852eb7dc71a8e587bc12 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Jul 2021 23:59:16 +0900 Subject: [PATCH 0414/2442] Move height warning to bottom of screen to avoid overlap --- osu.Game.Tournament/TournamentGame.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index 87e23e3404..cd0e601a2f 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -97,7 +97,12 @@ namespace osu.Game.Tournament }, } }, - heightWarning = new WarningBox("Please make the window wider"), + heightWarning = new WarningBox("Please make the window wider") + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Margin = new MarginPadding(20), + }, new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both, From e8595871ded0ad53f3f3caf6f79f00a952523a5d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 00:14:48 +0900 Subject: [PATCH 0415/2442] Update remaining screens to also show the warning message --- .../Screens/BeatmapInfoScreen.cs | 2 +- .../Screens/Gameplay/GameplayScreen.cs | 29 ++++++++++--------- .../Screens/Showcase/ShowcaseScreen.cs | 8 +++++ .../Screens/TeamIntro/SeedingScreen.cs | 22 +++++++------- .../Screens/TeamIntro/TeamIntroScreen.cs | 16 ++++------ .../Screens/TeamWin/TeamWinScreen.cs | 19 ++++++------ 6 files changed, 52 insertions(+), 44 deletions(-) diff --git a/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs index 0a3163ef43..50498304ca 100644 --- a/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs +++ b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs @@ -11,7 +11,7 @@ using osu.Game.Tournament.IPC; namespace osu.Game.Tournament.Screens { - public abstract class BeatmapInfoScreen : TournamentScreen + public abstract class BeatmapInfoScreen : TournamentMatchScreen { protected readonly SongBar SongBar; diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 6e4c6784c8..f61506d7f2 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -24,8 +24,6 @@ namespace osu.Game.Tournament.Screens.Gameplay { private readonly BindableBool warmup = new BindableBool(); - private readonly Bindable currentMatch = new Bindable(); - public readonly Bindable State = new Bindable(); private OsuButton warmupButton; private MatchIPCInfo ipc; @@ -131,14 +129,6 @@ namespace osu.Game.Tournament.Screens.Gameplay ladder.ChromaKeyWidth.BindValueChanged(width => chroma.Width = width.NewValue, true); - currentMatch.BindValueChanged(m => - { - warmup.Value = m.NewValue.Team1Score.Value + m.NewValue.Team2Score.Value == 0; - scheduledOperation?.Cancel(); - }); - - currentMatch.BindTo(ladder.CurrentMatch); - warmup.BindValueChanged(w => { warmupButton.Alpha = !w.NewValue ? 0.5f : 1; @@ -146,6 +136,17 @@ namespace osu.Game.Tournament.Screens.Gameplay }, true); } + protected override void CurrentMatchChanged(ValueChangedEvent match) + { + base.CurrentMatchChanged(match); + + if (match.NewValue == null) + return; + + warmup.Value = match.NewValue.Team1Score.Value + match.NewValue.Team2Score.Value == 0; + scheduledOperation?.Cancel(); + } + private ScheduledDelegate scheduledOperation; private MatchScoreDisplay scoreDisplay; @@ -161,9 +162,9 @@ namespace osu.Game.Tournament.Screens.Gameplay if (warmup.Value) return; if (ipc.Score1.Value > ipc.Score2.Value) - currentMatch.Value.Team1Score.Value++; + CurrentMatch.Value.Team1Score.Value++; else - currentMatch.Value.Team2Score.Value++; + CurrentMatch.Value.Team2Score.Value++; } scheduledOperation?.Cancel(); @@ -198,9 +199,9 @@ namespace osu.Game.Tournament.Screens.Gameplay // we should automatically proceed after a short delay if (lastState == TourneyState.Ranking && !warmup.Value) { - if (currentMatch.Value?.Completed.Value == true) + if (CurrentMatch.Value?.Completed.Value == true) scheduledOperation = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(TeamWinScreen)); }, delay_before_progression); - else if (currentMatch.Value?.Completed.Value == false) + else if (CurrentMatch.Value?.Completed.Value == false) scheduledOperation = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(MapPoolScreen)); }, delay_before_progression); } diff --git a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs index 9785b7e647..32d458e191 100644 --- a/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs +++ b/osu.Game.Tournament/Screens/Showcase/ShowcaseScreen.cs @@ -2,10 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Tournament.Components; using osu.Framework.Graphics.Shapes; +using osu.Game.Tournament.Models; using osuTK.Graphics; namespace osu.Game.Tournament.Screens.Showcase @@ -39,5 +41,11 @@ namespace osu.Game.Tournament.Screens.Showcase } }); } + + protected override void CurrentMatchChanged(ValueChangedEvent match) + { + // showcase screen doesn't care about a match being selected. + // base call intentionally omitted to not show match warning. + } } } diff --git a/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs index 4f66d89b7f..71aed69738 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs @@ -18,12 +18,10 @@ using osuTK; namespace osu.Game.Tournament.Screens.TeamIntro { - public class SeedingScreen : TournamentScreen, IProvideVideo + public class SeedingScreen : TournamentMatchScreen, IProvideVideo { private Container mainContainer; - private readonly Bindable currentMatch = new Bindable(); - private readonly Bindable currentTeam = new Bindable(); [BackgroundDependencyLoader] @@ -50,13 +48,13 @@ namespace osu.Game.Tournament.Screens.TeamIntro { RelativeSizeAxes = Axes.X, Text = "Show first team", - Action = () => currentTeam.Value = currentMatch.Value.Team1.Value, + Action = () => currentTeam.Value = CurrentMatch.Value.Team1.Value, }, new TourneyButton { RelativeSizeAxes = Axes.X, Text = "Show second team", - Action = () => currentTeam.Value = currentMatch.Value.Team2.Value, + Action = () => currentTeam.Value = CurrentMatch.Value.Team2.Value, }, new SettingsTeamDropdown(LadderInfo.Teams) { @@ -67,9 +65,6 @@ namespace osu.Game.Tournament.Screens.TeamIntro } }; - currentMatch.BindValueChanged(matchChanged); - currentMatch.BindTo(LadderInfo.CurrentMatch); - currentTeam.BindValueChanged(teamChanged, true); } @@ -84,8 +79,15 @@ namespace osu.Game.Tournament.Screens.TeamIntro showTeam(team.NewValue); } - private void matchChanged(ValueChangedEvent match) => - currentTeam.Value = currentMatch.Value.Team1.Value; + protected override void CurrentMatchChanged(ValueChangedEvent match) + { + base.CurrentMatchChanged(match); + + if (match.NewValue == null) + return; + + currentTeam.Value = match.NewValue.Team1.Value; + } private void showTeam(TournamentTeam team) { diff --git a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs index 6c2848897b..74957cbca5 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/TeamIntroScreen.cs @@ -12,12 +12,10 @@ using osuTK; namespace osu.Game.Tournament.Screens.TeamIntro { - public class TeamIntroScreen : TournamentScreen, IProvideVideo + public class TeamIntroScreen : TournamentMatchScreen, IProvideVideo { private Container mainContainer; - private readonly Bindable currentMatch = new Bindable(); - [BackgroundDependencyLoader] private void load(Storage storage) { @@ -35,18 +33,16 @@ namespace osu.Game.Tournament.Screens.TeamIntro RelativeSizeAxes = Axes.Both, } }; - - currentMatch.BindValueChanged(matchChanged); - currentMatch.BindTo(LadderInfo.CurrentMatch); } - private void matchChanged(ValueChangedEvent match) + protected override void CurrentMatchChanged(ValueChangedEvent match) { + base.CurrentMatchChanged(match); + + mainContainer.Clear(); + if (match.NewValue == null) - { - mainContainer.Clear(); return; - } const float y_flag_offset = 292; diff --git a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs index 7ca262a2e8..ebe2908b74 100644 --- a/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs +++ b/osu.Game.Tournament/Screens/TeamWin/TeamWinScreen.cs @@ -13,11 +13,10 @@ using osuTK; namespace osu.Game.Tournament.Screens.TeamWin { - public class TeamWinScreen : TournamentScreen, IProvideVideo + public class TeamWinScreen : TournamentMatchScreen, IProvideVideo { private Container mainContainer; - private readonly Bindable currentMatch = new Bindable(); private readonly Bindable currentCompleted = new Bindable(); private TourneyVideo blueWinVideo; @@ -48,17 +47,19 @@ namespace osu.Game.Tournament.Screens.TeamWin } }; - currentMatch.BindValueChanged(matchChanged); - currentMatch.BindTo(ladder.CurrentMatch); - currentCompleted.BindValueChanged(_ => update()); } - private void matchChanged(ValueChangedEvent match) + protected override void CurrentMatchChanged(ValueChangedEvent match) { - currentCompleted.UnbindBindings(); - currentCompleted.BindTo(match.NewValue.Completed); + base.CurrentMatchChanged(match); + currentCompleted.UnbindBindings(); + + if (match.NewValue == null) + return; + + currentCompleted.BindTo(match.NewValue.Completed); update(); } @@ -66,7 +67,7 @@ namespace osu.Game.Tournament.Screens.TeamWin private void update() => Schedule(() => { - var match = currentMatch.Value; + var match = CurrentMatch.Value; if (match.Winner == null) { From 7a671754f2c067b38ab8b4515fbd718cbcf8228e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 02:29:31 +0900 Subject: [PATCH 0416/2442] Change `RadioButton`'s `object` to a `string` --- .../Edit/Components/RadioButtons/RadioButton.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs b/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs index dcf5f8a788..c3ffd8add0 100644 --- a/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs +++ b/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs @@ -17,7 +17,7 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons /// /// The item related to this button. /// - public object Item; + public string Item; /// /// A function which creates a drawable icon to represent this item. If null, a sane default should be used. @@ -26,7 +26,7 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons private readonly Action action; - public RadioButton(object item, Action action, Func createIcon = null) + public RadioButton(string item, Action action, Func createIcon = null) { Item = item; CreateIcon = createIcon; @@ -34,13 +34,6 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons Selected = new BindableBool(); } - public RadioButton(string item) - : this(item, null) - { - Item = item; - action = null; - } - /// /// Selects this . /// From 3ae5f6707abe50ffa9c4ba9bae2a359a0dc49468 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 02:30:00 +0900 Subject: [PATCH 0417/2442] Expose whether an `EditorBeatmap` has timing present or not via bindable --- osu.Game/Screens/Edit/EditorBeatmap.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index be53abbd55..7de98e5e85 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -46,12 +46,22 @@ namespace osu.Game.Screens.Edit public readonly IBeatmap PlayableBeatmap; + /// + /// Whether at least one timing control point is present and providing timing information. + /// + public IBindable HasTiming => hasTiming; + + private readonly Bindable hasTiming = new Bindable(); + [CanBeNull] public readonly ISkin BeatmapSkin; [Resolved] private BindableBeatDivisor beatDivisor { get; set; } + [Resolved] + private EditorClock editorClock { get; set; } + private readonly IBeatmapProcessor beatmapProcessor; private readonly Dictionary> startTimeBindables = new Dictionary>(); @@ -238,6 +248,8 @@ namespace osu.Game.Screens.Edit if (batchPendingUpdates.Count > 0) UpdateState(); + + hasTiming.Value = !ReferenceEquals(ControlPointInfo.TimingPointAt(editorClock.CurrentTime), TimingControlPoint.DEFAULT); } protected override void UpdateState() From eac9b1ec7e32eee5998cf4ff95cc464e04375eb7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 02:30:13 +0900 Subject: [PATCH 0418/2442] Disable toolbox composition buttons when beatmap is not timed --- .../Editing/TestSceneHitObjectComposer.cs | 105 ++++++++++++++---- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 3 +- .../RadioButtons/DrawableRadioButton.cs | 27 +++-- 3 files changed, 103 insertions(+), 32 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs index 7ca24346aa..f75c976f56 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs @@ -2,17 +2,24 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Timing; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Edit; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Edit; +using osu.Game.Screens.Edit.Components.RadioButtons; +using osu.Game.Screens.Edit.Compose.Components; using osuTK; namespace osu.Game.Tests.Visual.Editing @@ -20,37 +27,89 @@ namespace osu.Game.Tests.Visual.Editing [TestFixture] public class TestSceneHitObjectComposer : EditorClockTestScene { - [BackgroundDependencyLoader] - private void load() + private OsuHitObjectComposer hitObjectComposer; + private EditorBeatmapContainer editorBeatmapContainer; + + private EditorBeatmap editorBeatmap => editorBeatmapContainer.EditorBeatmap; + + [SetUpSteps] + public void SetUpSteps() { - Beatmap.Value = CreateWorkingBeatmap(new Beatmap + AddStep("create beatmap", () => { - HitObjects = new List + Beatmap.Value = CreateWorkingBeatmap(new Beatmap { - new HitCircle { Position = new Vector2(256, 192), Scale = 0.5f }, - new HitCircle { Position = new Vector2(344, 148), Scale = 0.5f }, - new Slider + HitObjects = new List { - Position = new Vector2(128, 256), - Path = new SliderPath(PathType.Linear, new[] + new HitCircle { Position = new Vector2(256, 192), Scale = 0.5f }, + new HitCircle { Position = new Vector2(344, 148), Scale = 0.5f }, + new Slider { - Vector2.Zero, - new Vector2(216, 0), - }), - Scale = 0.5f, - } - }, + Position = new Vector2(128, 256), + Path = new SliderPath(PathType.Linear, new[] + { + Vector2.Zero, + new Vector2(216, 0), + }), + Scale = 0.5f, + } + }, + }); }); - var editorBeatmap = new EditorBeatmap(Beatmap.Value.GetPlayableBeatmap(new OsuRuleset().RulesetInfo)); + AddStep("Create composer", () => + { + Child = editorBeatmapContainer = new EditorBeatmapContainer(Beatmap.Value) + { + Child = hitObjectComposer = new OsuHitObjectComposer(new OsuRuleset()) + }; + }); + } - var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; - Dependencies.CacheAs(clock); - Dependencies.CacheAs(clock); - Dependencies.CacheAs(editorBeatmap); - Dependencies.CacheAs(editorBeatmap); + [Test] + public void TestPlacementOnlyWorksWithTiming() + { + AddStep("clear all control points", () => editorBeatmap.ControlPointInfo.Clear()); - Child = new OsuHitObjectComposer(new OsuRuleset()); + AddAssert("Tool is selection", () => hitObjectComposer.ChildrenOfType().First().CurrentTool is SelectTool); + AddAssert("Hitcircle button not clickable", () => !hitObjectComposer.ChildrenOfType().First(d => d.Button.Item == "HitCircle").Enabled.Value); + AddStep("Add timing point", () => editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint())); + AddAssert("Hitcircle button is clickable", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Item == "HitCircle").Enabled.Value); + AddStep("Change to hitcircle", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Item == "HitCircle").Click()); + AddAssert("Tool changed", () => hitObjectComposer.ChildrenOfType().First().CurrentTool is HitCircleCompositionTool); + } + + public class EditorBeatmapContainer : Container + { + private readonly WorkingBeatmap working; + + public EditorBeatmap EditorBeatmap { get; private set; } + + public EditorBeatmapContainer(WorkingBeatmap working) + { + this.working = working; + + RelativeSizeAxes = Axes.Both; + } + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + + EditorBeatmap = new EditorBeatmap(working.GetPlayableBeatmap(new OsuRuleset().RulesetInfo)); + + dependencies.CacheAs(EditorBeatmap); + dependencies.CacheAs(EditorBeatmap); + + return dependencies; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Add(EditorBeatmap); + } } } } diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index a7005954b2..c6c112bec8 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -219,7 +219,8 @@ namespace osu.Game.Rulesets.Edit if (item != null) { - item.Select(); + if (!item.Selected.Disabled) + item.Select(); return true; } } diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs index 1f608d28fd..79468f0ab0 100644 --- a/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs +++ b/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs @@ -5,9 +5,11 @@ using System; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -16,24 +18,28 @@ using osuTK.Graphics; namespace osu.Game.Screens.Edit.Components.RadioButtons { - public class DrawableRadioButton : OsuButton + public class DrawableRadioButton : OsuButton, IHasTooltip { /// /// Invoked when this has been selected. /// public Action Selected; + public readonly RadioButton Button; + private Color4 defaultBackgroundColour; private Color4 defaultBubbleColour; private Color4 selectedBackgroundColour; private Color4 selectedBubbleColour; private Drawable icon; - private readonly RadioButton button; + + [Resolved(canBeNull: true)] + private EditorBeatmap editorBeatmap { get; set; } public DrawableRadioButton(RadioButton button) { - this.button = button; + this.Button = button; Text = button.Item.ToString(); Action = button.Select; @@ -57,7 +63,7 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons Colour = Color4.Black.Opacity(0.5f) }; - Add(icon = (button.CreateIcon?.Invoke() ?? new Circle()).With(b => + Add(icon = (Button.CreateIcon?.Invoke() ?? new Circle()).With(b => { b.Blending = BlendingParameters.Additive; b.Anchor = Anchor.CentreLeft; @@ -71,13 +77,16 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons { base.LoadComplete(); - button.Selected.ValueChanged += selected => + Button.Selected.ValueChanged += selected => { updateSelectionState(); if (selected.NewValue) - Selected?.Invoke(button); + Selected?.Invoke(Button); }; + editorBeatmap?.HasTiming.BindValueChanged(hasTiming => Button.Selected.Disabled = !hasTiming.NewValue, true); + + Button.Selected.BindDisabledChanged(disabled => Enabled.Value = !disabled, true); updateSelectionState(); } @@ -86,8 +95,8 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons if (!IsLoaded) return; - BackgroundColour = button.Selected.Value ? selectedBackgroundColour : defaultBackgroundColour; - icon.Colour = button.Selected.Value ? selectedBubbleColour : defaultBubbleColour; + BackgroundColour = Button.Selected.Value ? selectedBackgroundColour : defaultBackgroundColour; + icon.Colour = Button.Selected.Value ? selectedBubbleColour : defaultBubbleColour; } protected override SpriteText CreateText() => new OsuSpriteText @@ -97,5 +106,7 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons Anchor = Anchor.CentreLeft, X = 40f }; + + public LocalisableString TooltipText => Enabled.Value ? string.Empty : "Add at least one timing point first!"; } } From 50eed26bd1addd0f929db1b0a55609e7e7cc8b97 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 02:30:49 +0900 Subject: [PATCH 0419/2442] Rename radio button `item` to `label` --- osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs | 6 +++--- .../Edit/Components/RadioButtons/DrawableRadioButton.cs | 4 ++-- .../Screens/Edit/Components/RadioButtons/RadioButton.cs | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs index f75c976f56..67c37413ed 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs @@ -72,10 +72,10 @@ namespace osu.Game.Tests.Visual.Editing AddStep("clear all control points", () => editorBeatmap.ControlPointInfo.Clear()); AddAssert("Tool is selection", () => hitObjectComposer.ChildrenOfType().First().CurrentTool is SelectTool); - AddAssert("Hitcircle button not clickable", () => !hitObjectComposer.ChildrenOfType().First(d => d.Button.Item == "HitCircle").Enabled.Value); + AddAssert("Hitcircle button not clickable", () => !hitObjectComposer.ChildrenOfType().First(d => d.Button.Label == "HitCircle").Enabled.Value); AddStep("Add timing point", () => editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint())); - AddAssert("Hitcircle button is clickable", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Item == "HitCircle").Enabled.Value); - AddStep("Change to hitcircle", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Item == "HitCircle").Click()); + AddAssert("Hitcircle button is clickable", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Label == "HitCircle").Enabled.Value); + AddStep("Change to hitcircle", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Label == "HitCircle").Click()); AddAssert("Tool changed", () => hitObjectComposer.ChildrenOfType().First().CurrentTool is HitCircleCompositionTool); } diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs index 79468f0ab0..bbe77eaa07 100644 --- a/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs +++ b/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs @@ -39,9 +39,9 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons public DrawableRadioButton(RadioButton button) { - this.Button = button; + Button = button; - Text = button.Item.ToString(); + Text = button.Label; Action = button.Select; RelativeSizeAxes = Axes.X; diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs b/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs index c3ffd8add0..ca79dd15d7 100644 --- a/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs +++ b/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs @@ -17,7 +17,7 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons /// /// The item related to this button. /// - public string Item; + public string Label; /// /// A function which creates a drawable icon to represent this item. If null, a sane default should be used. @@ -26,9 +26,9 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons private readonly Action action; - public RadioButton(string item, Action action, Func createIcon = null) + public RadioButton(string label, Action action, Func createIcon = null) { - Item = item; + Label = label; CreateIcon = createIcon; this.action = action; Selected = new BindableBool(); From db4d64effb1f208b528af2ff4d0bd5a1954d86f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 14:29:18 +0900 Subject: [PATCH 0420/2442] Rename incorrect step --- .../Visual/UserInterface/TestSceneBeatSyncedContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs index 6bd5c7bcd8..6b56f339d8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs @@ -140,7 +140,7 @@ namespace osu.Game.Tests.Visual.UserInterface }); AddUntilStep("wait for trigger", () => lastBpm != null); - AddAssert("bpm is from beatmap", () => lastBpm != null && Precision.AlmostEquals(lastBpm.Value, 60)); + AddAssert("bpm is default", () => lastBpm != null && Precision.AlmostEquals(lastBpm.Value, 60)); } private class TestBeatSyncedContainer : BeatSyncedContainer From 23ed77f2c6f10135e3b849e5adf96008372b9cd4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 14:33:02 +0900 Subject: [PATCH 0421/2442] Fix test failure under visual tests due to double firing --- .../UserInterface/TestSceneBeatSyncedContainer.cs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs index 6b56f339d8..e5bcc08924 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs @@ -65,19 +65,16 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep($"set mistimed to {(allowMistimed ? "allowed" : "disallowed")}", () => beatContainer.AllowMistimedEventFiring = allowMistimed); - AddStep("bind event", () => + AddStep("Set time before zero", () => { beatContainer.NewBeat = (i, timingControlPoint, effectControlPoint, channelAmplitudes) => { lastActuationTime = gameplayClockContainer.CurrentTime; lastTimingPoint = timingControlPoint; lastBeatIndex = i; + beatContainer.NewBeat = null; }; - }); - AddStep("Set time before zero", () => - { - lastBeatIndex = null; gameplayClockContainer.Seek(-1000); }); @@ -99,19 +96,14 @@ namespace osu.Game.Tests.Visual.UserInterface int? lastBeatIndex = null; double? lastBpm = null; - AddStep("bind event", () => + AddStep("Set time before zero", () => { beatContainer.NewBeat = (i, timingControlPoint, effectControlPoint, channelAmplitudes) => { lastBeatIndex = i; lastBpm = timingControlPoint.BPM; }; - }); - AddStep("Set time before zero", () => - { - lastBeatIndex = null; - lastBpm = null; gameplayClockContainer.Seek(-1000); }); From 87d3bd4b9389a3582f15ccfbf054dd8b3fe8e01a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 14:35:43 +0900 Subject: [PATCH 0422/2442] Fix time until next beat potentially being exactly zero at point of trigger --- osu.Game/Graphics/Containers/BeatSyncedContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 78ba716cca..6e4901ab1a 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -151,7 +151,7 @@ namespace osu.Game.Graphics.Containers beatIndex--; TimeUntilNextBeat = (timingPoint.Time - currentTrackTime) % beatLength; - if (TimeUntilNextBeat < 0) + if (TimeUntilNextBeat <= 0) TimeUntilNextBeat += beatLength; TimeSinceLastBeat = beatLength - TimeUntilNextBeat; From d609839ff68e27e1e76d12d3f3cec601d8d68ce6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 14:56:10 +0900 Subject: [PATCH 0423/2442] Fix test not working due to popover container being too global --- .../Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs | 5 ++++- osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs index 3ba0f4969a..27fc1dcd51 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs @@ -3,6 +3,9 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.UserInterface; using osu.Framework.Screens; using osu.Framework.Testing; @@ -60,7 +63,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true)); AddStep("select room", () => InputManager.Key(Key.Down)); AddStep("attempt join room", () => InputManager.Key(Key.Enter)); - AddUntilStep("password prompt appeared", () => (passwordEntryPopover = loungeScreen.ChildrenOfType().FirstOrDefault()) != null); + AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null); AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password"); AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().Click()); diff --git a/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs b/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs index 01dd7a25c8..5d2309b88c 100644 --- a/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs +++ b/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Testing.Input; using osu.Game.Graphics.Cursor; @@ -35,8 +36,9 @@ namespace osu.Game.Tests.Visual MenuCursorContainer cursorContainer; CompositeDrawable mainContent = - (cursorContainer = new MenuCursorContainer { RelativeSizeAxes = Axes.Both }) - .WithChild(content = new OsuTooltipContainer(cursorContainer.Cursor) { RelativeSizeAxes = Axes.Both }); + new PopoverContainer { RelativeSizeAxes = Axes.Both } + .WithChild(cursorContainer = new MenuCursorContainer { RelativeSizeAxes = Axes.Both }) + .WithChild(content = new OsuTooltipContainer(cursorContainer.Cursor) { RelativeSizeAxes = Axes.Both }); if (CreateNestedActionContainer) { From c966cb053024e180838a8538ec6d46d0f9eb09bb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 15:04:18 +0900 Subject: [PATCH 0424/2442] Fix dependency lookup failing due to location of `PopoverContainer` --- .../Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 84c20a6acc..951b00376e 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -243,7 +243,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Alpha = 0; } - public Popover GetPopover() => new PasswordEntryPopover(Room); + public Popover GetPopover() => new PasswordEntryPopover(Room) { JoinRequested = lounge.Join }; public MenuItem[] ContextMenuItems => new MenuItem[] { @@ -350,8 +350,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { private readonly Room room; - [Resolved(canBeNull: true)] - private LoungeSubScreen lounge { get; set; } + public Action JoinRequested; public PasswordEntryPopover(Room room) { @@ -379,7 +378,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { Width = 80, Text = "Join Room", - Action = () => lounge?.Join(room, passwordTextbox.Text) + Action = () => JoinRequested?.Invoke(room, passwordTextbox.Text) } } }; From 567a94a28bd90a5082fda0c43a54a38cc1fdbbda Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 15:35:08 +0900 Subject: [PATCH 0425/2442] Remove unused using statements --- .../Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs index 27fc1dcd51..bde9ea89ed 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs @@ -3,9 +3,6 @@ using System.Linq; using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.UserInterface; using osu.Framework.Screens; using osu.Framework.Testing; From 64cf9b702ededf6e54e4908df54630baec3130c7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 17:26:11 +0900 Subject: [PATCH 0426/2442] Fix incorrec nesting of manual input manager test containers --- .../Tests/Visual/OsuManualInputManagerTestScene.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs b/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs index 5d2309b88c..c5e2e67eaf 100644 --- a/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs +++ b/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs @@ -35,10 +35,16 @@ namespace osu.Game.Tests.Visual { MenuCursorContainer cursorContainer; - CompositeDrawable mainContent = - new PopoverContainer { RelativeSizeAxes = Axes.Both } - .WithChild(cursorContainer = new MenuCursorContainer { RelativeSizeAxes = Axes.Both }) - .WithChild(content = new OsuTooltipContainer(cursorContainer.Cursor) { RelativeSizeAxes = Axes.Both }); + CompositeDrawable mainContent = new PopoverContainer + { + RelativeSizeAxes = Axes.Both, + Child = cursorContainer = new MenuCursorContainer { RelativeSizeAxes = Axes.Both, } + }; + + cursorContainer.Child = content = new OsuTooltipContainer(cursorContainer.Cursor) + { + RelativeSizeAxes = Axes.Both + }; if (CreateNestedActionContainer) { From ba9b51c12d5a4c49b96a9e2db71760290cb546c8 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sat, 17 Jul 2021 19:25:25 +0800 Subject: [PATCH 0427/2442] Add localisation for WikiHeader Co-authored-by: huoyaoyuan --- osu.Game/Localisation/WikiStrings.cs | 29 ++++++++++++++++++++++++++++ osu.Game/Overlays/Wiki/WikiHeader.cs | 24 +++++++++++++---------- 2 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 osu.Game/Localisation/WikiStrings.cs diff --git a/osu.Game/Localisation/WikiStrings.cs b/osu.Game/Localisation/WikiStrings.cs new file mode 100644 index 0000000000..6176fd4e85 --- /dev/null +++ b/osu.Game/Localisation/WikiStrings.cs @@ -0,0 +1,29 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class WikiStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.Wiki"; + + /// + /// "index" + /// + public static LocalisableString IndexPageString => new TranslatableString(getKey(@"index_page"), @"index"); + + /// + /// "wiki" + /// + public static LocalisableString HeaderTitle => new TranslatableString(getKey(@"header_title"), @"wiki"); + + /// + /// "knowledge base" + /// + public static LocalisableString HeaderDescription => new TranslatableString(getKey(@"header_description"), @"knowledge base"); + + private static string getKey(string key) => $"{prefix}:{key}"; + } +} diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index 6b8cba48b4..4bec42b6ce 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -5,14 +5,18 @@ using System; using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Localisation; +using osu.Game.Localisation; using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.Wiki { public class WikiHeader : BreadcrumbControlOverlayHeader { - private const string index_page_string = "index"; private const string index_path = "Main_Page"; + public static LocalisableString IndexPageString => WikiStrings.IndexPageString; + public static LocalisableString HeaderTitle => WikiStrings.HeaderTitle; + public static LocalisableString HeaderDescription => WikiStrings.HeaderDescription; public readonly Bindable WikiPageData = new Bindable(); @@ -21,8 +25,8 @@ namespace osu.Game.Overlays.Wiki public WikiHeader() { - TabControl.AddItem(index_page_string); - Current.Value = index_page_string; + TabControl.AddItem(IndexPageString); + Current.Value = IndexPageString; WikiPageData.BindValueChanged(onWikiPageChange); Current.BindValueChanged(onCurrentChange); @@ -34,13 +38,13 @@ namespace osu.Game.Overlays.Wiki return; TabControl.Clear(); - Current.Value = null; + Current.Value = string.Empty; - TabControl.AddItem(index_page_string); + TabControl.AddItem(IndexPageString); if (e.NewValue.Path == index_path) { - Current.Value = index_page_string; + Current.Value = IndexPageString; return; } @@ -51,12 +55,12 @@ namespace osu.Game.Overlays.Wiki Current.Value = e.NewValue.Title; } - private void onCurrentChange(ValueChangedEvent e) + private void onCurrentChange(ValueChangedEvent e) { if (e.NewValue == TabControl.Items.LastOrDefault()) return; - if (e.NewValue == index_page_string) + if (e.NewValue == IndexPageString) { ShowIndexPage?.Invoke(); return; @@ -73,8 +77,8 @@ namespace osu.Game.Overlays.Wiki { public WikiHeaderTitle() { - Title = "wiki"; - Description = "knowledge base"; + Title = HeaderTitle; + Description = HeaderDescription; IconTexture = "Icons/Hexacons/wiki"; } } From 51742da89a91ba318f7d2cee74a3458ec87801e8 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sat, 17 Jul 2021 19:28:27 +0800 Subject: [PATCH 0428/2442] Add localisation for NewsHeader Co-authored-by: huoyaoyuan --- osu.Game/Localisation/NewsStrings.cs | 29 ++++++++++++++++++++++++++++ osu.Game/Overlays/News/NewsHeader.cs | 16 +++++++++------ 2 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 osu.Game/Localisation/NewsStrings.cs diff --git a/osu.Game/Localisation/NewsStrings.cs b/osu.Game/Localisation/NewsStrings.cs new file mode 100644 index 0000000000..8cad6015cf --- /dev/null +++ b/osu.Game/Localisation/NewsStrings.cs @@ -0,0 +1,29 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class NewsStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.News"; + + /// + /// "frontpage" + /// + public static LocalisableString FrontPageString => new TranslatableString(getKey(@"front_page"), @"frontpage"); + + /// + /// "news" + /// + public static LocalisableString HeaderTitle => new TranslatableString(getKey(@"header_title"), @"news"); + + /// + /// "join the real-time discussion" + /// + public static LocalisableString HeaderDescription => new TranslatableString(getKey(@"header_description"), @"get up-to-date on community happenings"); + + private static string getKey(string key) => $"{prefix}:{key}"; + } +} diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs index 56c54425bd..0b0277f984 100644 --- a/osu.Game/Overlays/News/NewsHeader.cs +++ b/osu.Game/Overlays/News/NewsHeader.cs @@ -4,12 +4,16 @@ using System; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Localisation; +using osu.Game.Localisation; namespace osu.Game.Overlays.News { public class NewsHeader : BreadcrumbControlOverlayHeader { - private const string front_page_string = "frontpage"; + public static LocalisableString FrontPageString => NewsStrings.FrontPageString; + public static LocalisableString HeaderTitle => NewsStrings.HeaderTitle; + public static LocalisableString HeaderDescription => NewsStrings.HeaderDescription; public Action ShowFrontPage; @@ -17,7 +21,7 @@ namespace osu.Game.Overlays.News public NewsHeader() { - TabControl.AddItem(front_page_string); + TabControl.AddItem(FrontPageString); article.BindValueChanged(onArticleChanged, true); } @@ -28,7 +32,7 @@ namespace osu.Game.Overlays.News Current.BindValueChanged(e => { - if (e.NewValue == front_page_string) + if (e.NewValue == FrontPageString) ShowFrontPage?.Invoke(); }); } @@ -49,7 +53,7 @@ namespace osu.Game.Overlays.News } else { - Current.Value = front_page_string; + Current.Value = FrontPageString; } } @@ -61,8 +65,8 @@ namespace osu.Game.Overlays.News { public NewsHeaderTitle() { - Title = "news"; - Description = "get up-to-date on community happenings"; + Title = HeaderTitle; + Description = HeaderDescription; IconTexture = "Icons/Hexacons/news"; } } From 5b694ad28b1610b24a0d9303d3c39f18e0d14e4b Mon Sep 17 00:00:00 2001 From: huoyaoyuan Date: Sat, 17 Jul 2021 19:37:32 +0800 Subject: [PATCH 0429/2442] Replace string with LocalisableString --- osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs b/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs index 443b3dcf01..68faadeaf1 100644 --- a/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs +++ b/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs @@ -4,15 +4,16 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Localisation; using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays { - public abstract class BreadcrumbControlOverlayHeader : TabControlOverlayHeader + public abstract class BreadcrumbControlOverlayHeader : TabControlOverlayHeader { - protected override OsuTabControl CreateTabControl() => new OverlayHeaderBreadcrumbControl(); + protected override OsuTabControl CreateTabControl() => new OverlayHeaderBreadcrumbControl(); - public class OverlayHeaderBreadcrumbControl : BreadcrumbControl + public class OverlayHeaderBreadcrumbControl : BreadcrumbControl { public OverlayHeaderBreadcrumbControl() { @@ -26,7 +27,7 @@ namespace osu.Game.Overlays AccentColour = colourProvider.Light2; } - protected override TabItem CreateTabItem(string value) => new ControlTabItem(value) + protected override TabItem CreateTabItem(LocalisableString value) => new ControlTabItem(value) { AccentColour = AccentColour, }; @@ -35,7 +36,7 @@ namespace osu.Game.Overlays { protected override float ChevronSize => 8; - public ControlTabItem(string value) + public ControlTabItem(LocalisableString value) : base(value) { RelativeSizeAxes = Axes.Y; From 22ff40fdd5f1362c511f3f2f31be0a1fe0e1c3b5 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sat, 17 Jul 2021 19:40:25 +0800 Subject: [PATCH 0430/2442] Fix broken WikiHeader --- osu.Game/Overlays/Wiki/WikiHeader.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index 6b8cba48b4..2e15fbb566 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -5,6 +5,7 @@ using System; using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.Wiki @@ -34,7 +35,7 @@ namespace osu.Game.Overlays.Wiki return; TabControl.Clear(); - Current.Value = null; + Current.Value = string.Empty; TabControl.AddItem(index_page_string); @@ -51,7 +52,7 @@ namespace osu.Game.Overlays.Wiki Current.Value = e.NewValue.Title; } - private void onCurrentChange(ValueChangedEvent e) + private void onCurrentChange(ValueChangedEvent e) { if (e.NewValue == TabControl.Items.LastOrDefault()) return; From e3c10e39945fe7612a467a41ebca735cbd7bbc7a Mon Sep 17 00:00:00 2001 From: huoyaoyuan Date: Sat, 17 Jul 2021 19:40:32 +0800 Subject: [PATCH 0431/2442] Add missing LocalisableString --- osu.Game/Graphics/UserInterface/OsuTabControl.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index c447d7f609..f97880c7ca 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Framework.Utils; using osu.Game.Graphics.Sprites; @@ -160,7 +161,12 @@ namespace osu.Game.Graphics.UserInterface Margin = new MarginPadding { Top = 5, Bottom = 5 }, Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, - Text = (value as IHasDescription)?.Description ?? (value as Enum)?.GetLocalisableDescription() ?? value.ToString(), + Text = value switch{ + IHasDescription desc => desc?.Description, + Enum e => e.GetLocalisableDescription(), + LocalisableString l => l, + var other => other.ToString() + }, Font = OsuFont.GetFont(size: 14) }, Bar = new Box From 7859d02c5b4e0d65eef8b4ba9639b03c8a45af43 Mon Sep 17 00:00:00 2001 From: bdach Date: Sat, 17 Jul 2021 20:33:26 +0800 Subject: [PATCH 0432/2442] Allow null for breadcrumb control --- osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs | 10 +++++----- osu.Game/Overlays/Wiki/WikiHeader.cs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs b/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs index 68faadeaf1..0d383c374f 100644 --- a/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs +++ b/osu.Game/Overlays/BreadcrumbControlOverlayHeader.cs @@ -9,11 +9,11 @@ using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays { - public abstract class BreadcrumbControlOverlayHeader : TabControlOverlayHeader + public abstract class BreadcrumbControlOverlayHeader : TabControlOverlayHeader { - protected override OsuTabControl CreateTabControl() => new OverlayHeaderBreadcrumbControl(); + protected override OsuTabControl CreateTabControl() => new OverlayHeaderBreadcrumbControl(); - public class OverlayHeaderBreadcrumbControl : BreadcrumbControl + public class OverlayHeaderBreadcrumbControl : BreadcrumbControl { public OverlayHeaderBreadcrumbControl() { @@ -27,7 +27,7 @@ namespace osu.Game.Overlays AccentColour = colourProvider.Light2; } - protected override TabItem CreateTabItem(LocalisableString value) => new ControlTabItem(value) + protected override TabItem CreateTabItem(LocalisableString? value) => new ControlTabItem(value) { AccentColour = AccentColour, }; @@ -36,7 +36,7 @@ namespace osu.Game.Overlays { protected override float ChevronSize => 8; - public ControlTabItem(LocalisableString value) + public ControlTabItem(LocalisableString? value) : base(value) { RelativeSizeAxes = Axes.Y; diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index 2e15fbb566..fb87486b4e 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -35,7 +35,7 @@ namespace osu.Game.Overlays.Wiki return; TabControl.Clear(); - Current.Value = string.Empty; + Current.Value = null; TabControl.AddItem(index_page_string); @@ -52,7 +52,7 @@ namespace osu.Game.Overlays.Wiki Current.Value = e.NewValue.Title; } - private void onCurrentChange(ValueChangedEvent e) + private void onCurrentChange(ValueChangedEvent e) { if (e.NewValue == TabControl.Items.LastOrDefault()) return; From 5b4a1ef70a3294597979d5185919b51ac7a1f28d Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sat, 17 Jul 2021 20:40:59 +0800 Subject: [PATCH 0433/2442] Update test to match Breadcrumb change --- osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs index 863fa48ddf..e7e6030c66 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Overlays.Wiki; @@ -96,7 +97,7 @@ namespace osu.Game.Tests.Visual.Online private class TestHeader : WikiHeader { - public IReadOnlyList TabControlItems => TabControl.Items; + public IReadOnlyList TabControlItems => TabControl.Items; } } } From e6b8307b8e637479713accbc4a797a6a5818a7db Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 14:46:14 +0200 Subject: [PATCH 0434/2442] Localise `ProfileHeader` --- osu.Game/Overlays/Profile/ProfileHeader.cs | 33 +++++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index c947ef0781..32bca68f0e 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -7,12 +7,14 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; using osu.Game.Overlays.Profile.Header; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; namespace osu.Game.Overlays.Profile { - public class ProfileHeader : TabControlOverlayHeader + public class ProfileHeader : TabControlOverlayHeader { private UserCoverBackground coverContainer; @@ -27,8 +29,6 @@ namespace osu.Game.Overlays.Profile User.ValueChanged += e => updateDisplay(e.NewValue); - TabControl.AddItem("info"); - TabControl.AddItem("modding"); centreHeaderContainer.DetailsVisible.BindValueChanged(visible => detailHeaderContainer.Expanded = visible.NewValue, true); } @@ -96,7 +96,7 @@ namespace osu.Game.Overlays.Profile { public ProfileHeaderTitle() { - Title = "player info"; + Title = PageTitleStrings.MainUsersControllerDefault; IconTexture = "Icons/Hexacons/profile"; } } @@ -106,4 +106,29 @@ namespace osu.Game.Overlays.Profile protected override double LoadDelay => 0; } } + + [LocalisableEnum(typeof(ProfileHeaderTabEnumLocalisationMapper))] + public enum ProfileHeaderTab + { + Info, + Modding, + } + + public class ProfileHeaderTabEnumLocalisationMapper : EnumLocalisationMapper + { + public override LocalisableString Map(ProfileHeaderTab value) + { + switch (value) + { + case ProfileHeaderTab.Info: + return LayoutStrings.HeaderUsersShow; + + case ProfileHeaderTab.Modding: + return LayoutStrings.HeaderUsersModding; + + default: + return string.Empty; + } + } + } } From d9c7ea202672298d2ff31678218eae955d3f9271 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 14:57:05 +0200 Subject: [PATCH 0435/2442] Localise profile section titles. --- osu.Game/Overlays/Profile/ProfileSection.cs | 3 ++- osu.Game/Overlays/Profile/Sections/AboutSection.cs | 5 ++++- osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs | 4 +++- osu.Game/Overlays/Profile/Sections/HistoricalSection.cs | 4 +++- osu.Game/Overlays/Profile/Sections/KudosuSection.cs | 4 +++- osu.Game/Overlays/Profile/Sections/MedalsSection.cs | 5 ++++- osu.Game/Overlays/Profile/Sections/RanksSection.cs | 4 +++- osu.Game/Overlays/Profile/Sections/RecentSection.cs | 4 +++- 8 files changed, 25 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Profile/ProfileSection.cs b/osu.Game/Overlays/Profile/ProfileSection.cs index 21f7921da6..1a5f562fff 100644 --- a/osu.Game/Overlays/Profile/ProfileSection.cs +++ b/osu.Game/Overlays/Profile/ProfileSection.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Sprites; @@ -17,7 +18,7 @@ namespace osu.Game.Overlays.Profile { public abstract class ProfileSection : Container { - public abstract string Title { get; } + public abstract LocalisableString Title { get; } public abstract string Identifier { get; } diff --git a/osu.Game/Overlays/Profile/Sections/AboutSection.cs b/osu.Game/Overlays/Profile/Sections/AboutSection.cs index 1bc01cfd9e..c224d2b1be 100644 --- a/osu.Game/Overlays/Profile/Sections/AboutSection.cs +++ b/osu.Game/Overlays/Profile/Sections/AboutSection.cs @@ -1,11 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; + namespace osu.Game.Overlays.Profile.Sections { public class AboutSection : ProfileSection { - public override string Title => "me!"; + public override LocalisableString Title => UsersStrings.ShowExtraMeTitle; public override string Identifier => "me"; } diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs index c283de42f3..67aaf7d41e 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs @@ -1,14 +1,16 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Localisation; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Profile.Sections.Beatmaps; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Profile.Sections { public class BeatmapsSection : ProfileSection { - public override string Title => "Beatmaps"; + public override LocalisableString Title => UsersStrings.ShowExtraBeatmapsTitle; public override string Identifier => "beatmaps"; diff --git a/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs b/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs index 4fbb7fc7d7..09ca492aa9 100644 --- a/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs +++ b/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs @@ -2,15 +2,17 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Profile.Sections.Historical; using osu.Game.Overlays.Profile.Sections.Ranks; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Profile.Sections { public class HistoricalSection : ProfileSection { - public override string Title => "Historical"; + public override LocalisableString Title => UsersStrings.ShowExtraHistoricalTitle; public override string Identifier => "historical"; diff --git a/osu.Game/Overlays/Profile/Sections/KudosuSection.cs b/osu.Game/Overlays/Profile/Sections/KudosuSection.cs index a9e9952257..ffa7ef4eaf 100644 --- a/osu.Game/Overlays/Profile/Sections/KudosuSection.cs +++ b/osu.Game/Overlays/Profile/Sections/KudosuSection.cs @@ -3,12 +3,14 @@ using osu.Framework.Graphics; using osu.Game.Overlays.Profile.Sections.Kudosu; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Profile.Sections { public class KudosuSection : ProfileSection { - public override string Title => "Kudosu!"; + public override LocalisableString Title => UsersStrings.ShowExtraKudosuTitle; public override string Identifier => "kudosu"; diff --git a/osu.Game/Overlays/Profile/Sections/MedalsSection.cs b/osu.Game/Overlays/Profile/Sections/MedalsSection.cs index 575a2f2c19..3512333e27 100644 --- a/osu.Game/Overlays/Profile/Sections/MedalsSection.cs +++ b/osu.Game/Overlays/Profile/Sections/MedalsSection.cs @@ -1,11 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; + namespace osu.Game.Overlays.Profile.Sections { public class MedalsSection : ProfileSection { - public override string Title => "Medals"; + public override LocalisableString Title => UsersStrings.ShowExtraMedalsTitle; public override string Identifier => "medals"; } diff --git a/osu.Game/Overlays/Profile/Sections/RanksSection.cs b/osu.Game/Overlays/Profile/Sections/RanksSection.cs index 33f7c2f71a..a7931c8675 100644 --- a/osu.Game/Overlays/Profile/Sections/RanksSection.cs +++ b/osu.Game/Overlays/Profile/Sections/RanksSection.cs @@ -3,12 +3,14 @@ using osu.Game.Overlays.Profile.Sections.Ranks; using osu.Game.Online.API.Requests; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Profile.Sections { public class RanksSection : ProfileSection { - public override string Title => "Ranks"; + public override LocalisableString Title => UsersStrings.ShowExtraTopRanksTitle; public override string Identifier => "top_ranks"; diff --git a/osu.Game/Overlays/Profile/Sections/RecentSection.cs b/osu.Game/Overlays/Profile/Sections/RecentSection.cs index 1e6cfcc9fd..7a6536c3af 100644 --- a/osu.Game/Overlays/Profile/Sections/RecentSection.cs +++ b/osu.Game/Overlays/Profile/Sections/RecentSection.cs @@ -1,13 +1,15 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Localisation; using osu.Game.Overlays.Profile.Sections.Recent; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Profile.Sections { public class RecentSection : ProfileSection { - public override string Title => "Recent"; + public override LocalisableString Title => UsersStrings.ShowExtraRecentActivityTitle; public override string Identifier => "recent_activity"; From ca1080dfb5d3cfa28319b69785c55e8c2278781c Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sat, 17 Jul 2021 21:16:57 +0800 Subject: [PATCH 0436/2442] use switch statement Co-authored-by: bdach --- .../Graphics/UserInterface/OsuTabControl.cs | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index f97880c7ca..21e7a68e99 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs @@ -154,6 +154,26 @@ namespace osu.Game.Graphics.UserInterface AutoSizeAxes = Axes.X; RelativeSizeAxes = Axes.Y; + LocalisableString text; + switch (value) + { + case IHasDescription hasDescription: + text = hasDescription.GetDescription(); + break; + + case Enum e: + text = e.GetLocalisableDescription(); + break; + + case LocalisableString l: + text = l; + break; + + default: + text = value.ToString(); + break; + }; + Children = new Drawable[] { Text = new OsuSpriteText @@ -161,12 +181,7 @@ namespace osu.Game.Graphics.UserInterface Margin = new MarginPadding { Top = 5, Bottom = 5 }, Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, - Text = value switch{ - IHasDescription desc => desc?.Description, - Enum e => e.GetLocalisableDescription(), - LocalisableString l => l, - var other => other.ToString() - }, + Text = text, Font = OsuFont.GetFont(size: 14) }, Bar = new Box From 4d276b114bbacdd9df278aeaa23ef59a956525ff Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 15:18:45 +0200 Subject: [PATCH 0437/2442] Localise profile header. --- .../Overlays/Profile/Header/Components/ExpandDetailsButton.cs | 3 ++- osu.Game/Overlays/Profile/Header/Components/FollowersButton.cs | 3 ++- .../Overlays/Profile/Header/Components/LevelProgressBar.cs | 3 ++- .../Profile/Header/Components/MappingSubscribersButton.cs | 3 ++- .../Overlays/Profile/Header/Components/MessageUserButton.cs | 3 ++- .../Profile/Header/Components/OverlinedInfoContainer.cs | 3 ++- .../Profile/Header/Components/OverlinedTotalPlayTime.cs | 3 ++- .../Overlays/Profile/Header/Components/PreviousUsernames.cs | 3 ++- 8 files changed, 16 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs b/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs index 16b443875e..b4a5d5e31b 100644 --- a/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Graphics.UserInterface; +using osu.Game.Resources.Localisation.Web; using osuTK; namespace osu.Game.Overlays.Profile.Header.Components @@ -18,7 +19,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { public readonly BindableBool DetailsVisible = new BindableBool(); - public override LocalisableString TooltipText => DetailsVisible.Value ? "collapse" : "expand"; + public override LocalisableString TooltipText => DetailsVisible.Value ? CommonStrings.ButtonsCollapse : CommonStrings.ButtonsExpand; private SpriteIcon icon; private Sample sampleOpen; diff --git a/osu.Game/Overlays/Profile/Header/Components/FollowersButton.cs b/osu.Game/Overlays/Profile/Header/Components/FollowersButton.cs index db94762efd..8f66120055 100644 --- a/osu.Game/Overlays/Profile/Header/Components/FollowersButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/FollowersButton.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; namespace osu.Game.Overlays.Profile.Header.Components @@ -13,7 +14,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { public readonly Bindable User = new Bindable(); - public override LocalisableString TooltipText => "followers"; + public override LocalisableString TooltipText => FriendsStrings.ButtonsDisabled; protected override IconUsage Icon => FontAwesome.Solid.User; diff --git a/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs index 528b05a80a..ed89d78a10 100644 --- a/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs +++ b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs @@ -10,6 +10,7 @@ using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; using osuTK.Graphics; @@ -26,7 +27,7 @@ namespace osu.Game.Overlays.Profile.Header.Components public LevelProgressBar() { - TooltipText = "progress to next level"; + TooltipText = UsersStrings.ShowStatsLevelProgress; } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/Profile/Header/Components/MappingSubscribersButton.cs b/osu.Game/Overlays/Profile/Header/Components/MappingSubscribersButton.cs index ae3d024fbf..5cdf3a5ef9 100644 --- a/osu.Game/Overlays/Profile/Header/Components/MappingSubscribersButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/MappingSubscribersButton.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; namespace osu.Game.Overlays.Profile.Header.Components @@ -13,7 +14,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { public readonly Bindable User = new Bindable(); - public override LocalisableString TooltipText => "mapping subscribers"; + public override LocalisableString TooltipText => FollowsStrings.MappingFollowers; protected override IconUsage Icon => FontAwesome.Solid.Bell; diff --git a/osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs b/osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs index 4c2cc768ce..07f1f1c3ed 100644 --- a/osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Online.API; using osu.Game.Online.Chat; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; using osuTK; @@ -17,7 +18,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { public readonly Bindable User = new Bindable(); - public override LocalisableString TooltipText => "send message"; + public override LocalisableString TooltipText => UsersStrings.CardSendMessage; [Resolved(CanBeNull = true)] private ChannelManager channelManager { get; set; } diff --git a/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs b/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs index 9f56a34aa6..8f1bbc4097 100644 --- a/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs +++ b/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs @@ -4,6 +4,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK.Graphics; @@ -16,7 +17,7 @@ namespace osu.Game.Overlays.Profile.Header.Components private readonly OsuSpriteText title; private readonly OsuSpriteText content; - public string Title + public LocalisableString Title { set => title.Text = value; } diff --git a/osu.Game/Overlays/Profile/Header/Components/OverlinedTotalPlayTime.cs b/osu.Game/Overlays/Profile/Header/Components/OverlinedTotalPlayTime.cs index aa7cb8636a..1a40944632 100644 --- a/osu.Game/Overlays/Profile/Header/Components/OverlinedTotalPlayTime.cs +++ b/osu.Game/Overlays/Profile/Header/Components/OverlinedTotalPlayTime.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; namespace osu.Game.Overlays.Profile.Header.Components @@ -31,7 +32,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { InternalChild = info = new OverlinedInfoContainer { - Title = "Total Play Time", + Title = UsersStrings.ShowStatsPlayTime, LineColour = colourProvider.Highlight1, }; diff --git a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs index 3cdf110090..14eeb4e5f0 100644 --- a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs +++ b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; using osuTK; @@ -68,7 +69,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Text = @"formerly known as", + Text = UsersStrings.ShowPreviousUsernames, Font = OsuFont.GetFont(size: 10, italics: true) } }, From 306a34a80280dc608dff653666c2e7f5a2caaddf Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 15:21:30 +0200 Subject: [PATCH 0438/2442] Localise level badge tooltip. --- osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs b/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs index a0b8ef0f11..1deed1a748 100644 --- a/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs +++ b/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; namespace osu.Game.Overlays.Profile.Header.Components @@ -19,13 +20,13 @@ namespace osu.Game.Overlays.Profile.Header.Components { public readonly Bindable User = new Bindable(); - public LocalisableString TooltipText { get; } + public LocalisableString TooltipText { get; private set; } private OsuSpriteText levelText; public LevelBadge() { - TooltipText = "level"; + TooltipText = UsersStrings.ShowStatsLevel("0"); } [BackgroundDependencyLoader] @@ -53,6 +54,7 @@ namespace osu.Game.Overlays.Profile.Header.Components private void updateLevel(User user) { levelText.Text = user?.Statistics?.Level.Current.ToString() ?? "0"; + TooltipText = UsersStrings.ShowStatsLevel(user?.Statistics?.Level.Current.ToString()); } } } From a0c6945f8fd320e3d23ab8b07706dc66b01fcaf6 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 15:25:45 +0200 Subject: [PATCH 0439/2442] Localise user graph. --- osu.Game/Overlays/Profile/Header/Components/RankGraph.cs | 5 +++-- osu.Game/Overlays/Profile/UserGraph.cs | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs index ad91e491ef..6bf356c0ff 100644 --- a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs @@ -9,6 +9,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; namespace osu.Game.Overlays.Profile.Header.Components @@ -27,7 +28,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Text = "No recent plays", + Text = UsersStrings.ShowExtraUnranked, Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular) }); } @@ -74,7 +75,7 @@ namespace osu.Game.Overlays.Profile.Header.Components private class RankGraphTooltip : UserGraphTooltip { public RankGraphTooltip() - : base("Global Ranking") + : base(UsersStrings.ShowRankGlobalSimple) { } diff --git a/osu.Game/Overlays/Profile/UserGraph.cs b/osu.Game/Overlays/Profile/UserGraph.cs index cdfd722d68..b7a08b6c5e 100644 --- a/osu.Game/Overlays/Profile/UserGraph.cs +++ b/osu.Game/Overlays/Profile/UserGraph.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -211,7 +212,7 @@ namespace osu.Game.Overlays.Profile protected readonly OsuSpriteText Counter, BottomText; private readonly Box background; - protected UserGraphTooltip(string tooltipCounterName) + protected UserGraphTooltip(LocalisableString tooltipCounterName) { AutoSizeAxes = Axes.Both; Masking = true; From 2cfec1dc326af464ccb324a3092ac54673b0dad2 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 15:26:18 +0200 Subject: [PATCH 0440/2442] Localise osu!supporter badge. --- osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs index 9a43997030..77f0378762 100644 --- a/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs +++ b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Graphics; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Profile.Header.Components { @@ -19,7 +20,7 @@ namespace osu.Game.Overlays.Profile.Header.Components private readonly FillFlowContainer iconContainer; private readonly CircularContainer content; - public LocalisableString TooltipText => "osu!supporter"; + public LocalisableString TooltipText => UsersStrings.ShowIsSupporter; public int SupportLevel { From c6a27e4baaf84cf9e07df687bbd83b6a90c5bb36 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 15:27:40 +0200 Subject: [PATCH 0441/2442] Localise `CentreHeaderContainer`. --- osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs index 62ebee7677..4195b0b2f1 100644 --- a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; using osu.Game.Overlays.Profile.Header.Components; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; using osuTK; @@ -119,12 +120,12 @@ namespace osu.Game.Overlays.Profile.Header { hiddenDetailGlobal = new OverlinedInfoContainer { - Title = "Global Ranking", + Title = UsersStrings.ShowRankGlobalSimple, LineColour = colourProvider.Highlight1 }, hiddenDetailCountry = new OverlinedInfoContainer { - Title = "Country Ranking", + Title = UsersStrings.ShowRankCountrySimple, LineColour = colourProvider.Highlight1 }, } From 213e3c0716e8706565489a4cd3896e08ca0a5383 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 15:29:34 +0200 Subject: [PATCH 0442/2442] Localise `DetailHeaderContainer` --- osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs index 574aef02fd..6214e504b0 100644 --- a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs @@ -11,6 +11,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.Leaderboards; using osu.Game.Overlays.Profile.Header.Components; +using osu.Game.Resources.Localisation.Web; using osu.Game.Scoring; using osu.Game.Users; using osuTK; @@ -100,7 +101,7 @@ namespace osu.Game.Overlays.Profile.Header }, medalInfo = new OverlinedInfoContainer { - Title = "Medals", + Title = UsersStrings.ShowStatsMedals, LineColour = colours.GreenLight, }, ppInfo = new OverlinedInfoContainer @@ -151,12 +152,12 @@ namespace osu.Game.Overlays.Profile.Header { detailGlobalRank = new OverlinedInfoContainer(true, 110) { - Title = "Global Ranking", + Title = UsersStrings.ShowRankGlobalSimple, LineColour = colourProvider.Highlight1, }, detailCountryRank = new OverlinedInfoContainer(false, 110) { - Title = "Country Ranking", + Title = UsersStrings.ShowRankCountrySimple, LineColour = colourProvider.Highlight1, }, } From c6bc95767dc5e2ac3e24596a6d99fd06e3d53ed1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 17 Jul 2021 22:31:47 +0900 Subject: [PATCH 0443/2442] Simplify popover hide logic and add test coverage --- .../TestSceneMultiplayerLoungeSubScreen.cs | 12 +++++++++++ .../Lounge/Components/RoomsContainer.cs | 7 ------- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 20 ++++++++++++------- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs index bde9ea89ed..de46d9e25a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs @@ -52,6 +52,18 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("room join password correct", () => lastJoinedPassword == null); } + [Test] + public void TestPopoverHidesOnLeavingScreen() + { + AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true)); + AddStep("select room", () => InputManager.Key(Key.Down)); + AddStep("attempt join room", () => InputManager.Key(Key.Enter)); + + AddUntilStep("password prompt appeared", () => InputManager.ChildrenOfType().Any()); + AddStep("exit screen", () => Stack.Exit()); + AddUntilStep("password prompt hidden", () => !InputManager.ChildrenOfType().Any()); + } + [Test] public void TestJoinRoomWithPassword() { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index 0910ff1a7a..07e412ee75 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -7,7 +7,6 @@ using System.Collections.Specialized; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -239,11 +238,5 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components if (roomManager != null) roomManager.RoomsUpdated -= updateSorting; } - - public void HideAnyPopovers() - { - // must be called on a child of the PopoverContainer due to parent traversal not considering self. - roomFlow.HidePopover(); - } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index dd6106b868..f43109c4fa 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -6,6 +6,7 @@ using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; @@ -151,24 +152,29 @@ namespace osu.Game.Screens.OnlinePlay.Lounge onReturning(); } - private void onReturning() - { - filter.HoldFocus = true; - } - public override bool OnExiting(IScreen next) { - filter.HoldFocus = false; + onLeaving(); return base.OnExiting(next); } public override void OnSuspending(IScreen next) { + onLeaving(); base.OnSuspending(next); + } + + private void onReturning() + { + filter.HoldFocus = true; + } + + private void onLeaving() + { filter.HoldFocus = false; // ensure any password prompt is dismissed. - roomsContainer.HideAnyPopovers(); + this.HidePopover(); } public void Join(Room room, string password) From d36842aa15a47a5dd252802970ed9e465fb4552a Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 15:32:28 +0200 Subject: [PATCH 0444/2442] Localise `TopHeaderContainer` --- .../Profile/Header/TopHeaderContainer.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index d751424367..b64dba62e3 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -7,11 +7,13 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Overlays.Profile.Header.Components; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; using osu.Game.Users.Drawables; using osuTK; @@ -179,19 +181,19 @@ namespace osu.Game.Overlays.Profile.Header if (user?.Statistics != null) { - userStats.Add(new UserStatsLine("Ranked Score", user.Statistics.RankedScore.ToString("#,##0"))); - userStats.Add(new UserStatsLine("Hit Accuracy", user.Statistics.DisplayAccuracy)); - userStats.Add(new UserStatsLine("Play Count", user.Statistics.PlayCount.ToString("#,##0"))); - userStats.Add(new UserStatsLine("Total Score", user.Statistics.TotalScore.ToString("#,##0"))); - userStats.Add(new UserStatsLine("Total Hits", user.Statistics.TotalHits.ToString("#,##0"))); - userStats.Add(new UserStatsLine("Maximum Combo", user.Statistics.MaxCombo.ToString("#,##0"))); - userStats.Add(new UserStatsLine("Replays Watched by Others", user.Statistics.ReplaysWatched.ToString("#,##0"))); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsRankedScore, user.Statistics.RankedScore.ToString("#,##0"))); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsHitAccuracy, user.Statistics.DisplayAccuracy)); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsPlayCount, user.Statistics.PlayCount.ToString("#,##0"))); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsTotalScore, user.Statistics.TotalScore.ToString("#,##0"))); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsTotalHits, user.Statistics.TotalHits.ToString("#,##0"))); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsMaximumCombo, user.Statistics.MaxCombo.ToString("#,##0"))); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsReplaysWatchedByOthers, user.Statistics.ReplaysWatched.ToString("#,##0"))); } } private class UserStatsLine : Container { - public UserStatsLine(string left, string right) + public UserStatsLine(LocalisableString left, string right) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; From f4eeb9139ed3784b9ada9f7b13cc307b839f84ab Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sat, 17 Jul 2021 21:37:58 +0800 Subject: [PATCH 0445/2442] Correct code style --- osu.Game/Graphics/UserInterface/OsuTabControl.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index 21e7a68e99..3572ea5c31 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs @@ -155,6 +155,7 @@ namespace osu.Game.Graphics.UserInterface RelativeSizeAxes = Axes.Y; LocalisableString text; + switch (value) { case IHasDescription hasDescription: @@ -172,7 +173,7 @@ namespace osu.Game.Graphics.UserInterface default: text = value.ToString(); break; - }; + } Children = new Drawable[] { From 5bb45c7f84fb4b30e310a1723fb2e8dfc0753ce1 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 15:45:17 +0200 Subject: [PATCH 0446/2442] Localise beatmap section. --- .../Sections/Beatmaps/PaginatedBeatmapContainer.cs | 5 +++-- osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs | 10 +++++----- .../Profile/Sections/PaginatedProfileSubsection.cs | 9 +++++---- .../Overlays/Profile/Sections/ProfileSubsection.cs | 5 +++-- .../Profile/Sections/ProfileSubsectionHeader.cs | 5 +++-- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index fe9c710bcc..af1ed9d529 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; @@ -19,8 +20,8 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps private const float panel_padding = 10f; private readonly BeatmapSetType type; - public PaginatedBeatmapContainer(BeatmapSetType type, Bindable user, string headerText) - : base(user, headerText) + public PaginatedBeatmapContainer(BeatmapSetType type, Bindable user, LocalisableString headerText) + : base(user, headerText, null) { this.type = type; ItemsPerPage = 6; diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs index 67aaf7d41e..b8fbe73c0c 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs @@ -18,11 +18,11 @@ namespace osu.Game.Overlays.Profile.Sections { Children = new[] { - new PaginatedBeatmapContainer(BeatmapSetType.Favourite, User, "Favourite Beatmaps"), - new PaginatedBeatmapContainer(BeatmapSetType.RankedAndApproved, User, "Ranked & Approved Beatmaps"), - new PaginatedBeatmapContainer(BeatmapSetType.Loved, User, "Loved Beatmaps"), - new PaginatedBeatmapContainer(BeatmapSetType.Unranked, User, "Pending Beatmaps"), - new PaginatedBeatmapContainer(BeatmapSetType.Graveyard, User, "Graveyarded Beatmaps") + new PaginatedBeatmapContainer(BeatmapSetType.Favourite, User, UsersStrings.ShowExtraBeatmapsFavouriteTitle), + new PaginatedBeatmapContainer(BeatmapSetType.RankedAndApproved, User, UsersStrings.ShowExtraBeatmapsRankedTitle), + new PaginatedBeatmapContainer(BeatmapSetType.Loved, User, UsersStrings.ShowExtraBeatmapsLovedTitle), + new PaginatedBeatmapContainer(BeatmapSetType.Unranked, User, UsersStrings.ShowExtraBeatmapsPendingTitle), + new PaginatedBeatmapContainer(BeatmapSetType.Graveyard, User, UsersStrings.ShowExtraBeatmapsGraveyardTitle) }; } } diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs index e237b43b2e..25c72a24da 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs @@ -15,6 +15,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets; using osu.Game.Graphics.Sprites; using osu.Game.Graphics; +using osu.Framework.Localisation; namespace osu.Game.Overlays.Profile.Sections { @@ -36,9 +37,9 @@ namespace osu.Game.Overlays.Profile.Sections private ShowMoreButton moreButton; private OsuSpriteText missing; - private readonly string missingText; + private readonly LocalisableString? missingText; - protected PaginatedProfileSubsection(Bindable user, string headerText = "", string missingText = "") + protected PaginatedProfileSubsection(Bindable user, LocalisableString headerText, LocalisableString? missingText) : base(user, headerText, CounterVisibilityState.AlwaysVisible) { this.missingText = missingText; @@ -68,7 +69,7 @@ namespace osu.Game.Overlays.Profile.Sections missing = new OsuSpriteText { Font = OsuFont.GetFont(size: 15), - Text = missingText, + Text = missingText ?? string.Empty, Alpha = 0, } } @@ -114,7 +115,7 @@ namespace osu.Game.Overlays.Profile.Sections moreButton.Hide(); moreButton.IsLoading = false; - if (!string.IsNullOrEmpty(missingText)) + if (missingText.HasValue) missing.Show(); return; diff --git a/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs index 3e331f85e9..a9d2448abf 100644 --- a/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Users; using JetBrains.Annotations; +using osu.Framework.Localisation; namespace osu.Game.Overlays.Profile.Sections { @@ -14,12 +15,12 @@ namespace osu.Game.Overlays.Profile.Sections { protected readonly Bindable User = new Bindable(); - private readonly string headerText; + private readonly LocalisableString headerText; private readonly CounterVisibilityState counterVisibilityState; private ProfileSubsectionHeader header; - protected ProfileSubsection(Bindable user, string headerText = "", CounterVisibilityState counterVisibilityState = CounterVisibilityState.AlwaysHidden) + protected ProfileSubsection(Bindable user, LocalisableString headerText, CounterVisibilityState counterVisibilityState = CounterVisibilityState.AlwaysHidden) { this.headerText = headerText; this.counterVisibilityState = counterVisibilityState; diff --git a/osu.Game/Overlays/Profile/Sections/ProfileSubsectionHeader.cs b/osu.Game/Overlays/Profile/Sections/ProfileSubsectionHeader.cs index 5858cebe89..408cb00770 100644 --- a/osu.Game/Overlays/Profile/Sections/ProfileSubsectionHeader.cs +++ b/osu.Game/Overlays/Profile/Sections/ProfileSubsectionHeader.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Shapes; using osuTK; using osu.Game.Graphics.Sprites; using osu.Game.Graphics; +using osu.Framework.Localisation; namespace osu.Game.Overlays.Profile.Sections { @@ -24,12 +25,12 @@ namespace osu.Game.Overlays.Profile.Sections set => current.Current = value; } - private readonly string text; + private readonly LocalisableString text; private readonly CounterVisibilityState counterState; private CounterPill counterPill; - public ProfileSubsectionHeader(string text, CounterVisibilityState counterState) + public ProfileSubsectionHeader(LocalisableString text, CounterVisibilityState counterState) { this.text = text; this.counterState = counterState; From d17f6589857a1448bdb445dccb3cb0d916a137ff Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 15:50:56 +0200 Subject: [PATCH 0447/2442] Localise Recent section. --- .../Sections/Recent/PaginatedRecentActivityContainer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs index d7101a8147..04c8d7483b 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs @@ -10,13 +10,14 @@ using osu.Game.Online.API; using System.Collections.Generic; using osuTK; using osu.Framework.Allocation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Profile.Sections.Recent { public class PaginatedRecentActivityContainer : PaginatedProfileSubsection { public PaginatedRecentActivityContainer(Bindable user) - : base(user, missingText: "This user hasn't done anything notable recently!") + : base(user, string.Empty, EventsStrings.Empty) { ItemsPerPage = 10; } From fbbf8ce5a3950f7a3da6a08ec61c64c4f5a2b1f7 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 15:53:24 +0200 Subject: [PATCH 0448/2442] Localise Ranks section. --- .../Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs | 2 +- .../Overlays/Profile/Sections/PaginatedProfileSubsection.cs | 2 +- .../Profile/Sections/Ranks/PaginatedScoreContainer.cs | 3 ++- osu.Game/Overlays/Profile/Sections/RanksSection.cs | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index af1ed9d529..ec64371a5d 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps private readonly BeatmapSetType type; public PaginatedBeatmapContainer(BeatmapSetType type, Bindable user, LocalisableString headerText) - : base(user, headerText, null) + : base(user, headerText) { this.type = type; ItemsPerPage = 6; diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs index 25c72a24da..4a37c94abe 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs @@ -39,7 +39,7 @@ namespace osu.Game.Overlays.Profile.Sections private OsuSpriteText missing; private readonly LocalisableString? missingText; - protected PaginatedProfileSubsection(Bindable user, LocalisableString headerText, LocalisableString? missingText) + protected PaginatedProfileSubsection(Bindable user, LocalisableString headerText, LocalisableString? missingText = null) : base(user, headerText, CounterVisibilityState.AlwaysVisible) { this.missingText = missingText; diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs index 720cd4a3db..7c04b331c2 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs @@ -11,6 +11,7 @@ using osu.Game.Online.API.Requests.Responses; using System.Collections.Generic; using osu.Game.Online.API; using osu.Framework.Allocation; +using osu.Framework.Localisation; namespace osu.Game.Overlays.Profile.Sections.Ranks { @@ -18,7 +19,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks { private readonly ScoreType type; - public PaginatedScoreContainer(ScoreType type, Bindable user, string headerText) + public PaginatedScoreContainer(ScoreType type, Bindable user, LocalisableString headerText) : base(user, headerText) { this.type = type; diff --git a/osu.Game/Overlays/Profile/Sections/RanksSection.cs b/osu.Game/Overlays/Profile/Sections/RanksSection.cs index a7931c8675..5e0648dbb1 100644 --- a/osu.Game/Overlays/Profile/Sections/RanksSection.cs +++ b/osu.Game/Overlays/Profile/Sections/RanksSection.cs @@ -18,8 +18,8 @@ namespace osu.Game.Overlays.Profile.Sections { Children = new[] { - new PaginatedScoreContainer(ScoreType.Best, User, "Best Performance"), - new PaginatedScoreContainer(ScoreType.Firsts, User, "First Place Ranks") + new PaginatedScoreContainer(ScoreType.Best, User, UsersStrings.ShowExtraTopRanksBestTitle), + new PaginatedScoreContainer(ScoreType.Firsts, User, UsersStrings.ShowExtraTopRanksFirstTitle) }; } } From 2f3ed4a4ab080d7758afc6d2786a7d1118b4f736 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 16:13:33 +0200 Subject: [PATCH 0449/2442] Fix `PaginatedProfileSubsection` ctor arguments --- .../Overlays/Profile/Sections/PaginatedProfileSubsection.cs | 2 +- osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs index 4a37c94abe..d60243cd0a 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs @@ -39,7 +39,7 @@ namespace osu.Game.Overlays.Profile.Sections private OsuSpriteText missing; private readonly LocalisableString? missingText; - protected PaginatedProfileSubsection(Bindable user, LocalisableString headerText, LocalisableString? missingText = null) + protected PaginatedProfileSubsection(Bindable user, LocalisableString? headerText = null, LocalisableString? missingText = null) : base(user, headerText, CounterVisibilityState.AlwaysVisible) { this.missingText = missingText; diff --git a/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs index a9d2448abf..5a17f0d8bb 100644 --- a/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs @@ -20,9 +20,9 @@ namespace osu.Game.Overlays.Profile.Sections private ProfileSubsectionHeader header; - protected ProfileSubsection(Bindable user, LocalisableString headerText, CounterVisibilityState counterVisibilityState = CounterVisibilityState.AlwaysHidden) + protected ProfileSubsection(Bindable user, LocalisableString? headerText = null, CounterVisibilityState counterVisibilityState = CounterVisibilityState.AlwaysHidden) { - this.headerText = headerText; + this.headerText = headerText ?? string.Empty; this.counterVisibilityState = counterVisibilityState; User.BindTo(user); } @@ -38,7 +38,7 @@ namespace osu.Game.Overlays.Profile.Sections { header = new ProfileSubsectionHeader(headerText, counterVisibilityState) { - Alpha = string.IsNullOrEmpty(headerText) ? 0 : 1 + Alpha = string.IsNullOrEmpty(headerText.ToString()) ? 0 : 1 }, CreateContent() }; From 148eb890fff5707f28d586121db754561943d81d Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 16:20:37 +0200 Subject: [PATCH 0450/2442] Localise Historical section. --- .../Sections/Historical/ChartProfileSubsection.cs | 5 +++-- .../Sections/Historical/DrawableMostPlayedBeatmap.cs | 3 ++- .../Historical/PaginatedMostPlayedBeatmapContainer.cs | 3 ++- .../Sections/Historical/PlayHistorySubsection.cs | 6 ++++-- .../Profile/Sections/Historical/ProfileLineChart.cs | 3 ++- .../Profile/Sections/Historical/ReplaysSubsection.cs | 6 ++++-- .../Profile/Sections/Historical/UserHistoryGraph.cs | 11 ++++++----- .../Overlays/Profile/Sections/HistoricalSection.cs | 2 +- 8 files changed, 24 insertions(+), 15 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs index a48036dcbb..986b3d9874 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; using osu.Game.Users; using static osu.Game.Users.User; @@ -18,9 +19,9 @@ namespace osu.Game.Overlays.Profile.Sections.Historical /// /// Text describing the value being plotted on the graph, which will be displayed as a prefix to the value in the history graph tooltip. /// - protected abstract string GraphCounterName { get; } + protected abstract LocalisableString GraphCounterName { get; } - protected ChartProfileSubsection(Bindable user, string headerText) + protected ChartProfileSubsection(Bindable user, LocalisableString headerText) : base(user, headerText) { } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs index 6f1869966a..a419bef233 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs @@ -13,6 +13,7 @@ using osu.Game.Graphics.Sprites; using osuTK; using osu.Framework.Graphics.Cursor; using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Profile.Sections.Historical { @@ -143,7 +144,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical private class PlayCountText : CompositeDrawable, IHasTooltip { - public LocalisableString TooltipText => "times played"; + public LocalisableString TooltipText => UsersStrings.ShowExtraHistoricalMostPlayedCount; public PlayCountText(int playCount) { diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index eeb14e5e4f..d0979526da 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; namespace osu.Game.Overlays.Profile.Sections.Historical @@ -16,7 +17,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical public class PaginatedMostPlayedBeatmapContainer : PaginatedProfileSubsection { public PaginatedMostPlayedBeatmapContainer(Bindable user) - : base(user, "Most Played Beatmaps") + : base(user, UsersStrings.ShowExtraHistoricalMostPlayedTitle) { ItemsPerPage = 5; } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PlayHistorySubsection.cs b/osu.Game/Overlays/Profile/Sections/Historical/PlayHistorySubsection.cs index dfd29db693..83c005970e 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PlayHistorySubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PlayHistorySubsection.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; using static osu.Game.Users.User; @@ -9,10 +11,10 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { public class PlayHistorySubsection : ChartProfileSubsection { - protected override string GraphCounterName => "Plays"; + protected override LocalisableString GraphCounterName => UsersStrings.ShowExtraHistoricalMonthlyPlaycountsCountLabel; public PlayHistorySubsection(Bindable user) - : base(user, "Play History") + : base(user, UsersStrings.ShowExtraHistoricalMonthlyPlaycountsTitle) { } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index eb5deb2802..af39251781 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -13,6 +13,7 @@ using osu.Game.Graphics; using osu.Framework.Graphics.Shapes; using osuTK; using static osu.Game.Users.User; +using osu.Framework.Localisation; namespace osu.Game.Overlays.Profile.Sections.Historical { @@ -42,7 +43,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical private readonly Container rowLinesContainer; private readonly Container columnLinesContainer; - public ProfileLineChart(string graphCounterName) + public ProfileLineChart(LocalisableString graphCounterName) { RelativeSizeAxes = Axes.X; Height = 250; diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ReplaysSubsection.cs b/osu.Game/Overlays/Profile/Sections/Historical/ReplaysSubsection.cs index 1c28306f17..76d5f73bd7 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ReplaysSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ReplaysSubsection.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; using static osu.Game.Users.User; @@ -9,10 +11,10 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { public class ReplaysSubsection : ChartProfileSubsection { - protected override string GraphCounterName => "Replays Watched"; + protected override LocalisableString GraphCounterName => UsersStrings.ShowExtraHistoricalReplaysWatchedCountsCountLabel; public ReplaysSubsection(Bindable user) - : base(user, "Replays Watched History") + : base(user, UsersStrings.ShowExtraHistoricalReplaysWatchedCountsTitle) { } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs b/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs index 52831b4243..d626c63fed 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs @@ -5,13 +5,14 @@ using System; using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; +using osu.Framework.Localisation; using static osu.Game.Users.User; namespace osu.Game.Overlays.Profile.Sections.Historical { public class UserHistoryGraph : UserGraph { - private readonly string tooltipCounterName; + private readonly LocalisableString tooltipCounterName; [CanBeNull] public UserHistoryCount[] Values @@ -19,7 +20,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical set => Data = value?.Select(v => new KeyValuePair(v.Date, v.Count)).ToArray(); } - public UserHistoryGraph(string tooltipCounterName) + public UserHistoryGraph(LocalisableString tooltipCounterName) { this.tooltipCounterName = tooltipCounterName; } @@ -40,9 +41,9 @@ namespace osu.Game.Overlays.Profile.Sections.Historical protected class HistoryGraphTooltip : UserGraphTooltip { - private readonly string tooltipCounterName; + private readonly LocalisableString tooltipCounterName; - public HistoryGraphTooltip(string tooltipCounterName) + public HistoryGraphTooltip(LocalisableString tooltipCounterName) : base(tooltipCounterName) { this.tooltipCounterName = tooltipCounterName; @@ -61,7 +62,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical private class TooltipDisplayContent { - public string Name; + public LocalisableString Name; public string Count; public string Date; } diff --git a/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs b/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs index 09ca492aa9..cba25c0a8b 100644 --- a/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs +++ b/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Profile.Sections { new PlayHistorySubsection(User), new PaginatedMostPlayedBeatmapContainer(User), - new PaginatedScoreContainer(ScoreType.Recent, User, "Recent Plays (24h)"), + new PaginatedScoreContainer(ScoreType.Recent, User, UsersStrings.ShowExtraHistoricalRecentPlaysTitle), new ReplaysSubsection(User) }; } From 2545275f7102e0607d81eaced1c00db66efb08c1 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 16:29:09 +0200 Subject: [PATCH 0451/2442] Partly localise Kudosu section. --- osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs index cdb24b784c..37de669b3b 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs @@ -12,6 +12,8 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Users; using osu.Framework.Allocation; +using osu.Game.Resources.Localisation.Web; +using osu.Framework.Localisation; namespace osu.Game.Overlays.Profile.Sections.Kudosu { @@ -37,7 +39,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu private class CountTotal : CountSection { public CountTotal() - : base("Total Kudosu Earned") + : base(UsersStrings.ShowExtraKudosuTotal) { DescriptionText.AddText("Based on how much of a contribution the user has made to beatmap moderation. See "); DescriptionText.AddLink("this page", "https://osu.ppy.sh/wiki/Kudosu"); @@ -56,7 +58,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu set => valueText.Text = value.ToString("N0"); } - public CountSection(string header) + public CountSection(LocalisableString header) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; From a7c280508f9480141da9b324edddf613e7c7c022 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 16:40:37 +0200 Subject: [PATCH 0452/2442] Throw instead of silently returning. --- osu.Game/Overlays/Profile/ProfileHeader.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 32bca68f0e..083725418e 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -1,6 +1,7 @@ // 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 osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -127,7 +128,7 @@ namespace osu.Game.Overlays.Profile return LayoutStrings.HeaderUsersModding; default: - return string.Empty; + throw new ArgumentOutOfRangeException(nameof(value), value, null); } } } From 77d8f240f8e33d75f2f92caf0c6d7e12b7465463 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 16:41:42 +0200 Subject: [PATCH 0453/2442] Use ctor default values. --- .../Profile/Sections/Recent/PaginatedRecentActivityContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs index 04c8d7483b..db2e6bc1e0 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Profile.Sections.Recent public class PaginatedRecentActivityContainer : PaginatedProfileSubsection { public PaginatedRecentActivityContainer(Bindable user) - : base(user, string.Empty, EventsStrings.Empty) + : base(user, missingText: EventsStrings.Empty) { ItemsPerPage = 10; } From 28845364a3380980a101f984f38312d792f9f376 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 16:52:35 +0200 Subject: [PATCH 0454/2442] Localise score weighting. --- .../Profile/Sections/Ranks/DrawableProfileWeightedScore.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs index 3afa79e59e..63305d004c 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs @@ -5,6 +5,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; using osu.Game.Scoring; using osuTK; @@ -51,7 +52,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks new OsuSpriteText { Font = OsuFont.GetFont(size: 12), - Text = $@"weighted {weight:0%}" + Text = UsersStrings.ShowExtraTopRanksPpWeight(weight.ToString("0%")) } } }; From 2c26248042e58a6db18531fbbe00980e7fdada8f Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 17 Jul 2021 17:36:49 +0200 Subject: [PATCH 0455/2442] Localise missing text of `PaginatedKudosuHistoryContainer`. --- .../Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs index 008d89d881..76cd7ed722 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs @@ -8,13 +8,14 @@ using osu.Framework.Bindables; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API; using System.Collections.Generic; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Profile.Sections.Kudosu { public class PaginatedKudosuHistoryContainer : PaginatedProfileSubsection { public PaginatedKudosuHistoryContainer(Bindable user) - : base(user, missingText: "This user hasn't received any kudosu!") + : base(user, missingText: UsersStrings.ShowExtraKudosuEntryEmpty) { ItemsPerPage = 5; } From 9a2fb8ca6c26daf8cb926f8744cc550496e7e5d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 17 Jul 2021 18:06:45 +0200 Subject: [PATCH 0456/2442] Add test coverage for null mod on seeding screen --- .../Screens/TestSceneSeedingScreen.cs | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneSeedingScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneSeedingScreen.cs index d414d8e36e..a18e73e38f 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneSeedingScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneSeedingScreen.cs @@ -1,9 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; +using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Testing; using osu.Game.Tournament.Models; +using osu.Game.Tournament.Screens.Ladder.Components; using osu.Game.Tournament.Screens.TeamIntro; namespace osu.Game.Tournament.Tests.Screens @@ -11,16 +15,41 @@ namespace osu.Game.Tournament.Tests.Screens public class TestSceneSeedingScreen : TournamentTestScene { [Cached] - private readonly LadderInfo ladder = new LadderInfo(); - - [BackgroundDependencyLoader] - private void load() + private readonly LadderInfo ladder = new LadderInfo { - Add(new SeedingScreen + Teams = + { + new TournamentTeam + { + FullName = { Value = @"Japan" }, + Acronym = { Value = "JPN" }, + SeedingResults = + { + new SeedingResult + { + // Mod intentionally left blank. + Seed = { Value = 4 } + }, + new SeedingResult + { + Mod = { Value = "DT" }, + Seed = { Value = 8 } + } + } + } + } + }; + + [Test] + public void TestBasic() + { + AddStep("create seeding screen", () => Add(new SeedingScreen { FillMode = FillMode.Fit, FillAspectRatio = 16 / 9f - }); + })); + + AddStep("set team to Japan", () => this.ChildrenOfType().Single().Current.Value = ladder.Teams.Single()); } } } From 714255e6d4059e72e46083328367e1d534726581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 17 Jul 2021 17:53:58 +0200 Subject: [PATCH 0457/2442] Fix seeding screen crashing on seedings with null mod --- .../Screens/TeamIntro/SeedingScreen.cs | 64 ++++++++++--------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs index 4f66d89b7f..2f82578586 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs @@ -179,44 +179,48 @@ namespace osu.Game.Tournament.Screens.TeamIntro [BackgroundDependencyLoader] private void load(TextureStore textures) { + FillFlowContainer row; + InternalChildren = new Drawable[] { - new FillFlowContainer + row = new FillFlowContainer { AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, Spacing = new Vector2(5), - Children = new Drawable[] - { - new Sprite - { - Texture = textures.Get($"mods/{mods.ToLower()}"), - Scale = new Vector2(0.5f) - }, - new Container - { - Size = new Vector2(50, 16), - CornerRadius = 10, - Masking = true, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = TournamentGame.ELEMENT_BACKGROUND_COLOUR, - }, - new TournamentSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = seeding.ToString("#,0"), - Colour = TournamentGame.ELEMENT_FOREGROUND_COLOUR - }, - } - }, - } }, }; + + if (!string.IsNullOrEmpty(mods)) + { + row.Add(new Sprite + { + Texture = textures.Get($"mods/{mods.ToLower()}"), + Scale = new Vector2(0.5f) + }); + } + + row.Add(new Container + { + Size = new Vector2(50, 16), + CornerRadius = 10, + Masking = true, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = TournamentGame.ELEMENT_BACKGROUND_COLOUR, + }, + new TournamentSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = seeding.ToString("#,0"), + Colour = TournamentGame.ELEMENT_FOREGROUND_COLOUR + }, + } + }); } } } From 1b4bff0d9fa52dec5b18f33158b4201bcb1ed2e2 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sun, 18 Jul 2021 00:28:32 +0800 Subject: [PATCH 0458/2442] Optimize code style Co-authored-by: frenzibyte Co-authored-by: bdach --- osu.Game/Localisation/NewsStrings.cs | 10 ---------- osu.Game/Localisation/WikiStrings.cs | 29 ---------------------------- osu.Game/Overlays/News/NewsHeader.cs | 11 ++++------- osu.Game/Overlays/Wiki/WikiHeader.cs | 10 ++++------ 4 files changed, 8 insertions(+), 52 deletions(-) delete mode 100644 osu.Game/Localisation/WikiStrings.cs diff --git a/osu.Game/Localisation/NewsStrings.cs b/osu.Game/Localisation/NewsStrings.cs index 8cad6015cf..4f1fe6e776 100644 --- a/osu.Game/Localisation/NewsStrings.cs +++ b/osu.Game/Localisation/NewsStrings.cs @@ -9,16 +9,6 @@ namespace osu.Game.Localisation { private const string prefix = @"osu.Game.Resources.Localisation.News"; - /// - /// "frontpage" - /// - public static LocalisableString FrontPageString => new TranslatableString(getKey(@"front_page"), @"frontpage"); - - /// - /// "news" - /// - public static LocalisableString HeaderTitle => new TranslatableString(getKey(@"header_title"), @"news"); - /// /// "join the real-time discussion" /// diff --git a/osu.Game/Localisation/WikiStrings.cs b/osu.Game/Localisation/WikiStrings.cs deleted file mode 100644 index 6176fd4e85..0000000000 --- a/osu.Game/Localisation/WikiStrings.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Localisation; - -namespace osu.Game.Localisation -{ - public static class WikiStrings - { - private const string prefix = @"osu.Game.Resources.Localisation.Wiki"; - - /// - /// "index" - /// - public static LocalisableString IndexPageString => new TranslatableString(getKey(@"index_page"), @"index"); - - /// - /// "wiki" - /// - public static LocalisableString HeaderTitle => new TranslatableString(getKey(@"header_title"), @"wiki"); - - /// - /// "knowledge base" - /// - public static LocalisableString HeaderDescription => new TranslatableString(getKey(@"header_description"), @"knowledge base"); - - private static string getKey(string key) => $"{prefix}:{key}"; - } -} diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs index 0b0277f984..7f08300912 100644 --- a/osu.Game/Overlays/News/NewsHeader.cs +++ b/osu.Game/Overlays/News/NewsHeader.cs @@ -5,16 +5,13 @@ using System; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Localisation; -using osu.Game.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.News { public class NewsHeader : BreadcrumbControlOverlayHeader { - public static LocalisableString FrontPageString => NewsStrings.FrontPageString; - public static LocalisableString HeaderTitle => NewsStrings.HeaderTitle; - public static LocalisableString HeaderDescription => NewsStrings.HeaderDescription; - + public LocalisableString FrontPageString => osu.Game.Resources.Localisation.Web.NewsStrings.IndexTitleInfo; public Action ShowFrontPage; private readonly Bindable article = new Bindable(); @@ -65,8 +62,8 @@ namespace osu.Game.Overlays.News { public NewsHeaderTitle() { - Title = HeaderTitle; - Description = HeaderDescription; + Title = LayoutStrings.MenuHomeNewsIndex; + Description = osu.Game.Localisation.NewsStrings.HeaderDescription; IconTexture = "Icons/Hexacons/news"; } } diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index 4bec42b6ce..f24d59a8d3 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -6,17 +6,15 @@ using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Localisation; -using osu.Game.Localisation; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Wiki { public class WikiHeader : BreadcrumbControlOverlayHeader { private const string index_path = "Main_Page"; - public static LocalisableString IndexPageString => WikiStrings.IndexPageString; - public static LocalisableString HeaderTitle => WikiStrings.HeaderTitle; - public static LocalisableString HeaderDescription => WikiStrings.HeaderDescription; + public LocalisableString IndexPageString => LayoutStrings.HeaderHelpIndex; public readonly Bindable WikiPageData = new Bindable(); @@ -77,8 +75,8 @@ namespace osu.Game.Overlays.Wiki { public WikiHeaderTitle() { - Title = HeaderTitle; - Description = HeaderDescription; + Title = LayoutStrings.MenuHelpGetWiki; + Description = PageTitleStrings.MainWikiControllerDefault; IconTexture = "Icons/Hexacons/wiki"; } } From 53fe61504cbc53fd77207a4caec081cc76855d63 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sun, 18 Jul 2021 01:35:54 +0800 Subject: [PATCH 0459/2442] Add localisation for ChangelogHeader --- osu.Game/Localisation/ChangelogStrings.cs | 19 +++++++++++++++++++ .../Overlays/Changelog/ChangelogHeader.cs | 14 ++++++++------ 2 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 osu.Game/Localisation/ChangelogStrings.cs diff --git a/osu.Game/Localisation/ChangelogStrings.cs b/osu.Game/Localisation/ChangelogStrings.cs new file mode 100644 index 0000000000..b720670ded --- /dev/null +++ b/osu.Game/Localisation/ChangelogStrings.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class ChangelogStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.Changelog"; + + /// + /// "track recent dev updates in the osu! ecosystem" + /// + public static LocalisableString HeaderDescription => new TranslatableString(getKey(@"header_description"), @"track recent dev updates in the osu! ecosystem"); + + private static string getKey(string key) => $"{prefix}:{key}"; + } +} diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index f4be4328e7..c1c81046ed 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -9,7 +9,9 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Changelog { @@ -21,16 +23,16 @@ namespace osu.Game.Overlays.Changelog public ChangelogUpdateStreamControl Streams; - private const string listing_string = "listing"; + public LocalisableString ListingString => LayoutStrings.HeaderChangelogIndex; private Box streamsBackground; public ChangelogHeader() { - TabControl.AddItem(listing_string); + TabControl.AddItem(ListingString); Current.ValueChanged += e => { - if (e.NewValue == listing_string) + if (e.NewValue == ListingString) ListingSelected?.Invoke(); }; @@ -63,7 +65,7 @@ namespace osu.Game.Overlays.Changelog } else { - Current.Value = listing_string; + Current.Value = ListingString; Streams.Current.Value = null; } } @@ -114,8 +116,8 @@ namespace osu.Game.Overlays.Changelog { public ChangelogHeaderTitle() { - Title = "changelog"; - Description = "track recent dev updates in the osu! ecosystem"; + Title = LayoutStrings.MenuHomeChangelogIndex; + Description = osu.Game.Localisation.ChangelogStrings.HeaderDescription; IconTexture = "Icons/Hexacons/devtools"; } } From 224c37bdc3e67e0f110c3c679080dddcaa0113b6 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sun, 18 Jul 2021 02:15:14 +0800 Subject: [PATCH 0460/2442] Add localisation for RankingOverlay --- .../Rankings/RankingsOverlayHeader.cs | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs index 92e22f5873..4766d1ab3c 100644 --- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs @@ -1,9 +1,12 @@ // 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 osu.Framework.Graphics; using osu.Framework.Bindables; +using osu.Framework.Localisation; using osu.Game.Rulesets; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; namespace osu.Game.Overlays.Rankings @@ -29,13 +32,14 @@ namespace osu.Game.Overlays.Rankings { public RankingsTitle() { - Title = "ranking"; + Title = LayoutStrings.MenuRankingsDefault; Description = "find out who's the best right now"; IconTexture = "Icons/Hexacons/rankings"; } } } + [LocalisableEnum(typeof(RankingsScopeEnumLocalisationMapper))] public enum RankingsScope { Performance, @@ -43,4 +47,29 @@ namespace osu.Game.Overlays.Rankings Score, Country } + + public class RankingsScopeEnumLocalisationMapper : EnumLocalisationMapper + { + public override LocalisableString Map(RankingsScope value) + { + switch (value) + { + case RankingsScope.Performance: + return LayoutStrings.MenuRankingsIndex; + + case RankingsScope.Spotlights: + return LayoutStrings.MenuRankingsCharts; + + case RankingsScope.Score: + return LayoutStrings.MenuRankingsScore; + + case RankingsScope.Country: + return LayoutStrings.MenuRankingsCountry; + + default: + throw new ArgumentOutOfRangeException(nameof(value), value, null); + } + } + } + } From 93e79d122fe04e0ab5080eb8e042c313ac27c17d Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sun, 18 Jul 2021 02:23:12 +0800 Subject: [PATCH 0461/2442] Move strings together --- ...Strings.cs => HeaderDescriptionStrings.cs} | 11 ++++++++--- osu.Game/Localisation/NewsStrings.cs | 19 ------------------- .../Overlays/Changelog/ChangelogHeader.cs | 3 ++- osu.Game/Overlays/News/NewsHeader.cs | 3 ++- 4 files changed, 12 insertions(+), 24 deletions(-) rename osu.Game/Localisation/{ChangelogStrings.cs => HeaderDescriptionStrings.cs} (50%) delete mode 100644 osu.Game/Localisation/NewsStrings.cs diff --git a/osu.Game/Localisation/ChangelogStrings.cs b/osu.Game/Localisation/HeaderDescriptionStrings.cs similarity index 50% rename from osu.Game/Localisation/ChangelogStrings.cs rename to osu.Game/Localisation/HeaderDescriptionStrings.cs index b720670ded..6dbc28a619 100644 --- a/osu.Game/Localisation/ChangelogStrings.cs +++ b/osu.Game/Localisation/HeaderDescriptionStrings.cs @@ -5,14 +5,19 @@ using osu.Framework.Localisation; namespace osu.Game.Localisation { - public static class ChangelogStrings + public static class HeaderDescriptionStrings { - private const string prefix = @"osu.Game.Resources.Localisation.Changelog"; + private const string prefix = @"osu.Game.Resources.Localisation.HeaderDescription"; /// /// "track recent dev updates in the osu! ecosystem" /// - public static LocalisableString HeaderDescription => new TranslatableString(getKey(@"header_description"), @"track recent dev updates in the osu! ecosystem"); + public static LocalisableString Changelog => new TranslatableString(getKey(@"changelog"), @"track recent dev updates in the osu! ecosystem"); + + /// + /// "get up-to-date on community happenings" + /// + public static LocalisableString News => new TranslatableString(getKey(@"news"), @"get up-to-date on community happenings"); private static string getKey(string key) => $"{prefix}:{key}"; } diff --git a/osu.Game/Localisation/NewsStrings.cs b/osu.Game/Localisation/NewsStrings.cs deleted file mode 100644 index 4f1fe6e776..0000000000 --- a/osu.Game/Localisation/NewsStrings.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Localisation; - -namespace osu.Game.Localisation -{ - public static class NewsStrings - { - private const string prefix = @"osu.Game.Resources.Localisation.News"; - - /// - /// "join the real-time discussion" - /// - public static LocalisableString HeaderDescription => new TranslatableString(getKey(@"header_description"), @"get up-to-date on community happenings"); - - private static string getKey(string key) => $"{prefix}:{key}"; - } -} diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index c1c81046ed..a282c77bd6 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; +using osu.Game.Localisation; using osu.Game.Online.API.Requests.Responses; using osu.Game.Resources.Localisation.Web; @@ -117,7 +118,7 @@ namespace osu.Game.Overlays.Changelog public ChangelogHeaderTitle() { Title = LayoutStrings.MenuHomeChangelogIndex; - Description = osu.Game.Localisation.ChangelogStrings.HeaderDescription; + Description = HeaderDescriptionStrings.Changelog; IconTexture = "Icons/Hexacons/devtools"; } } diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs index 7f08300912..b6e49811ad 100644 --- a/osu.Game/Overlays/News/NewsHeader.cs +++ b/osu.Game/Overlays/News/NewsHeader.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Localisation; +using osu.Game.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.News @@ -63,7 +64,7 @@ namespace osu.Game.Overlays.News public NewsHeaderTitle() { Title = LayoutStrings.MenuHomeNewsIndex; - Description = osu.Game.Localisation.NewsStrings.HeaderDescription; + Description = HeaderDescriptionStrings.News; IconTexture = "Icons/Hexacons/news"; } } From 3b48975f1e7f1b0ed760c56a964bdc8aea75ed34 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sun, 18 Jul 2021 02:32:23 +0800 Subject: [PATCH 0462/2442] Add localisation for RankingOverlayHeader --- osu.Game/Localisation/HeaderDescriptionStrings.cs | 5 +++++ osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Localisation/HeaderDescriptionStrings.cs b/osu.Game/Localisation/HeaderDescriptionStrings.cs index 6dbc28a619..88ee4a0d75 100644 --- a/osu.Game/Localisation/HeaderDescriptionStrings.cs +++ b/osu.Game/Localisation/HeaderDescriptionStrings.cs @@ -14,6 +14,11 @@ namespace osu.Game.Localisation /// public static LocalisableString Changelog => new TranslatableString(getKey(@"changelog"), @"track recent dev updates in the osu! ecosystem"); + /// + /// "find out who's the best right now" + /// + public static LocalisableString Rankings => new TranslatableString(getKey(@"rankings"), @"find out who's the best right now"); + /// /// "get up-to-date on community happenings" /// diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs index 4766d1ab3c..92750d8806 100644 --- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Graphics; using osu.Framework.Bindables; using osu.Framework.Localisation; +using osu.Game.Localisation; using osu.Game.Rulesets; using osu.Game.Resources.Localisation.Web; using osu.Game.Users; @@ -33,7 +34,7 @@ namespace osu.Game.Overlays.Rankings public RankingsTitle() { Title = LayoutStrings.MenuRankingsDefault; - Description = "find out who's the best right now"; + Description = HeaderDescriptionStrings.Rankings; IconTexture = "Icons/Hexacons/rankings"; } } From f588607ed978aeb333e3f06e69ed833b7b2525f6 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sun, 18 Jul 2021 02:38:15 +0800 Subject: [PATCH 0463/2442] Add localisation for BeatmapListingHeader --- osu.Game/Localisation/HeaderDescriptionStrings.cs | 5 +++++ osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs | 7 +++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Localisation/HeaderDescriptionStrings.cs b/osu.Game/Localisation/HeaderDescriptionStrings.cs index 88ee4a0d75..c84822de53 100644 --- a/osu.Game/Localisation/HeaderDescriptionStrings.cs +++ b/osu.Game/Localisation/HeaderDescriptionStrings.cs @@ -9,6 +9,11 @@ namespace osu.Game.Localisation { private const string prefix = @"osu.Game.Resources.Localisation.HeaderDescription"; + /// + /// "browse for new beatmaps" + /// + public static LocalisableString BeatmapListing => new TranslatableString(getKey(@"beatmap_listing"), @"browse for new beatmaps"); + /// /// "track recent dev updates in the osu! ecosystem" /// diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs index 6a9a71210a..48ecbce7c1 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs @@ -1,6 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Game.Localisation; +using osu.Game.Resources.Localisation.Web; + namespace osu.Game.Overlays.BeatmapListing { public class BeatmapListingHeader : OverlayHeader @@ -11,8 +14,8 @@ namespace osu.Game.Overlays.BeatmapListing { public BeatmapListingTitle() { - Title = "beatmap listing"; - Description = "browse for new beatmaps"; + Title = PageTitleStrings.MainBeatmapsetsControllerIndex; + Description = HeaderDescriptionStrings.BeatmapListing; IconTexture = "Icons/Hexacons/beatmap"; } } From f80b18377775e3bf52b4370d0bd7459dc383112c Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sun, 18 Jul 2021 02:44:09 +0800 Subject: [PATCH 0464/2442] Add localisation for DashboardOverlayHeader --- osu.Game/Localisation/HeaderDescriptionStrings.cs | 5 +++++ osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Localisation/HeaderDescriptionStrings.cs b/osu.Game/Localisation/HeaderDescriptionStrings.cs index c84822de53..6ed703191a 100644 --- a/osu.Game/Localisation/HeaderDescriptionStrings.cs +++ b/osu.Game/Localisation/HeaderDescriptionStrings.cs @@ -19,6 +19,11 @@ namespace osu.Game.Localisation /// public static LocalisableString Changelog => new TranslatableString(getKey(@"changelog"), @"track recent dev updates in the osu! ecosystem"); + /// + /// "view your friends and other information" + /// + public static LocalisableString Dashboard => new TranslatableString(getKey(@"dashboard"), @"view your friends and other information"); + /// /// "find out who's the best right now" /// diff --git a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs index 056d4ad6f7..284e95ad84 100644 --- a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs +++ b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs @@ -4,6 +4,7 @@ using System; using System.ComponentModel; using osu.Framework.Localisation; +using osu.Game.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Dashboard @@ -17,7 +18,7 @@ namespace osu.Game.Overlays.Dashboard public DashboardTitle() { Title = HomeStrings.UserTitle; - Description = "view your friends and other information"; + Description = HeaderDescriptionStrings.Dashboard; IconTexture = "Icons/Hexacons/social"; } } From 49f0c707f6cb4e5676105f88e015966dbc41794c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 18 Jul 2021 22:34:28 +0900 Subject: [PATCH 0465/2442] Move approach circle hiding within `BeginAbsoluteSequence` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 5fa3bc4520..471847dbd6 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -147,21 +147,22 @@ namespace osu.Game.Rulesets.Osu.Mods double startTime = circle.HitObject.StartTime; double preempt = circle.HitObject.TimePreempt; - using (drawable.BeginAbsoluteSequence(startTime - preempt)) + using (circle.BeginAbsoluteSequence(startTime - preempt)) { // initial state - drawable.ScaleTo(0.5f) + circle.ScaleTo(0.5f) .FadeColour(OsuColour.Gray(0.5f)); // scale to final size - drawable.ScaleTo(1f, preempt); + circle.ScaleTo(1f, preempt); + + // Remove approach circles + circle.ApproachCircle.Hide(); } - using (drawable.BeginAbsoluteSequence(startTime - controlPointInfo.TimingPointAt(startTime).BeatLength - undim_duration)) - drawable.FadeColour(Color4.White, undim_duration); + using (circle.BeginAbsoluteSequence(startTime - controlPointInfo.TimingPointAt(startTime).BeatLength - undim_duration)) + circle.FadeColour(Color4.White, undim_duration); - // Remove approach circles - circle.ApproachCircle.Hide(); } #endregion From fdebe4b94ac30050334dc6330308c54506a50079 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sun, 18 Jul 2021 22:01:26 +0800 Subject: [PATCH 0466/2442] Code formatting fixes --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 471847dbd6..918b9b1c94 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -151,7 +151,7 @@ namespace osu.Game.Rulesets.Osu.Mods { // initial state circle.ScaleTo(0.5f) - .FadeColour(OsuColour.Gray(0.5f)); + .FadeColour(OsuColour.Gray(0.5f)); // scale to final size circle.ScaleTo(1f, preempt); @@ -162,7 +162,6 @@ namespace osu.Game.Rulesets.Osu.Mods using (circle.BeginAbsoluteSequence(startTime - controlPointInfo.TimingPointAt(startTime).BeatLength - undim_duration)) circle.FadeColour(Color4.White, undim_duration); - } #endregion From ee220feecf09781876973366ea136b22c6d5b809 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Sun, 18 Jul 2021 16:04:23 +0200 Subject: [PATCH 0467/2442] Avoid using guesses to determine whether inputs blocked --- .../Compose/Components/BlueprintContainer.cs | 12 ++++--- .../Timeline/TimelineBlueprintContainer.cs | 33 +++++-------------- 2 files changed, 15 insertions(+), 30 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 185f029d14..06cbf7c2e0 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -110,9 +110,9 @@ namespace osu.Game.Screens.Edit.Compose.Components bool selectionPerformed = performMouseDownActions(e); // even if a selection didn't occur, a drag event may still move the selection. - prepareSelectionMovement(); + bool movementPossible = prepareSelectionMovement(); - return selectionPerformed || e.Button == MouseButton.Left; + return selectionPerformed || movementPossible; } protected SelectionBlueprint ClickedBlueprint { get; private set; } @@ -427,19 +427,21 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// Attempts to begin the movement of any selected blueprints. /// - private void prepareSelectionMovement() + /// Whether a movement is possible. + private bool prepareSelectionMovement() { if (!SelectionHandler.SelectedBlueprints.Any()) - return; + return false; // Any selected blueprint that is hovered can begin the movement of the group, however only the first item (according to SortForMovement) is used for movement. // A special case is added for when a click selection occurred before the drag if (!clickSelectionBegan && !SelectionHandler.SelectedBlueprints.Any(b => b.IsHovered)) - return; + return false; // Movement is tracked from the blueprint of the earliest item, since it only makes sense to distance snap from that item movementBlueprints = SortForMovement(SelectionHandler.SelectedBlueprints).ToArray(); movementBlueprintOriginalPositions = movementBlueprints.Select(m => m.ScreenSpaceSelectionPoint).ToArray(); + return true; } /// diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index abd7f5923c..ae33eaa355 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -35,15 +35,10 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private Bindable placement; private SelectionBlueprint placementBlueprint; - // We want children to be able to be clicked and dragged - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; + private SelectableAreaBackground backgroundBox; - // This drawable itself should still check whether the mouse is over it - private bool shouldHandleInputAt(Vector2 screenSpacePos) - { - float localY = ToLocalSpace(screenSpacePos).Y; - return DrawRectangle.Top <= localY && DrawRectangle.Bottom >= localY; - } + // We want children to be able to be clicked and dragged, regardless of this drawable's size + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; public TimelineBlueprintContainer(HitObjectComposer composer) : base(composer) @@ -58,7 +53,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [BackgroundDependencyLoader] private void load() { - AddInternal(new SelectableAreaBackground + AddInternal(backgroundBox = new SelectableAreaBackground { Colour = Color4.Black, Depth = float.MaxValue, @@ -97,25 +92,13 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override Container> CreateSelectionBlueprintContainer() => new TimelineSelectionBlueprintContainer { RelativeSizeAxes = Axes.Both }; - protected override bool OnMouseDown(MouseDownEvent e) - { - int selectionCount = SelectedItems.Count; - - // We let BlueprintContainer attempt a HitObject selection - // If it fails, we'll pass it this input back to the timeline so it can be dragged - // We know it failed if the selection count is unchanged after the selection attempt - if (base.OnMouseDown(e) && selectionCount != SelectedItems.Count) - return true; - - return false; - } - protected override bool OnDragStart(DragStartEvent e) { - if (!shouldHandleInputAt(e.ScreenSpaceMouseDownPosition)) - return false; + // We should only allow BlueprintContainer to create a drag box if the mouse is within selection bounds + if (backgroundBox.ReceivePositionalInputAt(e.ScreenSpaceMouseDownPosition)) + return base.OnDragStart(e); - return base.OnDragStart(e); + return false; } protected override void OnDrag(DragEvent e) From 2e2a2bdd99bda1065e7f79f3064d6dcf40cc3dba Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Sun, 18 Jul 2021 17:20:30 +0200 Subject: [PATCH 0468/2442] Allow moving timeline selection when mousedown event is outside of blueprint container --- .../Timeline/TimelineBlueprintContainer.cs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index ae33eaa355..8e0b39513a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -35,8 +35,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private Bindable placement; private SelectionBlueprint placementBlueprint; - private SelectableAreaBackground backgroundBox; - // We want children to be able to be clicked and dragged, regardless of this drawable's size public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; @@ -53,7 +51,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [BackgroundDependencyLoader] private void load() { - AddInternal(backgroundBox = new SelectableAreaBackground + AddInternal(new SelectableAreaBackground { Colour = Color4.Black, Depth = float.MaxValue, @@ -92,15 +90,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override Container> CreateSelectionBlueprintContainer() => new TimelineSelectionBlueprintContainer { RelativeSizeAxes = Axes.Both }; - protected override bool OnDragStart(DragStartEvent e) - { - // We should only allow BlueprintContainer to create a drag box if the mouse is within selection bounds - if (backgroundBox.ReceivePositionalInputAt(e.ScreenSpaceMouseDownPosition)) - return base.OnDragStart(e); - - return false; - } - protected override void OnDrag(DragEvent e) { handleScrollViaDrag(e); @@ -319,6 +308,10 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public override bool HandleDrag(MouseButtonEvent e) { + // The dragbox should only be active if the mouseDownPosition.Y is within this drawable's bounds. + if (DrawRectangle.Top > e.MouseDownPosition.Y || DrawRectangle.Bottom < e.MouseDownPosition.Y) + return false; + selectionStart ??= e.MouseDownPosition.X / timeline.CurrentZoom; // only calculate end when a transition is not in progress to avoid bouncing. From 1cc9e504cf2bc922a19253a007d12be80646380d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 02:17:21 +0900 Subject: [PATCH 0469/2442] Remove incorrectly added null coalesce --- osu.Game/Overlays/Wiki/WikiHeader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index 4eac60f0eb..811e654387 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -55,7 +55,7 @@ namespace osu.Game.Overlays.Wiki private void onCurrentChange(ValueChangedEvent e) { - if (e?.NewValue == TabControl.Items.LastOrDefault()) + if (e.NewValue == TabControl.Items.LastOrDefault()) return; if (e.NewValue == IndexPageString) From 9257cd7fad4c3883389d8620daffd78b1e18cee7 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 18 Jul 2021 19:18:06 +0200 Subject: [PATCH 0470/2442] Revert the use of an enum in `ProfileHeader`. --- osu.Game/Overlays/Profile/ProfileHeader.cs | 31 +++------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 083725418e..88c785d4e1 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.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 osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -15,7 +14,7 @@ using osu.Game.Users; namespace osu.Game.Overlays.Profile { - public class ProfileHeader : TabControlOverlayHeader + public class ProfileHeader : TabControlOverlayHeader { private UserCoverBackground coverContainer; @@ -30,6 +29,9 @@ namespace osu.Game.Overlays.Profile User.ValueChanged += e => updateDisplay(e.NewValue); + TabControl.AddItem(LayoutStrings.HeaderUsersShow); + TabControl.AddItem(LayoutStrings.HeaderUsersModding); + centreHeaderContainer.DetailsVisible.BindValueChanged(visible => detailHeaderContainer.Expanded = visible.NewValue, true); } @@ -107,29 +109,4 @@ namespace osu.Game.Overlays.Profile protected override double LoadDelay => 0; } } - - [LocalisableEnum(typeof(ProfileHeaderTabEnumLocalisationMapper))] - public enum ProfileHeaderTab - { - Info, - Modding, - } - - public class ProfileHeaderTabEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(ProfileHeaderTab value) - { - switch (value) - { - case ProfileHeaderTab.Info: - return LayoutStrings.HeaderUsersShow; - - case ProfileHeaderTab.Modding: - return LayoutStrings.HeaderUsersModding; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } From 80885301a391e57d5d413337e87b1495da7624f1 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 18 Jul 2021 19:36:34 +0200 Subject: [PATCH 0471/2442] Fix codefactor issues. --- osu.Game/Overlays/Profile/ProfileHeader.cs | 1 - .../Overlays/Profile/Sections/Historical/ProfileLineChart.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 88c785d4e1..815f9fdafc 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -32,7 +32,6 @@ namespace osu.Game.Overlays.Profile TabControl.AddItem(LayoutStrings.HeaderUsersShow); TabControl.AddItem(LayoutStrings.HeaderUsersModding); - centreHeaderContainer.DetailsVisible.BindValueChanged(visible => detailHeaderContainer.Expanded = visible.NewValue, true); } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index af39251781..e6fd09301e 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -12,8 +12,8 @@ using osu.Framework.Allocation; using osu.Game.Graphics; using osu.Framework.Graphics.Shapes; using osuTK; -using static osu.Game.Users.User; using osu.Framework.Localisation; +using static osu.Game.Users.User; namespace osu.Game.Overlays.Profile.Sections.Historical { From fb5d25405e82013fcc71bd06786dc68d94435662 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 18 Jul 2021 21:52:16 +0200 Subject: [PATCH 0472/2442] Replace calls to obsoleted `GetOrDefault()` extension --- .../Difficulty/CatchPerformanceCalculator.cs | 11 +++++------ .../Difficulty/ManiaPerformanceCalculator.cs | 13 ++++++------- .../Difficulty/OsuPerformanceCalculator.cs | 9 ++++----- .../Difficulty/TaikoPerformanceCalculator.cs | 9 ++++----- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 11 +++++------ osu.Game/Scoring/ScoreInfo.cs | 7 +++---- osu.Game/Scoring/ScoreManager.cs | 3 +-- 7 files changed, 28 insertions(+), 35 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs index fdd6ac0857..439890dac2 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using osu.Framework.Extensions; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; @@ -33,11 +32,11 @@ namespace osu.Game.Rulesets.Catch.Difficulty { mods = Score.Mods; - fruitsHit = Score.Statistics.GetOrDefault(HitResult.Great); - ticksHit = Score.Statistics.GetOrDefault(HitResult.LargeTickHit); - tinyTicksHit = Score.Statistics.GetOrDefault(HitResult.SmallTickHit); - tinyTicksMissed = Score.Statistics.GetOrDefault(HitResult.SmallTickMiss); - misses = Score.Statistics.GetOrDefault(HitResult.Miss); + fruitsHit = Score.Statistics.GetValueOrDefault(HitResult.Great); + ticksHit = Score.Statistics.GetValueOrDefault(HitResult.LargeTickHit); + tinyTicksHit = Score.Statistics.GetValueOrDefault(HitResult.SmallTickHit); + tinyTicksMissed = Score.Statistics.GetValueOrDefault(HitResult.SmallTickMiss); + misses = Score.Statistics.GetValueOrDefault(HitResult.Miss); // We are heavily relying on aim in catch the beat double value = Math.Pow(5.0 * Math.Max(1.0, Attributes.StarRating / 0.0049) - 4.0, 2.0) / 100000.0; diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs index 405ac56e94..b04ff3548f 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using osu.Framework.Extensions; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; @@ -37,12 +36,12 @@ namespace osu.Game.Rulesets.Mania.Difficulty { mods = Score.Mods; scaledScore = Score.TotalScore; - countPerfect = Score.Statistics.GetOrDefault(HitResult.Perfect); - countGreat = Score.Statistics.GetOrDefault(HitResult.Great); - countGood = Score.Statistics.GetOrDefault(HitResult.Good); - countOk = Score.Statistics.GetOrDefault(HitResult.Ok); - countMeh = Score.Statistics.GetOrDefault(HitResult.Meh); - countMiss = Score.Statistics.GetOrDefault(HitResult.Miss); + countPerfect = Score.Statistics.GetValueOrDefault(HitResult.Perfect); + countGreat = Score.Statistics.GetValueOrDefault(HitResult.Great); + countGood = Score.Statistics.GetValueOrDefault(HitResult.Good); + countOk = Score.Statistics.GetValueOrDefault(HitResult.Ok); + countMeh = Score.Statistics.GetValueOrDefault(HitResult.Meh); + countMiss = Score.Statistics.GetValueOrDefault(HitResult.Miss); IEnumerable scoreIncreaseMods = Ruleset.GetModsFor(ModType.DifficultyIncrease); diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 8a3c426381..a76db4abe3 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using osu.Framework.Extensions; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; @@ -36,10 +35,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty mods = Score.Mods; accuracy = Score.Accuracy; scoreMaxCombo = Score.MaxCombo; - countGreat = Score.Statistics.GetOrDefault(HitResult.Great); - countOk = Score.Statistics.GetOrDefault(HitResult.Ok); - countMeh = Score.Statistics.GetOrDefault(HitResult.Meh); - countMiss = Score.Statistics.GetOrDefault(HitResult.Miss); + countGreat = Score.Statistics.GetValueOrDefault(HitResult.Great); + countOk = Score.Statistics.GetValueOrDefault(HitResult.Ok); + countMeh = Score.Statistics.GetValueOrDefault(HitResult.Meh); + countMiss = Score.Statistics.GetValueOrDefault(HitResult.Miss); // Custom multipliers for NoFail and SpunOut. double multiplier = 1.12; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs index 6117ed1673..90dd733dfd 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using osu.Framework.Extensions; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; @@ -31,10 +30,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty public override double Calculate(Dictionary categoryDifficulty = null) { mods = Score.Mods; - countGreat = Score.Statistics.GetOrDefault(HitResult.Great); - countOk = Score.Statistics.GetOrDefault(HitResult.Ok); - countMeh = Score.Statistics.GetOrDefault(HitResult.Meh); - countMiss = Score.Statistics.GetOrDefault(HitResult.Miss); + countGreat = Score.Statistics.GetValueOrDefault(HitResult.Great); + countOk = Score.Statistics.GetValueOrDefault(HitResult.Ok); + countMeh = Score.Statistics.GetValueOrDefault(HitResult.Meh); + countMiss = Score.Statistics.GetValueOrDefault(HitResult.Miss); // Custom multipliers for NoFail and SpunOut. double multiplier = 1.1; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index f32f70d4ba..eeb881cd39 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using osu.Framework.Bindables; -using osu.Framework.Extensions; using osu.Framework.Utils; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; @@ -146,7 +145,7 @@ namespace osu.Game.Rulesets.Scoring rollingMaxBaseScore += result.Judgement.MaxNumericResult; } - scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) + 1; + scoreResultCounts[result.Type] = scoreResultCounts.GetValueOrDefault(result.Type) + 1; hitEvents.Add(CreateHitEvent(result)); lastHitObject = result.HitObject; @@ -181,7 +180,7 @@ namespace osu.Game.Rulesets.Scoring rollingMaxBaseScore -= result.Judgement.MaxNumericResult; } - scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) - 1; + scoreResultCounts[result.Type] = scoreResultCounts.GetValueOrDefault(result.Type) - 1; Debug.Assert(hitEvents.Count > 0); lastHitObject = hitEvents[^1].LastHitObject; @@ -272,8 +271,8 @@ namespace osu.Game.Rulesets.Scoring private double calculateComboRatio(int maxCombo) => maxAchievableCombo > 0 ? (double)maxCombo / maxAchievableCombo : 1; private double getBonusScore(Dictionary statistics) - => statistics.GetOrDefault(HitResult.SmallBonus) * Judgement.SMALL_BONUS_SCORE - + statistics.GetOrDefault(HitResult.LargeBonus) * Judgement.LARGE_BONUS_SCORE; + => statistics.GetValueOrDefault(HitResult.SmallBonus) * Judgement.SMALL_BONUS_SCORE + + statistics.GetValueOrDefault(HitResult.LargeBonus) * Judgement.LARGE_BONUS_SCORE; private ScoreRank rankFrom(double acc) { @@ -291,7 +290,7 @@ namespace osu.Game.Rulesets.Scoring return ScoreRank.D; } - public int GetStatistic(HitResult result) => scoreResultCounts.GetOrDefault(result); + public int GetStatistic(HitResult result) => scoreResultCounts.GetValueOrDefault(result); public double GetStandardisedScore() => getScore(ScoringMode.Standardised); diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 4fd1d00fef..1d22dab0af 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -7,7 +7,6 @@ using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Converters; -using osu.Framework.Extensions; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Online.API; @@ -209,13 +208,13 @@ namespace osu.Game.Scoring { foreach (var r in Ruleset.CreateInstance().GetHitResults()) { - int value = Statistics.GetOrDefault(r.result); + int value = Statistics.GetValueOrDefault(r.result); switch (r.result) { case HitResult.SmallTickHit: { - int total = value + Statistics.GetOrDefault(HitResult.SmallTickMiss); + int total = value + Statistics.GetValueOrDefault(HitResult.SmallTickMiss); if (total > 0) yield return new HitResultDisplayStatistic(r.result, value, total, r.displayName); @@ -224,7 +223,7 @@ namespace osu.Game.Scoring case HitResult.LargeTickHit: { - int total = value + Statistics.GetOrDefault(HitResult.LargeTickMiss); + int total = value + Statistics.GetValueOrDefault(HitResult.LargeTickMiss); if (total > 0) yield return new HitResultDisplayStatistic(r.result, value, total, r.displayName); diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index ebbdc8a109..83bcac01ac 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -11,7 +11,6 @@ using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; using osu.Framework.Bindables; -using osu.Framework.Extensions; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Beatmaps; @@ -210,7 +209,7 @@ namespace osu.Game.Scoring { // This is guaranteed to be a non-legacy score. // The combo must be determined through the score's statistics, as both the beatmap's max combo and the difficulty calculator will provide osu!stable combo values. - beatmapMaxCombo = Enum.GetValues(typeof(HitResult)).OfType().Where(r => r.AffectsCombo()).Select(r => score.Statistics.GetOrDefault(r)).Sum(); + beatmapMaxCombo = Enum.GetValues(typeof(HitResult)).OfType().Where(r => r.AffectsCombo()).Select(r => score.Statistics.GetValueOrDefault(r)).Sum(); } updateScore(beatmapMaxCombo, accuracy); From e0af5f0469d97a0b4b371cc0b4874d5aeed0294e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 11:38:28 +0900 Subject: [PATCH 0473/2442] Fix osu!(lazer) overwriting osu!(stable) desktop icons by adding back a suffix As discussed in https://github.com/ppy/osu/issues/13864, Squirrel will use the product name before the title, allowing us to use this variable to update the icon while not changing the window display title or naming elsewhere. --- osu.Desktop/osu.Desktop.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 53a4e5edf5..89b9ffb94b 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -6,7 +6,7 @@ A free-to-win rhythm game. Rhythm is just a *click* away! osu! osu! - osu! + osu!(lazer) lazer.ico app.manifest 0.0.0 From d7f997a7f3f221279ee4b1497129655950adb525 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 12:36:13 +0900 Subject: [PATCH 0474/2442] Set score's rank on a failed submission As we don't have a `RankInfo.F`, this is the next best choice. I am also adding a check osu-web side for this - this is just to make sure we aren't sending scores with SS when they are not actually completed. I'm working on a separate PR to ensure this does not get mutated during the player exit process. --- osu.Game/Screens/Play/Player.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 97854ee12f..dc7ac1a48b 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -953,7 +953,11 @@ namespace osu.Game.Screens.Play // if arriving here and the results screen preparation task hasn't run, it's safe to say the user has not completed the beatmap. if (prepareScoreForDisplayTask == null) + { Score.ScoreInfo.Passed = false; + // potentially should be ScoreRank.F instead? this is the best alternative for now. + Score.ScoreInfo.Rank = ScoreRank.D; + } // EndPlaying() is typically called from ReplayRecorder.Dispose(). Disposal is currently asynchronous. // To resolve test failures, forcefully end playing synchronously when this screen exits. From 3c028ce05cd619c58488793e9e26548ae07aeff3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 12:38:22 +0900 Subject: [PATCH 0475/2442] Add `IDeepCloneable` interface and update existing `CreateCopy` methods to use it --- CodeAnalysis/BannedSymbols.txt | 1 + osu.Game.Tests/NonVisual/ControlPointInfoTest.cs | 4 ++-- .../Visual/UserInterface/TestSceneModSettings.cs | 4 ++-- osu.Game/Beatmaps/ControlPoints/ControlPoint.cs | 5 +++-- .../Beatmaps/ControlPoints/ControlPointInfo.cs | 7 ++++--- osu.Game/Online/Rooms/Room.cs | 5 +++-- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 +- .../Rulesets/Difficulty/DifficultyCalculator.cs | 2 +- osu.Game/Rulesets/Mods/Mod.cs | 4 ++-- osu.Game/Rulesets/Mods/MultiMod.cs | 2 +- osu.Game/Screens/Edit/Editor.cs | 2 +- .../OnlinePlay/Lounge/Components/DrawableRoom.cs | 2 +- .../Screens/OnlinePlay/OnlinePlaySongSelect.cs | 8 ++++---- .../OnlinePlay/Playlists/PlaylistsSongSelect.cs | 4 ++-- osu.Game/Screens/Play/Player.cs | 2 +- osu.Game/Utils/IDeepCloneable.cs | 16 ++++++++++++++++ 16 files changed, 45 insertions(+), 25 deletions(-) create mode 100644 osu.Game/Utils/IDeepCloneable.cs diff --git a/CodeAnalysis/BannedSymbols.txt b/CodeAnalysis/BannedSymbols.txt index 46c50dbfa2..ea3e25142c 100644 --- a/CodeAnalysis/BannedSymbols.txt +++ b/CodeAnalysis/BannedSymbols.txt @@ -3,6 +3,7 @@ M:System.Object.Equals(System.Object)~System.Boolean;Don't use object.Equals. Us M:System.ValueType.Equals(System.Object)~System.Boolean;Don't use object.Equals(Fallbacks to ValueType). Use IEquatable or EqualityComparer.Default instead. M:System.Nullable`1.Equals(System.Object)~System.Boolean;Use == instead. T:System.IComparable;Don't use non-generic IComparable. Use generic version instead. +T:SixLabors.ImageSharp.IDeepCloneable`1;Use osu.Game.Utils.IDeepCloneable instead. M:osu.Framework.Graphics.Sprites.SpriteText.#ctor;Use OsuSpriteText. M:osu.Framework.Bindables.IBindableList`1.GetBoundCopy();Fails on iOS. Use manual ctor + BindTo instead. (see https://github.com/mono/mono/issues/19900) T:Microsoft.EntityFrameworkCore.Internal.EnumerableExtensions;Don't use internal extension methods. diff --git a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs index b27c257795..240ae4a90c 100644 --- a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs +++ b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs @@ -248,13 +248,13 @@ namespace osu.Game.Tests.NonVisual } [Test] - public void TestCreateCopyIsDeepClone() + public void TestDeepClone() { var cpi = new ControlPointInfo(); cpi.Add(1000, new TimingControlPoint { BeatLength = 500 }); - var cpiCopy = cpi.CreateCopy(); + var cpiCopy = cpi.DeepClone(); cpiCopy.Add(2000, new TimingControlPoint { BeatLength = 500 }); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index bda1973354..65db2e9644 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs @@ -88,7 +88,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("create mods", () => { original = new OsuModDoubleTime(); - copy = (OsuModDoubleTime)original.CreateCopy(); + copy = (OsuModDoubleTime)original.DeepClone(); }); AddStep("change property", () => original.SpeedChange.Value = 2); @@ -106,7 +106,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("create mods", () => { original = new MultiMod(new OsuModDoubleTime()); - copy = (MultiMod)original.CreateCopy(); + copy = (MultiMod)original.DeepClone(); }); AddStep("change property", () => ((OsuModDoubleTime)original.Mods[0]).SpeedChange.Value = 2); diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs index e8dc623ddb..643c5d9adb 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs @@ -3,11 +3,12 @@ using System; using osu.Game.Graphics; +using osu.Game.Utils; using osuTK.Graphics; namespace osu.Game.Beatmaps.ControlPoints { - public abstract class ControlPoint : IComparable + public abstract class ControlPoint : IComparable, IDeepCloneable { /// /// The time at which the control point takes effect. @@ -32,7 +33,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// /// Create an unbound copy of this control point. /// - public ControlPoint CreateCopy() + public ControlPoint DeepClone() { var copy = (ControlPoint)Activator.CreateInstance(GetType()); diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index 25d0843a71..2d0fc17a7b 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -10,11 +10,12 @@ using osu.Framework.Bindables; using osu.Framework.Lists; using osu.Framework.Utils; using osu.Game.Screens.Edit; +using osu.Game.Utils; namespace osu.Game.Beatmaps.ControlPoints { [Serializable] - public class ControlPointInfo + public class ControlPointInfo : IDeepCloneable { /// /// All control points grouped by time. @@ -350,12 +351,12 @@ namespace osu.Game.Beatmaps.ControlPoints } } - public ControlPointInfo CreateCopy() + public ControlPointInfo DeepClone() { var controlPointInfo = new ControlPointInfo(); foreach (var point in AllControlPoints) - controlPointInfo.Add(point.Time, point.CreateCopy()); + controlPointInfo.Add(point.Time, point.DeepClone()); return controlPointInfo; } diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index b28680ffef..863fefd55c 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -10,10 +10,11 @@ using osu.Game.IO.Serialization.Converters; using osu.Game.Online.Rooms.GameTypes; using osu.Game.Online.Rooms.RoomStatuses; using osu.Game.Users; +using osu.Game.Utils; namespace osu.Game.Online.Rooms { - public class Room + public class Room : IDeepCloneable { [Cached] [JsonProperty("id")] @@ -120,7 +121,7 @@ namespace osu.Game.Online.Rooms /// Create a copy of this room without online information. /// Should be used to create a local copy of a room for submitting in the future. /// - public Room CreateCopy() + public Room DeepClone() { var copy = new Room(); diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 98a79a62c8..793bb79318 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -429,7 +429,7 @@ namespace osu.Game.Overlays.Mods if (!Stacked) modEnumeration = ModUtils.FlattenMods(modEnumeration); - section.Mods = modEnumeration.Select(getValidModOrNull).Where(m => m != null).Select(m => m.CreateCopy()); + section.Mods = modEnumeration.Select(getValidModOrNull).Where(m => m != null).Select(m => m.DeepClone()); } updateSelectedButtons(); diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 3cc69bd85b..224c9178ae 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Difficulty /// A structure describing the difficulty of the beatmap. public DifficultyAttributes Calculate(params Mod[] mods) { - mods = mods.Select(m => m.CreateCopy()).ToArray(); + mods = mods.Select(m => m.DeepClone()).ToArray(); IBeatmap playableBeatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods); diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 6f00bb6c75..f2fd02c652 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mods /// The base class for gameplay modifiers. /// [ExcludeFromDynamicCompile] - public abstract class Mod : IMod, IEquatable, IJsonSerializable + public abstract class Mod : IMod, IEquatable, IJsonSerializable, IDeepCloneable { /// /// The name of this mod. @@ -132,7 +132,7 @@ namespace osu.Game.Rulesets.Mods /// /// Creates a copy of this initialised to a default state. /// - public virtual Mod CreateCopy() + public virtual Mod DeepClone() { var result = (Mod)Activator.CreateInstance(GetType()); result.CopyFrom(this); diff --git a/osu.Game/Rulesets/Mods/MultiMod.cs b/osu.Game/Rulesets/Mods/MultiMod.cs index 2107009dbb..1c41c6b8b3 100644 --- a/osu.Game/Rulesets/Mods/MultiMod.cs +++ b/osu.Game/Rulesets/Mods/MultiMod.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Mods Mods = mods; } - public override Mod CreateCopy() => new MultiMod(Mods.Select(m => m.CreateCopy()).ToArray()); + public override Mod DeepClone() => new MultiMod(Mods.Select(m => m.DeepClone()).ToArray()); public override Type[] IncompatibleMods => Mods.SelectMany(m => m.IncompatibleMods).ToArray(); } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 986a4efb28..71dd47b058 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -128,7 +128,7 @@ namespace osu.Game.Screens.Edit // clone these locally for now to avoid incurring overhead on GetPlayableBeatmap usages. // eventually we will want to improve how/where this is done as there are issues with *not* cloning it in all cases. - playableBeatmap.ControlPointInfo = playableBeatmap.ControlPointInfo.CreateCopy(); + playableBeatmap.ControlPointInfo = playableBeatmap.ControlPointInfo.DeepClone(); } catch (Exception e) { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 35782c6104..b4b1a09c4f 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -242,7 +242,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { new OsuMenuItem("Create copy", MenuItemType.Standard, () => { - parentScreen?.OpenNewRoom(Room.CreateCopy()); + parentScreen?.OpenNewRoom(Room.DeepClone()); }) }; } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index 2c46f76737..be28de5c43 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -72,8 +72,8 @@ namespace osu.Game.Screens.OnlinePlay // At this point, Mods contains both the required and allowed mods. For selection purposes, it should only contain the required mods. // Similarly, freeMods is currently empty but should only contain the allowed mods. - Mods.Value = selectedItem?.Value?.RequiredMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty(); - FreeMods.Value = selectedItem?.Value?.AllowedMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty(); + Mods.Value = selectedItem?.Value?.RequiredMods.Select(m => m.DeepClone()).ToArray() ?? Array.Empty(); + FreeMods.Value = selectedItem?.Value?.AllowedMods.Select(m => m.DeepClone()).ToArray() ?? Array.Empty(); Mods.BindValueChanged(onModsChanged); Ruleset.BindValueChanged(onRulesetChanged); @@ -108,8 +108,8 @@ namespace osu.Game.Screens.OnlinePlay } }; - item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy())); - item.AllowedMods.AddRange(FreeMods.Value.Select(m => m.CreateCopy())); + item.RequiredMods.AddRange(Mods.Value.Select(m => m.DeepClone())); + item.AllowedMods.AddRange(FreeMods.Value.Select(m => m.DeepClone())); SelectItem(item); return true; diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs index 21335fc90c..076fa77336 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs @@ -55,10 +55,10 @@ namespace osu.Game.Screens.OnlinePlay.Playlists item.Ruleset.Value = Ruleset.Value; item.RequiredMods.Clear(); - item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy())); + item.RequiredMods.AddRange(Mods.Value.Select(m => m.DeepClone())); item.AllowedMods.Clear(); - item.AllowedMods.AddRange(FreeMods.Value.Select(m => m.CreateCopy())); + item.AllowedMods.AddRange(FreeMods.Value.Select(m => m.DeepClone())); } } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 97854ee12f..51f1dbd121 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -184,7 +184,7 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader(true)] private void load(AudioManager audio, OsuConfigManager config, OsuGameBase game) { - Mods.Value = base.Mods.Value.Select(m => m.CreateCopy()).ToArray(); + Mods.Value = base.Mods.Value.Select(m => m.DeepClone()).ToArray(); if (Beatmap.Value is DummyWorkingBeatmap) return; diff --git a/osu.Game/Utils/IDeepCloneable.cs b/osu.Game/Utils/IDeepCloneable.cs new file mode 100644 index 0000000000..6877f346c4 --- /dev/null +++ b/osu.Game/Utils/IDeepCloneable.cs @@ -0,0 +1,16 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Utils +{ + /// A generic interface for a deeply cloneable type. + /// The type of object to clone. + public interface IDeepCloneable where T : class + { + /// + /// Creates a new that is a deep copy of the current instance. + /// + /// The . + T DeepClone(); + } +} From 70c9d7105feb5162661379f427562de6cba17f91 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 13:33:22 +0900 Subject: [PATCH 0476/2442] Add a function to compute hit object position in catch editor --- .../Edit/CatchHitObjectUtils.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Edit/CatchHitObjectUtils.cs diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectUtils.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectUtils.cs new file mode 100644 index 0000000000..beffdf0362 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectUtils.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.UI.Scrolling; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Edit +{ + /// + /// Utility functions used by the editor. + /// + public static class CatchHitObjectUtils + { + /// + /// Get the position of the hit object in the playfield based on and . + /// + public static Vector2 GetStartPosition(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject hitObject) + { + return new Vector2(hitObject.OriginalX, hitObjectContainer.PositionAtTime(hitObject.StartTime)); + } + } +} From c34758485125a3d0ad2f41d28c4c60e1771b2fc1 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 13:33:46 +0900 Subject: [PATCH 0477/2442] Use added utility function --- .../Edit/Blueprints/CatchSelectionBlueprint.cs | 5 ++--- .../Edit/Blueprints/Components/FruitOutline.cs | 6 +----- .../Edit/Blueprints/Components/NestedOutlineContainer.cs | 9 ++------- .../Edit/Blueprints/Components/ScrollingPath.cs | 6 ------ .../Edit/Blueprints/FruitPlacementBlueprint.cs | 3 ++- .../Edit/Blueprints/FruitSelectionBlueprint.cs | 6 ++++-- .../Edit/Blueprints/JuiceStreamSelectionBlueprint.cs | 3 +-- 7 files changed, 12 insertions(+), 26 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/CatchSelectionBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/CatchSelectionBlueprint.cs index 720d730858..7e566c810c 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/CatchSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/CatchSelectionBlueprint.cs @@ -19,9 +19,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints { get { - float x = HitObject.OriginalX; - float y = HitObjectContainer.PositionAtTime(HitObject.StartTime); - return HitObjectContainer.ToScreenSpace(new Vector2(x, y + HitObjectContainer.DrawHeight)); + Vector2 position = CatchHitObjectUtils.GetStartPosition(HitObjectContainer, HitObject); + return HitObjectContainer.ToScreenSpace(position + new Vector2(0, HitObjectContainer.DrawHeight)); } } diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/FruitOutline.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/FruitOutline.cs index 345b59bdcd..0c03068e26 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/FruitOutline.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/FruitOutline.cs @@ -1,14 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Skinning.Default; -using osu.Game.Rulesets.UI.Scrolling; using osuTK; namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components @@ -28,10 +26,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components Colour = osuColour.Yellow; } - public void UpdateFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject hitObject, [CanBeNull] CatchHitObject parent = null) + public void UpdateFrom(CatchHitObject hitObject) { - X = hitObject.EffectiveX - (parent?.OriginalX ?? 0); - Y = hitObjectContainer.PositionAtTime(hitObject.StartTime, parent?.StartTime ?? hitObjectContainer.Time.Current); Scale = new Vector2(hitObject.Scale); } } diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/NestedOutlineContainer.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/NestedOutlineContainer.cs index 48d90e8b24..cf916b27a4 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/NestedOutlineContainer.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/NestedOutlineContainer.cs @@ -20,12 +20,6 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components Anchor = Anchor.BottomLeft; } - public void UpdatePositionFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject parentHitObject) - { - X = parentHitObject.OriginalX; - Y = hitObjectContainer.PositionAtTime(parentHitObject.StartTime); - } - public void UpdateNestedObjectsFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject parentHitObject) { nestedHitObjects.Clear(); @@ -43,7 +37,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components { var hitObject = nestedHitObjects[i]; var outline = (FruitOutline)InternalChildren[i]; - outline.UpdateFrom(hitObjectContainer, hitObject, parentHitObject); + outline.Position = CatchHitObjectUtils.GetStartPosition(hitObjectContainer, hitObject) - Position; + outline.UpdateFrom(hitObject); outline.Scale *= hitObject is Droplet ? 0.5f : 1; } } diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/ScrollingPath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/ScrollingPath.cs index 96111beda4..109bf61ea5 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/ScrollingPath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/ScrollingPath.cs @@ -33,12 +33,6 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components }; } - public void UpdatePositionFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject hitObject) - { - X = hitObject.OriginalX; - Y = hitObjectContainer.PositionAtTime(hitObject.StartTime); - } - public void UpdatePathFrom(ScrollingHitObjectContainer hitObjectContainer, JuiceStream hitObject) { double distanceToYFactor = -hitObjectContainer.LengthAtTime(hitObject.StartTime, hitObject.StartTime + 1 / hitObject.Velocity); diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/FruitPlacementBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/FruitPlacementBlueprint.cs index 0f28cf6786..e169e3b75c 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/FruitPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/FruitPlacementBlueprint.cs @@ -29,7 +29,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints { base.Update(); - outline.UpdateFrom(HitObjectContainer, HitObject); + outline.Position = CatchHitObjectUtils.GetStartPosition(HitObjectContainer, HitObject); + outline.UpdateFrom(HitObject); } protected override bool OnMouseDown(MouseDownEvent e) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/FruitSelectionBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/FruitSelectionBlueprint.cs index 9665aac2fb..150297badb 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/FruitSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/FruitSelectionBlueprint.cs @@ -20,8 +20,10 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints { base.Update(); - if (IsSelected) - outline.UpdateFrom(HitObjectContainer, HitObject); + if (!IsSelected) return; + + outline.Position = CatchHitObjectUtils.GetStartPosition(HitObjectContainer, HitObject); + outline.UpdateFrom(HitObject); } } } diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs index bf7b962e0a..0614c4c24d 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs @@ -49,8 +49,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints if (!IsSelected) return; - scrollingPath.UpdatePositionFrom(HitObjectContainer, HitObject); - nestedOutlineContainer.UpdatePositionFrom(HitObjectContainer, HitObject); + nestedOutlineContainer.Position = scrollingPath.Position = CatchHitObjectUtils.GetStartPosition(HitObjectContainer, HitObject); if (pathCache.IsValid) return; From be495a74887f12011df92911f1a72ff6d8b1b59e Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 13:03:53 +0900 Subject: [PATCH 0478/2442] Add `JuiceStreamPath` to allow editing `JuiceStream` in catch editor A `JuiceStream` holds a legacy `SliderPath` as the representation of the path. However, the `SliderPath` representation is difficult to work with because `SliderPath` works in 2D position, while osu!catch works in `(time, x)` coordinates. This `JuiceStreamPath` represents the path in a more convenient way, a polyline connecting list of `(distance, x)` vertices. --- .../Objects/JuiceStreamPath.cs | 340 ++++++++++++++++++ .../Objects/JuiceStreamPathVertex.cs | 33 ++ 2 files changed, 373 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs create mode 100644 osu.Game.Rulesets.Catch/Objects/JuiceStreamPathVertex.cs diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs new file mode 100644 index 0000000000..ac11bd9918 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs @@ -0,0 +1,340 @@ +// 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 osu.Framework.Utils; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osuTK; + +#nullable enable + +namespace osu.Game.Rulesets.Catch.Objects +{ + /// + /// Represents the path of a juice stream. + /// + /// A holds a legacy as the representation of the path. + /// However, the representation is difficult to work with. + /// This represents the path in a more convenient way, a polyline connecting list of s. + /// + /// + /// The path can be regarded as a function from the closed interval [Vertices[0].Distance, Vertices[^1].Distance] to the x position, given by . + /// To ensure the path is convertible to a , the slope of the function must not be more than 1 everywhere, + /// and this slope condition is always maintained as an invariant. + /// + /// + public class JuiceStreamPath + { + /// + /// The list of vertices of the path, which is represented as a polyline connecting the vertices. + /// + public IReadOnlyList Vertices => vertices; + + /// + /// The current version number. + /// This starts from 1 and incremented whenever this is modified. + /// + public int InvalidationID { get; private set; } = 1; + + /// + /// The difference between first vertex's and last vertex's . + /// + public double Distance => vertices[^1].Distance - vertices[0].Distance; + + /// + /// This list should always be non-empty. + /// + private readonly List vertices = new List + { + new JuiceStreamPathVertex() + }; + + /// + /// Compute the x-position of the path at the given . + /// + /// + /// When the given distance is outside of the path, the x position at the corresponding endpoint is returned, + /// + public float PositionAtDistance(double distance) + { + int index = vertexIndexAtDistance(distance); + return positionAtDistance(distance, index); + } + + /// + /// Remove all vertices of this path, then add a new vertex (0, 0). + /// + public void Clear() + { + vertices.Clear(); + vertices.Add(new JuiceStreamPathVertex()); + invalidate(); + } + + /// + /// Insert a vertex at given . + /// The is used as the position of the new vertex. + /// Thus, the set of points of the path is not changed (up to floating-point precision). + /// + /// The index of the new vertex. + public int InsertVertex(double distance) + { + if (!double.IsFinite(distance)) + throw new ArgumentOutOfRangeException(nameof(distance)); + + int index = vertexIndexAtDistance(distance); + float x = positionAtDistance(distance, index); + vertices.Insert(index, new JuiceStreamPathVertex(distance, x)); + + invalidate(); + return index; + } + + /// + /// Move the vertex of given to the given position . + /// When the distances between vertices are too small for the new vertex positions, the adjacent vertices are moved towards . + /// + public void SetVertexPosition(int index, float newX) + { + if (index < 0 || index >= vertices.Count) + throw new ArgumentOutOfRangeException(nameof(index)); + + if (!float.IsFinite(newX)) + throw new ArgumentOutOfRangeException(nameof(newX)); + + var newVertex = new JuiceStreamPathVertex(vertices[index].Distance, newX); + + for (int i = index - 1; i >= 0 && !canConnect(vertices[i], newVertex); i--) + { + float clampedX = clampToConnectablePosition(newVertex, vertices[i]); + vertices[i] = new JuiceStreamPathVertex(vertices[i].Distance, clampedX); + } + + for (int i = index + 1; i < vertices.Count; i++) + { + float clampedX = clampToConnectablePosition(newVertex, vertices[i]); + vertices[i] = new JuiceStreamPathVertex(vertices[i].Distance, clampedX); + } + + vertices[index] = newVertex; + + invalidate(); + } + + /// + /// Add a new vertex at given and position. + /// Adjacent vertices are moved when necessary in the same way as . + /// + public void Add(double distance, float x) + { + int index = InsertVertex(distance); + SetVertexPosition(index, x); + } + + /// + /// Remove all vertices that satisfy the given . + /// + /// + /// If all vertices are removed, a new vertex (0, 0) is added. + /// + /// The predicate to determine whether a vertex should be removed given the vertex and its index in the path. + /// The number of removed vertices. + public int RemoveVertices(Func predicate) + { + int index = 0; + int removeCount = vertices.RemoveAll(vertex => predicate(vertex, index++)); + + if (vertices.Count == 0) + vertices.Add(new JuiceStreamPathVertex()); + + if (removeCount != 0) + invalidate(); + + return removeCount; + } + + /// + /// Recreate this path by using difference set of vertices at given distances. + /// In addition to the given , the first vertex and the last vertex are always added to the new path. + /// New vertices use the positions on the original path. Thus, s at are preserved. + /// + public void ResampleVertices(IEnumerable sampleDistances) + { + var sampledVertices = new List(); + + foreach (double distance in sampleDistances) + { + if (!double.IsFinite(distance)) + throw new ArgumentOutOfRangeException(nameof(sampleDistances)); + + double clampedDistance = Math.Clamp(distance, vertices[0].Distance, vertices[^1].Distance); + float x = PositionAtDistance(clampedDistance); + sampledVertices.Add(new JuiceStreamPathVertex(clampedDistance, x)); + } + + sampledVertices.Sort(); + + // The first vertex and the last vertex are always used in the result. + vertices.RemoveRange(1, vertices.Count - (vertices.Count == 1 ? 1 : 2)); + vertices.InsertRange(1, sampledVertices); + + invalidate(); + } + + /// + /// Convert a to list of vertices and write the result to this . + /// + /// + /// Duplicated vertices are automatically removed. + /// + public void ConvertFromSliderPath(SliderPath sliderPath) + { + var sliderPathVertices = new List(); + sliderPath.GetPathToProgress(sliderPathVertices, 0, 1); + + double distance = 0; + + vertices.Clear(); + vertices.Add(new JuiceStreamPathVertex(0, sliderPathVertices.FirstOrDefault().X)); + + for (int i = 1; i < sliderPathVertices.Count; i++) + { + distance += Vector2.Distance(sliderPathVertices[i - 1], sliderPathVertices[i]); + + if (!Precision.AlmostEquals(vertices[^1].Distance, distance)) + vertices.Add(new JuiceStreamPathVertex(distance, sliderPathVertices[i].X)); + } + + invalidate(); + } + + /// + /// The height of legacy osu!standard playfield. + /// The sliders converted by are vertically contained in this height. + /// + public const float OSU_PLAYFIELD_HEIGHT = 384; + + /// + /// Convert the path of this to a and write the result to . + /// The resulting slider is "folded" to make it vertically contained in the playfield `(0..)` assuming the slider start position is . + /// + public void ConvertToSliderPath(SliderPath sliderPath, float sliderStartY) + { + const float margin = 1; + + // Note: these two variables and `sliderPath` are modified by the local functions. + double currentDistance = 0; + Vector2 lastPosition = new Vector2(vertices[0].X, 0); + + sliderPath.ControlPoints.Clear(); + sliderPath.ControlPoints.Add(new PathControlPoint(lastPosition)); + + for (int i = 1; i < vertices.Count; i++) + { + sliderPath.ControlPoints[^1].Type.Value = PathType.Linear; + + float deltaX = vertices[i].X - lastPosition.X; + double length = vertices[i].Distance - currentDistance; + + // Should satisfy `deltaX^2 + deltaY^2 = length^2`. + // By invariants, the expression inside the `sqrt` is (almost) non-negative. + double deltaY = Math.Sqrt(Math.Max(0, length * length - (double)deltaX * deltaX)); + + // When `deltaY` is small, one segment is always enough. + // This case is handled separately to prevent divide-by-zero. + if (deltaY <= OSU_PLAYFIELD_HEIGHT / 2 - margin) + { + float nextX = vertices[i].X; + float nextY = (float)(lastPosition.Y + getYDirection() * deltaY); + addControlPoint(nextX, nextY); + continue; + } + + // When `deltaY` is large or when the slider velocity is fast, the segment must be partitioned to subsegments to stay in bounds. + for (double currentProgress = 0; currentProgress < deltaY;) + { + double nextProgress = Math.Min(currentProgress + getMaxDeltaY(), deltaY); + float nextX = (float)(vertices[i - 1].X + nextProgress / deltaY * deltaX); + float nextY = (float)(lastPosition.Y + getYDirection() * (nextProgress - currentProgress)); + addControlPoint(nextX, nextY); + currentProgress = nextProgress; + } + } + + int getYDirection() + { + float lastSliderY = sliderStartY + lastPosition.Y; + return lastSliderY < OSU_PLAYFIELD_HEIGHT / 2 ? 1 : -1; + } + + float getMaxDeltaY() + { + float lastSliderY = sliderStartY + lastPosition.Y; + return Math.Max(lastSliderY, OSU_PLAYFIELD_HEIGHT - lastSliderY) - margin; + } + + void addControlPoint(float nextX, float nextY) + { + Vector2 nextPosition = new Vector2(nextX, nextY); + sliderPath.ControlPoints.Add(new PathControlPoint(nextPosition)); + currentDistance += Vector2.Distance(lastPosition, nextPosition); + lastPosition = nextPosition; + } + } + + /// + /// Find the index at which a new vertex with can be inserted. + /// + private int vertexIndexAtDistance(double distance) + { + // The position of `(distance, Infinity)` is uniquely determined because infinite positions are not allowed. + int i = vertices.BinarySearch(new JuiceStreamPathVertex(distance, float.PositiveInfinity)); + return i < 0 ? ~i : i; + } + + /// + /// Compute the position at the given , assuming is the vertex index returned by . + /// + private float positionAtDistance(double distance, int index) + { + if (index <= 0) + return vertices[0].X; + if (index >= vertices.Count) + return vertices[^1].X; + + double length = vertices[index].Distance - vertices[index - 1].Distance; + if (Precision.AlmostEquals(length, 0)) + return vertices[index].X; + + float deltaX = vertices[index].X - vertices[index - 1].X; + + return (float)(vertices[index - 1].X + deltaX * ((distance - vertices[index - 1].Distance) / length)); + } + + /// + /// Check the two vertices can connected directly while satisfying the slope condition. + /// + private bool canConnect(JuiceStreamPathVertex vertex1, JuiceStreamPathVertex vertex2, float allowance = 0) + { + double xDistance = Math.Abs((double)vertex2.X - vertex1.X); + float length = (float)Math.Abs(vertex2.Distance - vertex1.Distance); + return xDistance <= length + allowance; + } + + /// + /// Move the position of towards the position of + /// until the vertex pair satisfies the condition . + /// + /// The resulting position of . + private float clampToConnectablePosition(JuiceStreamPathVertex fixedVertex, JuiceStreamPathVertex movableVertex) + { + float length = (float)Math.Abs(movableVertex.Distance - fixedVertex.Distance); + return Math.Clamp(movableVertex.X, fixedVertex.X - length, fixedVertex.X + length); + } + + private void invalidate() => InvalidationID++; + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStreamPathVertex.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPathVertex.cs new file mode 100644 index 0000000000..58c50603c4 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPathVertex.cs @@ -0,0 +1,33 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; + +#nullable enable + +namespace osu.Game.Rulesets.Catch.Objects +{ + /// + /// A vertex of a . + /// + public readonly struct JuiceStreamPathVertex : IComparable + { + public readonly double Distance; + + public readonly float X; + + public JuiceStreamPathVertex(double distance, float x) + { + Distance = distance; + X = x; + } + + public int CompareTo(JuiceStreamPathVertex other) + { + int c = Distance.CompareTo(other.Distance); + return c != 0 ? c : X.CompareTo(other.X); + } + + public override string ToString() => $"({Distance}, {X})"; + } +} From fffe0d2e5735d68c7beef525b5c134fe5822ba69 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 13:09:33 +0900 Subject: [PATCH 0479/2442] Add tests of `JuiceStreamPath`. "Property testing" is heavily used, tests that generates random cases and asserting properties. That gives high confidence of round-trip correctness of `SliderPath` conversion, for example. --- .../JuiceStreamPathTest.cs | 288 ++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100644 osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs diff --git a/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs b/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs new file mode 100644 index 0000000000..5e4b6d9e1a --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs @@ -0,0 +1,288 @@ +// 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; +using osu.Framework.Utils; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Tests +{ + [TestFixture] + public class JuiceStreamPathTest + { + [TestCase(1e3, true, false)] + // When the coordinates are large, the slope invariant fails within the specified absolute allowance due to the floating-number precision. + [TestCase(1e9, false, false)] + // Using discrete values sometimes discover more edge cases. + [TestCase(10, true, true)] + public void TestRandomInsertSetPosition(double scale, bool checkSlope, bool integralValues) + { + var rng = new Random(1); + var path = new JuiceStreamPath(); + + for (int iteration = 0; iteration < 100000; iteration++) + { + if (rng.Next(10) == 0) + path.Clear(); + + int vertexCount = path.Vertices.Count; + + switch (rng.Next(2)) + { + case 0: + { + double distance = rng.NextDouble() * scale * 2 - scale; + if (integralValues) + distance = Math.Round(distance); + + float oldX = path.PositionAtDistance(distance); + int index = path.InsertVertex(distance); + Assert.That(path.Vertices.Count, Is.EqualTo(vertexCount + 1)); + Assert.That(path.Vertices[index].Distance, Is.EqualTo(distance)); + Assert.That(path.Vertices[index].X, Is.EqualTo(oldX)); + break; + } + + case 1: + { + int index = rng.Next(path.Vertices.Count); + double distance = path.Vertices[index].Distance; + float newX = (float)(rng.NextDouble() * scale * 2 - scale); + if (integralValues) + newX = MathF.Round(newX); + + path.SetVertexPosition(index, newX); + Assert.That(path.Vertices.Count, Is.EqualTo(vertexCount)); + Assert.That(path.Vertices[index].Distance, Is.EqualTo(distance)); + Assert.That(path.Vertices[index].X, Is.EqualTo(newX)); + break; + } + } + + assertInvariants(path.Vertices, checkSlope); + } + } + + [Test] + public void TestRemoveVertices() + { + var path = new JuiceStreamPath(); + path.Add(10, 5); + path.Add(20, -5); + + int removeCount = path.RemoveVertices((v, i) => v.Distance == 10 && i == 1); + Assert.That(removeCount, Is.EqualTo(1)); + Assert.That(path.Vertices, Is.EqualTo(new[] + { + new JuiceStreamPathVertex(0, 0), + new JuiceStreamPathVertex(20, -5) + })); + + removeCount = path.RemoveVertices((_, i) => i == 0); + Assert.That(removeCount, Is.EqualTo(1)); + Assert.That(path.Vertices, Is.EqualTo(new[] + { + new JuiceStreamPathVertex(20, -5) + })); + + removeCount = path.RemoveVertices((_, i) => true); + Assert.That(removeCount, Is.EqualTo(1)); + Assert.That(path.Vertices, Is.EqualTo(new[] + { + new JuiceStreamPathVertex() + })); + } + + [Test] + public void TestResampleVertices() + { + var path = new JuiceStreamPath(); + path.Add(-100, -10); + path.Add(100, 50); + path.ResampleVertices(new double[] + { + -50, + 0, + 70, + 120 + }); + Assert.That(path.Vertices, Is.EqualTo(new[] + { + new JuiceStreamPathVertex(-100, -10), + new JuiceStreamPathVertex(-50, -5), + new JuiceStreamPathVertex(0, 0), + new JuiceStreamPathVertex(70, 35), + new JuiceStreamPathVertex(100, 50), + new JuiceStreamPathVertex(100, 50), + })); + + path.Clear(); + path.SetVertexPosition(0, 10); + path.ResampleVertices(Array.Empty()); + Assert.That(path.Vertices, Is.EqualTo(new[] + { + new JuiceStreamPathVertex(0, 10) + })); + } + + [Test] + public void TestRandomConvertFromSliderPath() + { + var rng = new Random(1); + var path = new JuiceStreamPath(); + var sliderPath = new SliderPath(); + + for (int iteration = 0; iteration < 10000; iteration++) + { + sliderPath.ControlPoints.Clear(); + + do + { + int start = sliderPath.ControlPoints.Count; + + do + { + float x = (float)(rng.NextDouble() * 1e3); + float y = (float)(rng.NextDouble() * 1e3); + sliderPath.ControlPoints.Add(new PathControlPoint(new Vector2(x, y))); + } while (rng.Next(2) != 0); + + int length = sliderPath.ControlPoints.Count - start + 1; + sliderPath.ControlPoints[start].Type.Value = length <= 2 ? PathType.Linear : length == 3 ? PathType.PerfectCurve : PathType.Bezier; + } while (rng.Next(3) != 0); + + if (rng.Next(5) == 0) + sliderPath.ExpectedDistance.Value = rng.NextDouble() * 3e3; + else + sliderPath.ExpectedDistance.Value = null; + + path.ConvertFromSliderPath(sliderPath); + Assert.That(path.Vertices[0].Distance, Is.EqualTo(0)); + Assert.That(path.Distance, Is.EqualTo(sliderPath.Distance).Within(1e-3)); + assertInvariants(path.Vertices, true); + + double[] sampleDistances = Enumerable.Range(0, 10) + .Select(_ => rng.NextDouble() * sliderPath.Distance) + .ToArray(); + + foreach (double distance in sampleDistances) + { + float expected = sliderPath.PositionAt(distance / sliderPath.Distance).X; + Assert.That(path.PositionAtDistance(distance), Is.EqualTo(expected).Within(1e-3)); + } + + path.ResampleVertices(sampleDistances); + assertInvariants(path.Vertices, true); + + foreach (double distance in sampleDistances) + { + float expected = sliderPath.PositionAt(distance / sliderPath.Distance).X; + Assert.That(path.PositionAtDistance(distance), Is.EqualTo(expected).Within(1e-3)); + } + } + } + + [Test] + public void TestRandomConvertToSliderPath() + { + var rng = new Random(1); + var path = new JuiceStreamPath(); + var sliderPath = new SliderPath(); + + for (int iteration = 0; iteration < 10000; iteration++) + { + path.Clear(); + + do + { + double distance = rng.NextDouble() * 1e3; + float x = (float)(rng.NextDouble() * 1e3); + path.Add(distance, x); + } while (rng.Next(5) != 0); + + float sliderStartY = (float)(rng.NextDouble() * JuiceStreamPath.OSU_PLAYFIELD_HEIGHT); + + path.ConvertToSliderPath(sliderPath, sliderStartY); + Assert.That(sliderPath.Distance, Is.EqualTo(path.Distance).Within(1e-3)); + Assert.That(sliderPath.ControlPoints[0].Position.Value.X, Is.EqualTo(path.Vertices[0].X)); + assertInvariants(path.Vertices, true); + + foreach (var point in sliderPath.ControlPoints) + { + Assert.That(point.Type.Value, Is.EqualTo(PathType.Linear).Or.Null); + Assert.That(sliderStartY + point.Position.Value.Y, Is.InRange(0, JuiceStreamPath.OSU_PLAYFIELD_HEIGHT)); + } + + for (int i = 0; i < 10; i++) + { + double distance = rng.NextDouble() * path.Distance; + float expected = path.PositionAtDistance(distance); + Assert.That(sliderPath.PositionAt(distance / sliderPath.Distance).X, Is.EqualTo(expected).Within(1e-3)); + } + } + } + + [Test] + public void TestInvalidation() + { + var path = new JuiceStreamPath(); + Assert.That(path.InvalidationID, Is.EqualTo(1)); + int previousId = path.InvalidationID; + + path.InsertVertex(10); + checkNewId(); + + path.SetVertexPosition(1, 5); + checkNewId(); + + path.Add(20, 0); + checkNewId(); + + path.RemoveVertices((v, _) => v.Distance == 20); + checkNewId(); + + path.ResampleVertices(new double[] { 5, 10, 15 }); + checkNewId(); + + path.Clear(); + checkNewId(); + + path.ConvertFromSliderPath(new SliderPath()); + checkNewId(); + + void checkNewId() + { + Assert.That(path.InvalidationID, Is.Not.EqualTo(previousId)); + previousId = path.InvalidationID; + } + } + + private void assertInvariants(IReadOnlyList vertices, bool checkSlope) + { + Assert.That(vertices, Is.Not.Empty); + + for (int i = 0; i < vertices.Count; i++) + { + Assert.That(double.IsFinite(vertices[i].Distance)); + Assert.That(float.IsFinite(vertices[i].X)); + } + + for (int i = 1; i < vertices.Count; i++) + { + Assert.That(vertices[i].Distance, Is.GreaterThanOrEqualTo(vertices[i - 1].Distance)); + + if (!checkSlope) continue; + + float xDiff = Math.Abs(vertices[i].X - vertices[i - 1].X); + double distanceDiff = vertices[i].Distance - vertices[i - 1].Distance; + Assert.That(xDiff, Is.LessThanOrEqualTo(distanceDiff).Within(Precision.FLOAT_EPSILON)); + } + } + } +} From e507faef294748c4aac5686aef06fa343e83069b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 13:02:40 +0900 Subject: [PATCH 0480/2442] Add deep cloning support to `Score`/`ScoreInfo`/`Replay` --- osu.Game.Tests/NonVisual/ScoreInfoTest.cs | 33 +++++++++++++++++++++++ osu.Game/Replays/Replay.cs | 14 +++++++++- osu.Game/Scoring/Score.cs | 12 ++++++++- osu.Game/Scoring/ScoreInfo.cs | 11 +++++++- 4 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 osu.Game.Tests/NonVisual/ScoreInfoTest.cs diff --git a/osu.Game.Tests/NonVisual/ScoreInfoTest.cs b/osu.Game.Tests/NonVisual/ScoreInfoTest.cs new file mode 100644 index 0000000000..6e5718cd4c --- /dev/null +++ b/osu.Game.Tests/NonVisual/ScoreInfoTest.cs @@ -0,0 +1,33 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; + +namespace osu.Game.Tests.NonVisual +{ + [TestFixture] + public class ScoreInfoTest + { + [Test] + public void TestDeepClone() + { + var score = new ScoreInfo(); + + score.Statistics.Add(HitResult.Good, 10); + score.Rank = ScoreRank.B; + + var scoreCopy = score.DeepClone(); + + score.Statistics[HitResult.Good]++; + score.Rank = ScoreRank.X; + + Assert.That(scoreCopy.Statistics[HitResult.Good], Is.EqualTo(10)); + Assert.That(score.Statistics[HitResult.Good], Is.EqualTo(11)); + + Assert.That(scoreCopy.Rank, Is.EqualTo(ScoreRank.B)); + Assert.That(score.Rank, Is.EqualTo(ScoreRank.X)); + } + } +} diff --git a/osu.Game/Replays/Replay.cs b/osu.Game/Replays/Replay.cs index 5430915394..30e176b5c7 100644 --- a/osu.Game/Replays/Replay.cs +++ b/osu.Game/Replays/Replay.cs @@ -2,11 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; using osu.Game.Rulesets.Replays; +using osu.Game.Utils; namespace osu.Game.Replays { - public class Replay + public class Replay : IDeepCloneable { /// /// Whether all frames for this replay have been received. @@ -15,5 +17,15 @@ namespace osu.Game.Replays public bool HasReceivedAllFrames = true; public List Frames = new List(); + + public Replay DeepClone() + { + return new Replay + { + HasReceivedAllFrames = HasReceivedAllFrames, + // individual frames are mutable for now but hopefully this will not be a thing in the future. + Frames = Frames.ToList(), + }; + } } } diff --git a/osu.Game/Scoring/Score.cs b/osu.Game/Scoring/Score.cs index 4e82b1584e..83e4389dc8 100644 --- a/osu.Game/Scoring/Score.cs +++ b/osu.Game/Scoring/Score.cs @@ -2,12 +2,22 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Replays; +using osu.Game.Utils; namespace osu.Game.Scoring { - public class Score + public class Score : IDeepCloneable { public ScoreInfo ScoreInfo = new ScoreInfo(); public Replay Replay = new Replay(); + + public Score DeepClone() + { + return new Score + { + ScoreInfo = ScoreInfo.DeepClone(), + Replay = Replay.DeepClone(), + }; + } } } diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 1d22dab0af..a0c4d5a026 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -18,7 +18,7 @@ using osu.Game.Utils; namespace osu.Game.Scoring { - public class ScoreInfo : IHasFiles, IHasPrimaryKey, ISoftDelete, IEquatable + public class ScoreInfo : IHasFiles, IHasPrimaryKey, ISoftDelete, IEquatable, IDeepCloneable { public int ID { get; set; } @@ -242,6 +242,15 @@ namespace osu.Game.Scoring } } + public ScoreInfo DeepClone() + { + var clone = (ScoreInfo)MemberwiseClone(); + + clone.Statistics = new Dictionary(clone.Statistics); + + return clone; + } + public override string ToString() => $"{User} playing {Beatmap}"; public bool Equals(ScoreInfo other) From caba78cb5d0ebd67053537e634d613fae733933c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 13:04:16 +0900 Subject: [PATCH 0481/2442] Copy score during submission process to ensure it isn't modified --- osu.Game/Screens/Play/SoloPlayer.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/SoloPlayer.cs b/osu.Game/Screens/Play/SoloPlayer.cs index d90e8e0168..7d15fcf49f 100644 --- a/osu.Game/Screens/Play/SoloPlayer.cs +++ b/osu.Game/Screens/Play/SoloPlayer.cs @@ -38,13 +38,15 @@ namespace osu.Game.Screens.Play protected override APIRequest CreateSubmissionRequest(Score score, long token) { - var beatmap = score.ScoreInfo.Beatmap; + var scoreCopy = score.DeepClone(); + + var beatmap = scoreCopy.ScoreInfo.Beatmap; Debug.Assert(beatmap.OnlineBeatmapID != null); int beatmapId = beatmap.OnlineBeatmapID.Value; - return new SubmitSoloScoreRequest(beatmapId, token, score.ScoreInfo); + return new SubmitSoloScoreRequest(beatmapId, token, scoreCopy.ScoreInfo); } } } From 87c39909c6c3ab7f872b33a3bb20714144022ac9 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 14:37:19 +0900 Subject: [PATCH 0482/2442] Simplify `DependencyProvidingContainer` Using an array of tuple for dependencies instead of using children. --- .../TestSceneCatcher.cs | 7 +++--- .../Visual/DependencyProvidingContainer.cs | 24 ++++--------------- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index 0cf5e2b7a0..0a2dff6a21 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -47,15 +47,16 @@ namespace osu.Game.Rulesets.Catch.Tests { Anchor = Anchor.Centre, }; + droppedObjectContainer = new DroppedObjectContainer(); Child = new DependencyProvidingContainer { - Types = new[] + CachedDependencies = new (Type, object)[] { - typeof(DroppedObjectContainer), + (typeof(DroppedObjectContainer), droppedObjectContainer), }, Children = new Drawable[] { - droppedObjectContainer = new DroppedObjectContainer(), + droppedObjectContainer, catcher = new TestCatcher(trailContainer, difficulty), trailContainer }, diff --git a/osu.Game/Tests/Visual/DependencyProvidingContainer.cs b/osu.Game/Tests/Visual/DependencyProvidingContainer.cs index cd3ae1123b..c799cad61a 100644 --- a/osu.Game/Tests/Visual/DependencyProvidingContainer.cs +++ b/osu.Game/Tests/Visual/DependencyProvidingContainer.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; @@ -10,39 +9,24 @@ namespace osu.Game.Tests.Visual { /// /// A which providing ad-hoc dependencies to the child drawables. - /// - /// To provide a dependency, specify the dependency type to , then specify the dependency value to either or . - /// For each type specified in , the first value compatible with the type is selected and provided to the children. - /// /// /// - /// The and values of the dependencies must be set while this is not loaded. + /// The must be set while this is not loaded. /// public class DependencyProvidingContainer : Container { /// - /// The types of the dependencies provided to the children. + /// The dependencies provided to the children. /// // TODO: should be an init-only property when C# 9 - public Type[] Types { get; set; } = Array.Empty(); - - /// - /// The dependency values provided to the children. - /// - public object[] Values { get; set; } = Array.Empty(); + public (Type, object)[] CachedDependencies { get; set; } = Array.Empty<(Type, object)>(); protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { var dependencyContainer = new DependencyContainer(base.CreateChildDependencies(parent)); - foreach (var type in Types) - { - object value = Values.FirstOrDefault(v => type.IsInstanceOfType(v)) ?? - Children.FirstOrDefault(d => type.IsInstanceOfType(d)) ?? - throw new InvalidOperationException($"The type {type} is specified in this {nameof(DependencyProvidingContainer)}, but no corresponding value is provided."); - + foreach (var (type, value) in CachedDependencies) dependencyContainer.CacheAs(type, value); - } return dependencyContainer; } From bde35d9f211e9cd789eb6499e7321b33f87d1f31 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 16:57:12 +0900 Subject: [PATCH 0483/2442] Rename radio button classes to be local to editor --- .../Editing/TestSceneEditorComposeRadioButtons.cs | 4 ++-- .../Visual/Editing/TestSceneHitObjectComposer.cs | 6 +++--- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 4 ++-- .../{DrawableRadioButton.cs => EditorRadioButton.cs} | 6 +++--- ...tonCollection.cs => EditorRadioButtonCollection.cs} | 10 +++++----- 5 files changed, 15 insertions(+), 15 deletions(-) rename osu.Game/Screens/Edit/Components/RadioButtons/{DrawableRadioButton.cs => EditorRadioButton.cs} (94%) rename osu.Game/Screens/Edit/Components/RadioButtons/{RadioButtonCollection.cs => EditorRadioButtonCollection.cs} (85%) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorComposeRadioButtons.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorComposeRadioButtons.cs index 0b52ae2b95..028509ccd4 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorComposeRadioButtons.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorComposeRadioButtons.cs @@ -13,8 +13,8 @@ namespace osu.Game.Tests.Visual.Editing { public TestSceneEditorComposeRadioButtons() { - RadioButtonCollection collection; - Add(collection = new RadioButtonCollection + EditorRadioButtonCollection collection; + Add(collection = new EditorRadioButtonCollection { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs index 67c37413ed..550896270a 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs @@ -72,10 +72,10 @@ namespace osu.Game.Tests.Visual.Editing AddStep("clear all control points", () => editorBeatmap.ControlPointInfo.Clear()); AddAssert("Tool is selection", () => hitObjectComposer.ChildrenOfType().First().CurrentTool is SelectTool); - AddAssert("Hitcircle button not clickable", () => !hitObjectComposer.ChildrenOfType().First(d => d.Button.Label == "HitCircle").Enabled.Value); + AddAssert("Hitcircle button not clickable", () => !hitObjectComposer.ChildrenOfType().First(d => d.Button.Label == "HitCircle").Enabled.Value); AddStep("Add timing point", () => editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint())); - AddAssert("Hitcircle button is clickable", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Label == "HitCircle").Enabled.Value); - AddStep("Change to hitcircle", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Label == "HitCircle").Click()); + AddAssert("Hitcircle button is clickable", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Label == "HitCircle").Enabled.Value); + AddStep("Change to hitcircle", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Label == "HitCircle").Click()); AddAssert("Tool changed", () => hitObjectComposer.ChildrenOfType().First().CurrentTool is HitCircleCompositionTool); } diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index c6c112bec8..7ee631674e 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Edit private InputManager inputManager; - private RadioButtonCollection toolboxCollection; + private EditorRadioButtonCollection toolboxCollection; private FillFlowContainer togglesCollection; @@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Edit { new ToolboxGroup("toolbox (1-9)") { - Child = toolboxCollection = new RadioButtonCollection { RelativeSizeAxes = Axes.X } + Child = toolboxCollection = new EditorRadioButtonCollection { RelativeSizeAxes = Axes.X } }, new ToolboxGroup("toggles (Q~P)") { diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs similarity index 94% rename from osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs rename to osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs index bbe77eaa07..d66856ebd8 100644 --- a/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs +++ b/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs @@ -18,10 +18,10 @@ using osuTK.Graphics; namespace osu.Game.Screens.Edit.Components.RadioButtons { - public class DrawableRadioButton : OsuButton, IHasTooltip + public class EditorRadioButton : OsuButton, IHasTooltip { /// - /// Invoked when this has been selected. + /// Invoked when this has been selected. /// public Action Selected; @@ -37,7 +37,7 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons [Resolved(canBeNull: true)] private EditorBeatmap editorBeatmap { get; set; } - public DrawableRadioButton(RadioButton button) + public EditorRadioButton(RadioButton button) { Button = button; diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/RadioButtonCollection.cs b/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButtonCollection.cs similarity index 85% rename from osu.Game/Screens/Edit/Components/RadioButtons/RadioButtonCollection.cs rename to osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButtonCollection.cs index 16574c0baf..6a7b0c9ef7 100644 --- a/osu.Game/Screens/Edit/Components/RadioButtons/RadioButtonCollection.cs +++ b/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButtonCollection.cs @@ -9,7 +9,7 @@ using osuTK; namespace osu.Game.Screens.Edit.Components.RadioButtons { - public class RadioButtonCollection : CompositeDrawable + public class EditorRadioButtonCollection : CompositeDrawable { private IReadOnlyList items; @@ -28,13 +28,13 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons } } - private readonly FlowContainer buttonContainer; + private readonly FlowContainer buttonContainer; - public RadioButtonCollection() + public EditorRadioButtonCollection() { AutoSizeAxes = Axes.Y; - InternalChild = buttonContainer = new FillFlowContainer + InternalChild = buttonContainer = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -58,7 +58,7 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons currentlySelected = null; }; - buttonContainer.Add(new DrawableRadioButton(button)); + buttonContainer.Add(new EditorRadioButton(button)); } } } From b29209d13f6bd66b52f5d2c122001392691dd3c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 17:08:40 +0900 Subject: [PATCH 0484/2442] Ensure tool is always set back to select tool when beatmap becomes untimed --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 7ee631674e..8090fcbd32 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; @@ -67,6 +68,8 @@ namespace osu.Game.Rulesets.Edit private FillFlowContainer togglesCollection; + private IBindable hasTiming; + protected HitObjectComposer(Ruleset ruleset) { Ruleset = ruleset; @@ -160,6 +163,14 @@ namespace osu.Game.Rulesets.Edit base.LoadComplete(); inputManager = GetContainingInputManager(); + + hasTiming = EditorBeatmap.HasTiming.GetBoundCopy(); + hasTiming.BindValueChanged(timing => + { + // it's important this is performed before the similar code in EditorRadioButton disables the button. + if (!timing.NewValue) + setSelectTool(); + }); } public override Playfield Playfield => drawableRulesetWrapper.Playfield; From 13cb658d29e4372b5f986f1caad46adc2d28be91 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 18:15:09 +0900 Subject: [PATCH 0485/2442] Mark identifiers as verbatim strings --- osu.Game/Overlays/Profile/Sections/AboutSection.cs | 2 +- osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs | 2 +- osu.Game/Overlays/Profile/Sections/HistoricalSection.cs | 2 +- osu.Game/Overlays/Profile/Sections/KudosuSection.cs | 2 +- osu.Game/Overlays/Profile/Sections/MedalsSection.cs | 2 +- osu.Game/Overlays/Profile/Sections/RanksSection.cs | 2 +- osu.Game/Overlays/Profile/Sections/RecentSection.cs | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/AboutSection.cs b/osu.Game/Overlays/Profile/Sections/AboutSection.cs index c224d2b1be..d0d9362fd2 100644 --- a/osu.Game/Overlays/Profile/Sections/AboutSection.cs +++ b/osu.Game/Overlays/Profile/Sections/AboutSection.cs @@ -10,6 +10,6 @@ namespace osu.Game.Overlays.Profile.Sections { public override LocalisableString Title => UsersStrings.ShowExtraMeTitle; - public override string Identifier => "me"; + public override string Identifier => @"me"; } } diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs index b8fbe73c0c..843ab531be 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Profile.Sections { public override LocalisableString Title => UsersStrings.ShowExtraBeatmapsTitle; - public override string Identifier => "beatmaps"; + public override string Identifier => @"beatmaps"; public BeatmapsSection() { diff --git a/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs b/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs index cba25c0a8b..203844b6b5 100644 --- a/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs +++ b/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs @@ -14,7 +14,7 @@ namespace osu.Game.Overlays.Profile.Sections { public override LocalisableString Title => UsersStrings.ShowExtraHistoricalTitle; - public override string Identifier => "historical"; + public override string Identifier => @"historical"; public HistoricalSection() { diff --git a/osu.Game/Overlays/Profile/Sections/KudosuSection.cs b/osu.Game/Overlays/Profile/Sections/KudosuSection.cs index ffa7ef4eaf..5b749c78a8 100644 --- a/osu.Game/Overlays/Profile/Sections/KudosuSection.cs +++ b/osu.Game/Overlays/Profile/Sections/KudosuSection.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Profile.Sections { public override LocalisableString Title => UsersStrings.ShowExtraKudosuTitle; - public override string Identifier => "kudosu"; + public override string Identifier => @"kudosu"; public KudosuSection() { diff --git a/osu.Game/Overlays/Profile/Sections/MedalsSection.cs b/osu.Game/Overlays/Profile/Sections/MedalsSection.cs index 3512333e27..cacdd44b61 100644 --- a/osu.Game/Overlays/Profile/Sections/MedalsSection.cs +++ b/osu.Game/Overlays/Profile/Sections/MedalsSection.cs @@ -10,6 +10,6 @@ namespace osu.Game.Overlays.Profile.Sections { public override LocalisableString Title => UsersStrings.ShowExtraMedalsTitle; - public override string Identifier => "medals"; + public override string Identifier => @"medals"; } } diff --git a/osu.Game/Overlays/Profile/Sections/RanksSection.cs b/osu.Game/Overlays/Profile/Sections/RanksSection.cs index 5e0648dbb1..00a68d5bf9 100644 --- a/osu.Game/Overlays/Profile/Sections/RanksSection.cs +++ b/osu.Game/Overlays/Profile/Sections/RanksSection.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Profile.Sections { public override LocalisableString Title => UsersStrings.ShowExtraTopRanksTitle; - public override string Identifier => "top_ranks"; + public override string Identifier => @"top_ranks"; public RanksSection() { diff --git a/osu.Game/Overlays/Profile/Sections/RecentSection.cs b/osu.Game/Overlays/Profile/Sections/RecentSection.cs index 7a6536c3af..33d435aa1b 100644 --- a/osu.Game/Overlays/Profile/Sections/RecentSection.cs +++ b/osu.Game/Overlays/Profile/Sections/RecentSection.cs @@ -11,7 +11,7 @@ namespace osu.Game.Overlays.Profile.Sections { public override LocalisableString Title => UsersStrings.ShowExtraRecentActivityTitle; - public override string Identifier => "recent_activity"; + public override string Identifier => @"recent_activity"; public RecentSection() { From 443058f87994c2db0d8cbe031fd943c75f7d67e0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 18:41:29 +0900 Subject: [PATCH 0486/2442] Move playfield constant to top of file and make `internal` --- osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs index ac11bd9918..f1cdb39e91 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs @@ -28,6 +28,12 @@ namespace osu.Game.Rulesets.Catch.Objects /// public class JuiceStreamPath { + /// + /// The height of legacy osu!standard playfield. + /// The sliders converted by are vertically contained in this height. + /// + internal const float OSU_PLAYFIELD_HEIGHT = 384; + /// /// The list of vertices of the path, which is represented as a polyline connecting the vertices. /// @@ -211,12 +217,6 @@ namespace osu.Game.Rulesets.Catch.Objects invalidate(); } - /// - /// The height of legacy osu!standard playfield. - /// The sliders converted by are vertically contained in this height. - /// - public const float OSU_PLAYFIELD_HEIGHT = 384; - /// /// Convert the path of this to a and write the result to . /// The resulting slider is "folded" to make it vertically contained in the playfield `(0..)` assuming the slider start position is . From f16b4957aae84870d90c7e964e972779d01e702e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 19:18:04 +0900 Subject: [PATCH 0487/2442] Move clone to earlier in the process --- osu.Game/Screens/Play/Player.cs | 8 +++++--- osu.Game/Screens/Play/SoloPlayer.cs | 6 ++---- osu.Game/Screens/Play/SubmittingPlayer.cs | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 51f1dbd121..2328390d76 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -694,9 +694,11 @@ namespace osu.Game.Screens.Play /// The final score. private async Task prepareScoreForResults() { + var scoreCopy = Score.DeepClone(); + try { - await PrepareScoreForResultsAsync(Score).ConfigureAwait(false); + await PrepareScoreForResultsAsync(scoreCopy).ConfigureAwait(false); } catch (Exception ex) { @@ -705,14 +707,14 @@ namespace osu.Game.Screens.Play try { - await ImportScore(Score).ConfigureAwait(false); + await ImportScore(scoreCopy).ConfigureAwait(false); } catch (Exception ex) { Logger.Error(ex, @"Score import failed!"); } - return Score.ScoreInfo; + return scoreCopy.ScoreInfo; } /// diff --git a/osu.Game/Screens/Play/SoloPlayer.cs b/osu.Game/Screens/Play/SoloPlayer.cs index 7d15fcf49f..d90e8e0168 100644 --- a/osu.Game/Screens/Play/SoloPlayer.cs +++ b/osu.Game/Screens/Play/SoloPlayer.cs @@ -38,15 +38,13 @@ namespace osu.Game.Screens.Play protected override APIRequest CreateSubmissionRequest(Score score, long token) { - var scoreCopy = score.DeepClone(); - - var beatmap = scoreCopy.ScoreInfo.Beatmap; + var beatmap = score.ScoreInfo.Beatmap; Debug.Assert(beatmap.OnlineBeatmapID != null); int beatmapId = beatmap.OnlineBeatmapID.Value; - return new SubmitSoloScoreRequest(beatmapId, token, scoreCopy.ScoreInfo); + return new SubmitSoloScoreRequest(beatmapId, token, score.ScoreInfo); } } } diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index 76e9f28dae..65622a4702 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -116,7 +116,7 @@ namespace osu.Game.Screens.Play { var exiting = base.OnExiting(next); - submitScore(Score); + submitScore(Score.DeepClone()); return exiting; } From b3f60c82535fe292cd172b291449cb0d6a61a878 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 19:28:35 +0900 Subject: [PATCH 0488/2442] Fix date being updated on replays unexpectedly --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 1 - osu.Game/Screens/Play/Player.cs | 8 +------- osu.Game/Screens/Play/SubmittingPlayer.cs | 2 ++ 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index eeb881cd39..6a2601170c 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -338,7 +338,6 @@ namespace osu.Game.Rulesets.Scoring score.MaxCombo = HighestCombo.Value; score.Accuracy = Accuracy.Value; score.Rank = Rank.Value; - score.Date = DateTimeOffset.Now; foreach (var result in Enum.GetValues(typeof(HitResult)).OfType().Where(r => r.IsScorable())) score.Statistics[result] = GetStatistic(result); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 2328390d76..dbe8d530b9 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -1019,13 +1019,7 @@ namespace osu.Game.Screens.Play /// /// The to prepare. /// A task that prepares the provided score. On completion, the score is assumed to be ready for display. - protected virtual Task PrepareScoreForResultsAsync(Score score) - { - // perform one final population to ensure everything is up-to-date. - ScoreProcessor.PopulateScore(score.ScoreInfo); - - return Task.CompletedTask; - } + protected virtual Task PrepareScoreForResultsAsync(Score score) => Task.CompletedTask; /// /// Creates the for a . diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index 65622a4702..5faa384d03 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -109,6 +109,8 @@ namespace osu.Game.Screens.Play { await base.PrepareScoreForResultsAsync(score).ConfigureAwait(false); + score.ScoreInfo.Date = DateTimeOffset.Now; + await submitScore(score).ConfigureAwait(false); } From 97059a9f50afafe091ffed155afd287e4970854d Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 19:44:40 +0900 Subject: [PATCH 0489/2442] Create `Catcher` in `CatchPlayfield` --- .../TestSceneCatcherArea.cs | 5 ++- .../TestSceneHyperDashColouring.cs | 7 ++-- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 30 ++++++++++------ osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 36 +++++++++++-------- 4 files changed, 51 insertions(+), 27 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs index 877e115e2f..18a6e0a66f 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs @@ -123,8 +123,11 @@ namespace osu.Game.Rulesets.Catch.Tests private readonly DroppedObjectContainer droppedObjectContainer; public TestCatcherArea(BeatmapDifficulty beatmapDifficulty) - : base(beatmapDifficulty) { + MovableCatcher = new Catcher(this, beatmapDifficulty) + { + X = CatchPlayfield.CENTER_X + }; AddInternal(droppedObjectContainer = new DroppedObjectContainer()); } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index e7b0259ea2..61057f4c4d 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -213,8 +213,11 @@ namespace osu.Game.Rulesets.Catch.Tests public TestCatcherArea() { - Scale = new Vector2(4f); - + MovableCatcher = new Catcher(this, new BeatmapDifficulty()) + { + X = CatchPlayfield.CENTER_X, + Scale = new Vector2(4) + }; AddInternal(droppedObjectContainer = new DroppedObjectContainer()); } } diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 05cd29dff5..727d3aca0f 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawables; @@ -26,31 +27,40 @@ namespace osu.Game.Rulesets.Catch.UI /// public const float CENTER_X = WIDTH / 2; - [Cached] - private readonly DroppedObjectContainer droppedObjectContainer; - - internal readonly CatcherArea CatcherArea; - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => // only check the X position; handle all vertical space. base.ReceivePositionalInputAt(new Vector2(screenSpacePos.X, ScreenSpaceDrawQuad.Centre.Y)); + internal readonly Catcher Catcher; + + internal readonly CatcherArea CatcherArea; + + [Cached] + private readonly DroppedObjectContainer droppedObjectContainer; + public CatchPlayfield(BeatmapDifficulty difficulty) { - CatcherArea = new CatcherArea(difficulty) + var trailContainer = new Container(); + + Catcher = new Catcher(trailContainer, difficulty) { - Anchor = Anchor.BottomLeft, - Origin = Anchor.TopLeft, + X = CENTER_X }; InternalChildren = new[] { droppedObjectContainer = new DroppedObjectContainer(), - CatcherArea.MovableCatcher.CreateProxiedContent(), + Catcher.CreateProxiedContent(), HitObjectContainer.CreateProxy(), // This ordering (`CatcherArea` before `HitObjectContainer`) is important to // make sure the up-to-date catcher position is used for the catcher catching logic of hit objects. - CatcherArea, + CatcherArea = new CatcherArea + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.TopLeft, + MovableCatcher = Catcher + }, + trailContainer, HitObjectContainer, }; } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index fea314df8d..a9d0275678 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -5,7 +5,6 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; -using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Judgements; using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Catch.Replays; @@ -20,9 +19,22 @@ namespace osu.Game.Rulesets.Catch.UI { public const float CATCHER_SIZE = 106.75f; - public readonly Catcher MovableCatcher; + public Catcher MovableCatcher + { + get => catcher; + set + { + if (catcher != null) + Remove(catcher); + + Add(catcher = value); + } + } + private readonly CatchComboDisplay comboDisplay; + private Catcher catcher; + /// /// -1 when only left button is pressed. /// 1 when only right button is pressed. @@ -30,21 +42,17 @@ namespace osu.Game.Rulesets.Catch.UI /// private int currentDirection; - public CatcherArea(BeatmapDifficulty difficulty = null) + public CatcherArea() { Size = new Vector2(CatchPlayfield.WIDTH, CATCHER_SIZE); - Children = new Drawable[] + Child = comboDisplay = new CatchComboDisplay { - comboDisplay = new CatchComboDisplay - { - RelativeSizeAxes = Axes.None, - AutoSizeAxes = Axes.Both, - Anchor = Anchor.TopLeft, - Origin = Anchor.Centre, - Margin = new MarginPadding { Bottom = 350f }, - X = CatchPlayfield.CENTER_X - }, - MovableCatcher = new Catcher(this, difficulty) { X = CatchPlayfield.CENTER_X }, + RelativeSizeAxes = Axes.None, + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopLeft, + Origin = Anchor.Centre, + Margin = new MarginPadding { Bottom = 350f }, + X = CatchPlayfield.CENTER_X }; } From 50f9e5f3629ce7003a54456daf2bfcea8ed3f588 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 19:52:40 +0900 Subject: [PATCH 0490/2442] Replace usage of `CatcherArea.MovableCatcher` with `Catcher` --- osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs | 4 ++-- osu.Game.Rulesets.Catch/Edit/CatchEditorPlayfield.cs | 2 +- osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs | 4 +--- osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs | 2 +- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 2 +- osu.Game.Rulesets.Catch/UI/CatchReplayRecorder.cs | 2 +- 6 files changed, 7 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs index fd6a9c7b7b..e7c7dc3c98 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs @@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Catch.Tests private bool playfieldIsEmpty => !((CatchPlayfield)drawableRuleset.Playfield).AllHitObjects.Any(h => h.IsAlive); - private CatcherAnimationState catcherState => ((CatchPlayfield)drawableRuleset.Playfield).CatcherArea.MovableCatcher.CurrentState; + private CatcherAnimationState catcherState => ((CatchPlayfield)drawableRuleset.Playfield).Catcher.CurrentState; private void spawnFruits(bool hit = false) { @@ -162,7 +162,7 @@ namespace osu.Game.Rulesets.Catch.Tests float xCoords = CatchPlayfield.CENTER_X; if (drawableRuleset.Playfield is CatchPlayfield catchPlayfield) - catchPlayfield.CatcherArea.MovableCatcher.X = xCoords - x_offset; + catchPlayfield.Catcher.X = xCoords - x_offset; if (hit) xCoords -= x_offset; diff --git a/osu.Game.Rulesets.Catch/Edit/CatchEditorPlayfield.cs b/osu.Game.Rulesets.Catch/Edit/CatchEditorPlayfield.cs index d383eb9ba6..8c9f292aa9 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchEditorPlayfield.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchEditorPlayfield.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Catch.Edit base.LoadComplete(); // TODO: honor "hit animation" setting? - CatcherArea.MovableCatcher.CatchFruitOnPlate = false; + Catcher.CatchFruitOnPlate = false; // TODO: disable hit lighting as well } diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs b/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs index 71268d899d..f399f48ebd 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs @@ -41,9 +41,7 @@ namespace osu.Game.Rulesets.Catch.Mods { base.Update(); - var catcherArea = playfield.CatcherArea; - - FlashlightPosition = catcherArea.ToSpaceOfOtherDrawable(catcherArea.MovableCatcher.DrawPosition, this); + FlashlightPosition = playfield.CatcherArea.ToSpaceOfOtherDrawable(playfield.Catcher.DrawPosition, this); } private float getSizeFor(int combo) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs index f9e106f097..d48edbcd74 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Catch.Mods var drawableCatchRuleset = (DrawableCatchRuleset)drawableRuleset; var catchPlayfield = (CatchPlayfield)drawableCatchRuleset.Playfield; - catchPlayfield.CatcherArea.MovableCatcher.CatchFruitOnPlate = false; + catchPlayfield.Catcher.CatchFruitOnPlate = false; } protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 727d3aca0f..21a96d6635 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Catch.UI ((DrawableCatchHitObject)d).CheckPosition = checkIfWeCanCatch; } - private bool checkIfWeCanCatch(CatchHitObject obj) => CatcherArea.MovableCatcher.CanCatch(obj); + private bool checkIfWeCanCatch(CatchHitObject obj) => Catcher.CanCatch(obj); private void onNewResult(DrawableHitObject judgedObject, JudgementResult result) => CatcherArea.OnNewResult((DrawableCatchHitObject)judgedObject, result); diff --git a/osu.Game.Rulesets.Catch/UI/CatchReplayRecorder.cs b/osu.Game.Rulesets.Catch/UI/CatchReplayRecorder.cs index 1ddb5ac630..a7879846df 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchReplayRecorder.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchReplayRecorder.cs @@ -21,6 +21,6 @@ namespace osu.Game.Rulesets.Catch.UI } protected override ReplayFrame HandleFrame(Vector2 mousePosition, List actions, ReplayFrame previousFrame) - => new CatchReplayFrame(Time.Current, playfield.CatcherArea.MovableCatcher.X, actions.Contains(CatchAction.Dash), previousFrame as CatchReplayFrame); + => new CatchReplayFrame(Time.Current, playfield.Catcher.X, actions.Contains(CatchAction.Dash), previousFrame as CatchReplayFrame); } } From 063f14da98b0491a874b9014c230f5e96e830b07 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 19:55:08 +0900 Subject: [PATCH 0491/2442] Update test room manager to not return passwords --- osu.Game/Online/Rooms/JoinRoomRequest.cs | 12 ++--- .../Multiplayer/TestMultiplayerRoomManager.cs | 47 ++++++++++++------- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/osu.Game/Online/Rooms/JoinRoomRequest.cs b/osu.Game/Online/Rooms/JoinRoomRequest.cs index 53bd2a6106..0111882d9a 100644 --- a/osu.Game/Online/Rooms/JoinRoomRequest.cs +++ b/osu.Game/Online/Rooms/JoinRoomRequest.cs @@ -9,23 +9,23 @@ namespace osu.Game.Online.Rooms { public class JoinRoomRequest : APIRequest { - private readonly Room room; - private readonly string password; + public readonly Room Room; + public readonly string Password; public JoinRoomRequest(Room room, string password) { - this.room = room; - this.password = password; + Room = room; + Password = password; } protected override WebRequest CreateWebRequest() { var req = base.CreateWebRequest(); req.Method = HttpMethod.Put; - req.AddParameter("password", password); + req.AddParameter("password", Password); return req; } - protected override string Target => $"rooms/{room.RoomID.Value}/users/{User.Id}"; + protected override string Target => $"rooms/{Room.RoomID.Value}/users/{User.Id}"; } } diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs index 5d66cdba02..77238f47fb 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs @@ -45,21 +45,33 @@ namespace osu.Game.Tests.Visual.Multiplayer switch (req) { case CreateRoomRequest createRoomRequest: - var createdRoom = new APICreatedRoom(); + var apiRoom = new Room(); - createdRoom.CopyFrom(createRoomRequest.Room); - createdRoom.RoomID.Value ??= currentRoomId++; + apiRoom.CopyFrom(createRoomRequest.Room); + apiRoom.RoomID.Value ??= currentRoomId++; + for (int i = 0; i < apiRoom.Playlist.Count; i++) + apiRoom.Playlist[i].ID = currentPlaylistItemId++; - for (int i = 0; i < createdRoom.Playlist.Count; i++) - createdRoom.Playlist[i].ID = currentPlaylistItemId++; + var responseRoom = new APICreatedRoom(); + responseRoom.CopyFrom(createResponseRoom(apiRoom, false)); - Rooms.Add(createdRoom); - createRoomRequest.TriggerSuccess(createdRoom); + Rooms.Add(apiRoom); + createRoomRequest.TriggerSuccess(responseRoom); return true; case JoinRoomRequest joinRoomRequest: + { + var room = Rooms.Single(r => r.RoomID.Value == joinRoomRequest.Room.RoomID.Value); + + if (joinRoomRequest.Password != room.Password.Value) + { + joinRoomRequest.TriggerFailure(new InvalidOperationException("Invalid password.")); + return true; + } + joinRoomRequest.TriggerSuccess(); return true; + } case PartRoomRequest partRoomRequest: partRoomRequest.TriggerSuccess(); @@ -69,20 +81,13 @@ namespace osu.Game.Tests.Visual.Multiplayer var roomsWithoutParticipants = new List(); foreach (var r in Rooms) - { - var newRoom = new Room(); - - newRoom.CopyFrom(r); - newRoom.RecentParticipants.Clear(); - - roomsWithoutParticipants.Add(newRoom); - } + roomsWithoutParticipants.Add(createResponseRoom(r, false)); getRoomsRequest.TriggerSuccess(roomsWithoutParticipants); return true; case GetRoomRequest getRoomRequest: - getRoomRequest.TriggerSuccess(Rooms.Single(r => r.RoomID.Value == getRoomRequest.RoomId)); + getRoomRequest.TriggerSuccess(createResponseRoom(Rooms.Single(r => r.RoomID.Value == getRoomRequest.RoomId), true)); return true; case GetBeatmapSetRequest getBeatmapSetRequest: @@ -118,6 +123,16 @@ namespace osu.Game.Tests.Visual.Multiplayer }; } + private Room createResponseRoom(Room room, bool withParticipants) + { + var responseRoom = new Room(); + responseRoom.CopyFrom(room); + responseRoom.Password.Value = null; + if (!withParticipants) + responseRoom.RecentParticipants.Clear(); + return responseRoom; + } + public new void ClearRooms() => base.ClearRooms(); public new void Schedule(Action action) => base.Schedule(action); From 8c0daa89a0ec0d027c0ba20d6d131a94073d5259 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 20:01:44 +0900 Subject: [PATCH 0492/2442] Make test multiplayer client validate password --- osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index adc632a2b1..82cb68b452 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -119,6 +119,9 @@ namespace osu.Game.Tests.Visual.Multiplayer { var apiRoom = roomManager.Rooms.Single(r => r.RoomID.Value == roomId); + if (password != apiRoom.Password.Value) + throw new InvalidOperationException("Invalid password."); + var localUser = new MultiplayerRoomUser(api.LocalUser.Value.Id) { User = api.LocalUser.Value From 2515785f933682593721279519855552507085a2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 20:02:14 +0900 Subject: [PATCH 0493/2442] Use room password to fill settings textbox --- osu.Game/Online/Rooms/Room.cs | 2 +- .../Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs | 1 + osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index f79a410bd9..48e9f94dd5 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -85,6 +85,7 @@ namespace osu.Game.Online.Rooms #region Properties only used for room creation request + [Cached(Name = nameof(Password))] [JsonProperty("password")] public readonly Bindable Password = new Bindable(); @@ -159,7 +160,6 @@ namespace osu.Game.Online.Rooms ChannelId.Value = other.ChannelId.Value; Status.Value = other.Status.Value; Availability.Value = other.Availability.Value; - Password.Value = other.Password.Value; HasPassword.Value = other.HasPassword.Value; Type.Value = other.Type.Value; MaxParticipants.Value = other.MaxParticipants.Value; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index fceb124e0a..338d2c9e84 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -271,6 +271,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match Type.BindValueChanged(type => TypePicker.Current.Value = type.NewValue, true); MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true); RoomID.BindValueChanged(roomId => initialBeatmapControl.Alpha = roomId.NewValue == null ? 1 : 0, true); + Password.BindValueChanged(password => PasswordTextBox.Text = password.NewValue ?? string.Empty, true); operationInProgress.BindTo(ongoingOperationTracker.InProgress); operationInProgress.BindValueChanged(v => diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index eb0b23f13f..0b28bc1a7e 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -56,6 +56,9 @@ namespace osu.Game.Screens.OnlinePlay [Resolved(typeof(Room))] protected Bindable Availability { get; private set; } + [Resolved(typeof(Room), nameof(Room.Password))] + public Bindable Password { get; private set; } + [Resolved(typeof(Room))] protected Bindable Duration { get; private set; } From 26d0eea4854d836e7af67617b7c2688f4d3d7b2a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 20:03:00 +0900 Subject: [PATCH 0494/2442] Set HasPassword correctly in the response room --- osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs index 77238f47fb..c94c0b8683 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs @@ -127,6 +127,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { var responseRoom = new Room(); responseRoom.CopyFrom(room); + responseRoom.HasPassword.Value = !string.IsNullOrEmpty(responseRoom.Password.Value); responseRoom.Password.Value = null; if (!withParticipants) responseRoom.RecentParticipants.Clear(); From a5a0f12e199f4fc2b7fe171f0879b4974e8744f3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 20:07:56 +0900 Subject: [PATCH 0495/2442] Also copy password in test room manager --- .../Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs index c94c0b8683..59679f3d66 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs @@ -49,6 +49,11 @@ namespace osu.Game.Tests.Visual.Multiplayer apiRoom.CopyFrom(createRoomRequest.Room); apiRoom.RoomID.Value ??= currentRoomId++; + + // Passwords are explicitly not copied between rooms. + apiRoom.HasPassword.Value = !string.IsNullOrEmpty(createRoomRequest.Room.Password.Value); + apiRoom.Password.Value = createRoomRequest.Room.Password.Value; + for (int i = 0; i < apiRoom.Playlist.Count; i++) apiRoom.Playlist[i].ID = currentPlaylistItemId++; @@ -127,7 +132,6 @@ namespace osu.Game.Tests.Visual.Multiplayer { var responseRoom = new Room(); responseRoom.CopyFrom(room); - responseRoom.HasPassword.Value = !string.IsNullOrEmpty(responseRoom.Password.Value); responseRoom.Password.Value = null; if (!withParticipants) responseRoom.RecentParticipants.Clear(); From 1b9d297911e071b569e64f31428a377384c41353 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 20:08:29 +0900 Subject: [PATCH 0496/2442] Add test --- .../Multiplayer/TestSceneMultiplayer.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 7673efb78f..f45ecca424 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -85,6 +85,26 @@ namespace osu.Game.Tests.Visual.Multiplayer // used to test the flow of multiplayer from visual tests. } + [Test] + public void TestCreateRoomWithPassword() + { + createRoom(() => new Room + { + Name = { Value = "Test Room" }, + Password = { Value = "password" }, + Playlist = + { + new PlaylistItem + { + Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + } + } + }); + + AddAssert("room has password", () => client.APIRoom?.Password.Value == "password"); + } + [Test] public void TestUserSetToIdleWhenBeatmapDeleted() { From b88ee3c1a1e183a6f153b17c0a4aac8b641822e5 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 20:11:49 +0900 Subject: [PATCH 0497/2442] Pass `DroppedObjectContainer` via constructor instead of DI It is now just one level deep, so it is not beneficial to use DI here. This effectively reverts ae09c23e. --- .../TestSceneCatchSkinConfiguration.cs | 7 +-- .../TestSceneCatcher.cs | 35 +++++++-------- .../TestSceneCatcherArea.cs | 10 ++--- .../TestSceneHyperDashColouring.cs | 43 ++++++++----------- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 10 ++--- osu.Game.Rulesets.Catch/UI/Catcher.cs | 6 +-- 6 files changed, 46 insertions(+), 65 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs index ec186bcfb2..83f28086e6 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs @@ -3,7 +3,6 @@ using System.Linq; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -24,16 +23,12 @@ namespace osu.Game.Rulesets.Catch.Tests { public class TestSceneCatchSkinConfiguration : OsuTestScene { - [Cached] - private readonly DroppedObjectContainer droppedObjectContainer; - private Catcher catcher; private readonly Container container; public TestSceneCatchSkinConfiguration() { - Add(droppedObjectContainer = new DroppedObjectContainer()); Add(container = new Container { RelativeSizeAxes = Axes.Both }); } @@ -46,7 +41,7 @@ namespace osu.Game.Rulesets.Catch.Tests var skin = new TestSkin { FlipCatcherPlate = flip }; container.Child = new SkinProvidingContainer(skin) { - Child = catcher = new Catcher(new Container()) + Child = catcher = new Catcher(new Container(), new DroppedObjectContainer()) { Anchor = Anchor.Centre } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index 8359657f84..b4282e6784 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -31,23 +31,12 @@ namespace osu.Game.Rulesets.Catch.Tests [Resolved] private OsuConfigManager config { get; set; } - [Cached] - private readonly DroppedObjectContainer droppedObjectContainer; + private Container trailContainer; - private readonly Container trailContainer; + private DroppedObjectContainer droppedObjectContainer; private TestCatcher catcher; - public TestSceneCatcher() - { - Add(trailContainer = new Container - { - Anchor = Anchor.Centre, - Depth = -1 - }); - Add(droppedObjectContainer = new DroppedObjectContainer()); - } - [SetUp] public void SetUp() => Schedule(() => { @@ -56,13 +45,19 @@ namespace osu.Game.Rulesets.Catch.Tests CircleSize = 0, }; - if (catcher != null) - Remove(catcher); + trailContainer = new Container(); + droppedObjectContainer = new DroppedObjectContainer(); - Add(catcher = new TestCatcher(trailContainer, difficulty) + Child = new Container { - Anchor = Anchor.Centre - }); + Anchor = Anchor.Centre, + Children = new Drawable[] + { + droppedObjectContainer, + catcher = new TestCatcher(trailContainer, droppedObjectContainer, difficulty), + trailContainer, + } + }; }); [Test] @@ -299,8 +294,8 @@ namespace osu.Game.Rulesets.Catch.Tests { public IEnumerable CaughtObjects => this.ChildrenOfType(); - public TestCatcher(Container trailsTarget, BeatmapDifficulty difficulty) - : base(trailsTarget, difficulty) + public TestCatcher(Container trailsTarget, DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty) + : base(trailsTarget, droppedObjectTarget, difficulty) { } } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs index 18a6e0a66f..5a2e5b60f5 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs @@ -119,16 +119,16 @@ namespace osu.Game.Rulesets.Catch.Tests private class TestCatcherArea : CatcherArea { - [Cached] - private readonly DroppedObjectContainer droppedObjectContainer; - public TestCatcherArea(BeatmapDifficulty beatmapDifficulty) { - MovableCatcher = new Catcher(this, beatmapDifficulty) + var droppedObjectContainer = new DroppedObjectContainer(); + + Add(droppedObjectContainer); + + MovableCatcher = new Catcher(this, droppedObjectContainer, beatmapDifficulty) { X = CatchPlayfield.CENTER_X }; - AddInternal(droppedObjectContainer = new DroppedObjectContainer()); } public void ToggleHyperDash(bool status) => MovableCatcher.SetHyperDashState(status ? 2 : 1); diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index 61057f4c4d..73797d0a6a 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -113,36 +113,45 @@ namespace osu.Game.Rulesets.Catch.Tests private void checkHyperDashCatcherColour(ISkin skin, Color4 expectedCatcherColour, Color4? expectedEndGlowColour = null) { - CatcherArea catcherArea = null; + Container trailsContainer = null; + Catcher catcher = null; CatcherTrailDisplay trails = null; AddStep("create hyper-dashing catcher", () => { - Child = setupSkinHierarchy(catcherArea = new TestCatcherArea + trailsContainer = new Container(); + Child = setupSkinHierarchy(new Container { Anchor = Anchor.Centre, - Origin = Anchor.Centre + Children = new Drawable[] + { + catcher = new Catcher(trailsContainer, new DroppedObjectContainer()) + { + Scale = new Vector2(4) + }, + trailsContainer + } }, skin); }); AddStep("get trails container", () => { - trails = catcherArea.OfType().Single(); - catcherArea.MovableCatcher.SetHyperDashState(2); + trails = trailsContainer.OfType().Single(); + catcher.SetHyperDashState(2); }); - AddUntilStep("catcher colour is correct", () => catcherArea.MovableCatcher.Colour == expectedCatcherColour); + AddUntilStep("catcher colour is correct", () => catcher.Colour == expectedCatcherColour); AddAssert("catcher trails colours are correct", () => trails.HyperDashTrailsColour == expectedCatcherColour); AddAssert("catcher end-glow colours are correct", () => trails.EndGlowSpritesColour == (expectedEndGlowColour ?? expectedCatcherColour)); AddStep("finish hyper-dashing", () => { - catcherArea.MovableCatcher.SetHyperDashState(); - catcherArea.MovableCatcher.FinishTransforms(); + catcher.SetHyperDashState(); + catcher.FinishTransforms(); }); - AddAssert("catcher colour returned to white", () => catcherArea.MovableCatcher.Colour == Color4.White); + AddAssert("catcher colour returned to white", () => catcher.Colour == Color4.White); } private void checkHyperDashFruitColour(ISkin skin, Color4 expectedColour) @@ -205,21 +214,5 @@ namespace osu.Game.Rulesets.Catch.Tests { } } - - private class TestCatcherArea : CatcherArea - { - [Cached] - private readonly DroppedObjectContainer droppedObjectContainer; - - public TestCatcherArea() - { - MovableCatcher = new Catcher(this, new BeatmapDifficulty()) - { - X = CatchPlayfield.CENTER_X, - Scale = new Vector2(4) - }; - AddInternal(droppedObjectContainer = new DroppedObjectContainer()); - } - } } } diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 21a96d6635..27bbc32fd9 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -35,21 +35,19 @@ namespace osu.Game.Rulesets.Catch.UI internal readonly CatcherArea CatcherArea; - [Cached] - private readonly DroppedObjectContainer droppedObjectContainer; - public CatchPlayfield(BeatmapDifficulty difficulty) { var trailContainer = new Container(); + var droppedObjectContainer = new DroppedObjectContainer(); - Catcher = new Catcher(trailContainer, difficulty) + Catcher = new Catcher(trailContainer, droppedObjectContainer, difficulty) { X = CENTER_X }; InternalChildren = new[] { - droppedObjectContainer = new DroppedObjectContainer(), + droppedObjectContainer, Catcher.CreateProxiedContent(), HitObjectContainer.CreateProxy(), // This ordering (`CatcherArea` before `HitObjectContainer`) is important to @@ -58,7 +56,7 @@ namespace osu.Game.Rulesets.Catch.UI { Anchor = Anchor.BottomLeft, Origin = Anchor.TopLeft, - MovableCatcher = Catcher + MovableCatcher = Catcher, }, trailContainer, HitObjectContainer, diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 57523d3505..a595536a06 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -74,8 +74,7 @@ namespace osu.Game.Rulesets.Catch.UI /// /// Contains objects dropped from the plate. /// - [Resolved] - private DroppedObjectContainer droppedObjectTarget { get; set; } + private readonly DroppedObjectContainer droppedObjectTarget; public CatcherAnimationState CurrentState { @@ -134,9 +133,10 @@ namespace osu.Game.Rulesets.Catch.UI private readonly DrawablePool caughtBananaPool; private readonly DrawablePool caughtDropletPool; - public Catcher([NotNull] Container trailsTarget, BeatmapDifficulty difficulty = null) + public Catcher([NotNull] Container trailsTarget, DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty = null) { this.trailsTarget = trailsTarget; + this.droppedObjectTarget = droppedObjectTarget; Origin = Anchor.TopCentre; From 879467961f9266ba7de239dccb3642ea8361aaf5 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 20:13:31 +0900 Subject: [PATCH 0498/2442] Fix catcher trails displayed in wrong place --- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 27bbc32fd9..19538aacde 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -37,7 +37,11 @@ namespace osu.Game.Rulesets.Catch.UI public CatchPlayfield(BeatmapDifficulty difficulty) { - var trailContainer = new Container(); + var trailContainer = new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.TopLeft + }; var droppedObjectContainer = new DroppedObjectContainer(); Catcher = new Catcher(trailContainer, droppedObjectContainer, difficulty) From 7201cfe0b4b36eddad1188f7c94908ed1d1af8ae Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 20:18:17 +0900 Subject: [PATCH 0499/2442] Move child drawable creation of `CatchPlayfield` from constructor to `load`. --- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 19538aacde..dcbac47505 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -16,6 +16,8 @@ namespace osu.Game.Rulesets.Catch.UI { public class CatchPlayfield : ScrollingPlayfield { + private readonly BeatmapDifficulty difficulty; + /// /// The width of the playfield. /// The horizontal movement of the catcher is confined in the area of this width. @@ -31,11 +33,17 @@ namespace osu.Game.Rulesets.Catch.UI // only check the X position; handle all vertical space. base.ReceivePositionalInputAt(new Vector2(screenSpacePos.X, ScreenSpaceDrawQuad.Centre.Y)); - internal readonly Catcher Catcher; + internal Catcher Catcher { get; private set; } - internal readonly CatcherArea CatcherArea; + internal CatcherArea CatcherArea { get; private set; } public CatchPlayfield(BeatmapDifficulty difficulty) + { + this.difficulty = difficulty; + } + + [BackgroundDependencyLoader] + private void load() { var trailContainer = new Container { @@ -49,7 +57,7 @@ namespace osu.Game.Rulesets.Catch.UI X = CENTER_X }; - InternalChildren = new[] + AddRangeInternal(new[] { droppedObjectContainer, Catcher.CreateProxiedContent(), @@ -64,12 +72,8 @@ namespace osu.Game.Rulesets.Catch.UI }, trailContainer, HitObjectContainer, - }; - } + }); - [BackgroundDependencyLoader] - private void load() - { RegisterPool(50); RegisterPool(50); RegisterPool(100); From d6aa15e5d7c92df054e29a12b6ff2d00068bec8b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 20:19:23 +0900 Subject: [PATCH 0500/2442] Remove local APIRoom from test multiplayer client --- .../Tests/Visual/Multiplayer/TestMultiplayerClient.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 82cb68b452..1528ed0bc8 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -28,7 +28,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public override IBindable IsConnected => isConnected; private readonly Bindable isConnected = new Bindable(true); - public Room? APIRoom { get; private set; } + public new Room? APIRoom => base.APIRoom; public Action? RoomSetupAction; @@ -138,6 +138,7 @@ namespace osu.Game.Tests.Visual.Multiplayer RequiredMods = apiRoom.Playlist.Last().RequiredMods.Select(m => new APIMod(m)).ToArray(), AllowedMods = apiRoom.Playlist.Last().AllowedMods.Select(m => new APIMod(m)).ToArray(), PlaylistItemId = apiRoom.Playlist.Last().ID, + // ReSharper disable once ConstantNullCoalescingCondition Incorrect inspection due to lack of nullable in Room.cs. Password = password ?? string.Empty, }, Users = { localUser }, @@ -147,16 +148,10 @@ namespace osu.Game.Tests.Visual.Multiplayer RoomSetupAction?.Invoke(room); RoomSetupAction = null; - APIRoom = apiRoom; - return Task.FromResult(room); } - protected override Task LeaveRoomInternal() - { - APIRoom = null; - return Task.CompletedTask; - } + protected override Task LeaveRoomInternal() => Task.CompletedTask; public override Task TransferHost(int userId) => ((IMultiplayerClient)this).HostChanged(userId); From 2eec524f2744e68705f9ede888ca57796b2bae4d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 20:20:08 +0900 Subject: [PATCH 0501/2442] Fix password not copied from multiplayer client --- .../Multiplayer/TestSceneMultiplayer.cs | 21 ++++++++++++++ .../Online/Multiplayer/MultiplayerClient.cs | 29 ++++++++++--------- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index f45ecca424..4ea628cb55 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -105,6 +105,27 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("room has password", () => client.APIRoom?.Password.Value == "password"); } + [Test] + public void TestLocalPasswordUpdatedWhenMultiplayerSettingsChange() + { + createRoom(() => new Room + { + Name = { Value = "Test Room" }, + Password = { Value = "password" }, + Playlist = + { + new PlaylistItem + { + Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + } + } + }); + + AddStep("change password", () => client.ChangeSettings(password: "password2")); + AddUntilStep("local password changed", () => client.APIRoom?.Password.Value == "password2"); + } + [Test] public void TestUserSetToIdleWhenBeatmapDeleted() { diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 42d436ef11..9972d7e88d 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -92,7 +92,7 @@ namespace osu.Game.Online.Multiplayer [Resolved] private UserLookupCache userLookupCache { get; set; } = null!; - private Room? apiRoom; + protected Room? APIRoom { get; private set; } [BackgroundDependencyLoader] private void load() @@ -139,7 +139,7 @@ namespace osu.Game.Online.Multiplayer await scheduleAsync(() => { Room = joinedRoom; - apiRoom = room; + APIRoom = room; foreach (var user in joinedRoom.Users) updateUserPlayingState(user.UserID, user.State); }, cancellationSource.Token).ConfigureAwait(false); @@ -168,7 +168,7 @@ namespace osu.Game.Online.Multiplayer // For example, if a room was left and the user immediately pressed the "create room" button, then the user could be taken into the lobby if the value of Room is not reset in time. var scheduledReset = scheduleAsync(() => { - apiRoom = null; + APIRoom = null; Room = null; CurrentMatchPlayingUserIds.Clear(); @@ -305,22 +305,22 @@ namespace osu.Game.Online.Multiplayer if (Room == null) return; - Debug.Assert(apiRoom != null); + Debug.Assert(APIRoom != null); Room.State = state; switch (state) { case MultiplayerRoomState.Open: - apiRoom.Status.Value = new RoomStatusOpen(); + APIRoom.Status.Value = new RoomStatusOpen(); break; case MultiplayerRoomState.Playing: - apiRoom.Status.Value = new RoomStatusPlaying(); + APIRoom.Status.Value = new RoomStatusPlaying(); break; case MultiplayerRoomState.Closed: - apiRoom.Status.Value = new RoomStatusEnded(); + APIRoom.Status.Value = new RoomStatusEnded(); break; } @@ -381,12 +381,12 @@ namespace osu.Game.Online.Multiplayer if (Room == null) return; - Debug.Assert(apiRoom != null); + Debug.Assert(APIRoom != null); var user = Room.Users.FirstOrDefault(u => u.UserID == userId); Room.Host = user; - apiRoom.Host.Value = user?.User; + APIRoom.Host.Value = user?.User; RoomUpdated?.Invoke(); }, false); @@ -529,11 +529,12 @@ namespace osu.Game.Online.Multiplayer if (Room == null) return; - Debug.Assert(apiRoom != null); + Debug.Assert(APIRoom != null); // Update a few properties of the room instantaneously. Room.Settings = settings; - apiRoom.Name.Value = Room.Settings.Name; + APIRoom.Name.Value = Room.Settings.Name; + APIRoom.Password.Value = Room.Settings.Password; // The current item update is delayed until an online beatmap lookup (below) succeeds. // In-order for the client to not display an outdated beatmap, the current item is forcefully cleared here. @@ -555,7 +556,7 @@ namespace osu.Game.Online.Multiplayer if (Room == null || !Room.Settings.Equals(settings)) return; - Debug.Assert(apiRoom != null); + Debug.Assert(APIRoom != null); var beatmap = beatmapSet.Beatmaps.Single(b => b.OnlineBeatmapID == settings.BeatmapID); beatmap.MD5Hash = settings.BeatmapChecksum; @@ -565,7 +566,7 @@ namespace osu.Game.Online.Multiplayer var allowedMods = settings.AllowedMods.Select(m => m.ToMod(ruleset)); // Try to retrieve the existing playlist item from the API room. - var playlistItem = apiRoom.Playlist.FirstOrDefault(i => i.ID == settings.PlaylistItemId); + var playlistItem = APIRoom.Playlist.FirstOrDefault(i => i.ID == settings.PlaylistItemId); if (playlistItem != null) updateItem(playlistItem); @@ -573,7 +574,7 @@ namespace osu.Game.Online.Multiplayer { // An existing playlist item does not exist, so append a new one. updateItem(playlistItem = new PlaylistItem()); - apiRoom.Playlist.Add(playlistItem); + APIRoom.Playlist.Add(playlistItem); } CurrentMatchPlayingItem.Value = playlistItem; From 41169fbdaffbac67917cc0de15b7608021526088 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 20:20:10 +0900 Subject: [PATCH 0502/2442] Add `[NotNull]` --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index a595536a06..a671bce590 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -133,7 +133,7 @@ namespace osu.Game.Rulesets.Catch.UI private readonly DrawablePool caughtBananaPool; private readonly DrawablePool caughtDropletPool; - public Catcher([NotNull] Container trailsTarget, DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty = null) + public Catcher([NotNull] Container trailsTarget, [NotNull] DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty = null) { this.trailsTarget = trailsTarget; this.droppedObjectTarget = droppedObjectTarget; From 80c2b1449bb2ae088104a37712b17829a32893d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 19 Jul 2021 20:27:00 +0900 Subject: [PATCH 0503/2442] Fix API request potentially firing failed events after completion Specifically, `Cancel()` calls were not thread safe. Due to a series of events, `ListPollingComponent` could call `Cancel` from a non-update thread, leading to a race condition where both a `Success` and `Fail` event can be fired. This is intended to be the simplest fix possible, locking and guarding specifically on the callbacks. Further work could be done in the future to improve the flow surrounding `pendingFailure`, potentially reducing redundant work and cleaning up the code, but that's not happening here. Closes https://github.com/ppy/osu/issues/13632. --- osu.Game/Online/API/APIException.cs | 15 +++++ osu.Game/Online/API/APIRequest.cs | 58 ++++++++++++------- .../Online/API/APIRequestCompletionState.cs | 23 ++++++++ osu.Game/Online/PollingComponent.cs | 2 +- 4 files changed, 75 insertions(+), 23 deletions(-) create mode 100644 osu.Game/Online/API/APIException.cs create mode 100644 osu.Game/Online/API/APIRequestCompletionState.cs diff --git a/osu.Game/Online/API/APIException.cs b/osu.Game/Online/API/APIException.cs new file mode 100644 index 0000000000..97786bced9 --- /dev/null +++ b/osu.Game/Online/API/APIException.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; + +namespace osu.Game.Online.API +{ + public class APIException : InvalidOperationException + { + public APIException(string messsage, Exception innerException) + : base(messsage, innerException) + { + } + } +} diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index 1a6868cfa4..8d816d3975 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -79,7 +79,13 @@ namespace osu.Game.Online.API /// public event APIFailureHandler Failure; - private bool cancelled; + private readonly object completionStateLock = new object(); + + /// + /// The state of this request, from an outside perspective. + /// This is used to ensure correct notification events are fired. + /// + private APIRequestCompletionState completionState; private Action pendingFailure; @@ -116,12 +122,7 @@ namespace osu.Game.Online.API PostProcess(); - API.Schedule(delegate - { - if (cancelled) return; - - TriggerSuccess(); - }); + API.Schedule(TriggerSuccess); } /// @@ -131,16 +132,29 @@ namespace osu.Game.Online.API { } - private bool succeeded; - internal virtual void TriggerSuccess() { - succeeded = true; + lock (completionStateLock) + { + if (completionState != APIRequestCompletionState.Waiting) + return; + + completionState = APIRequestCompletionState.Completed; + } + Success?.Invoke(); } internal void TriggerFailure(Exception e) { + lock (completionStateLock) + { + if (completionState != APIRequestCompletionState.Waiting) + return; + + completionState = APIRequestCompletionState.Failed; + } + Failure?.Invoke(e); } @@ -148,10 +162,14 @@ namespace osu.Game.Online.API public void Fail(Exception e) { - if (succeeded || cancelled) - return; + lock (completionStateLock) + { + // while it doesn't matter if code following this check is run more than once, + // this avoids unnecessarily performing work where we are already sure the user has been informed. + if (completionState != APIRequestCompletionState.Waiting) + return; + } - cancelled = true; WebRequest?.Abort(); string responseString = WebRequest?.GetResponseString(); @@ -181,7 +199,11 @@ namespace osu.Game.Online.API /// Whether we are in a failed or cancelled state. private bool checkAndScheduleFailure() { - if (pendingFailure == null) return cancelled; + lock (completionStateLock) + { + if (pendingFailure == null) + return completionState == APIRequestCompletionState.Failed; + } if (API == null) pendingFailure(); @@ -199,14 +221,6 @@ namespace osu.Game.Online.API } } - public class APIException : InvalidOperationException - { - public APIException(string messsage, Exception innerException) - : base(messsage, innerException) - { - } - } - public delegate void APIFailureHandler(Exception e); public delegate void APISuccessHandler(); diff --git a/osu.Game/Online/API/APIRequestCompletionState.cs b/osu.Game/Online/API/APIRequestCompletionState.cs new file mode 100644 index 0000000000..84c9974dd8 --- /dev/null +++ b/osu.Game/Online/API/APIRequestCompletionState.cs @@ -0,0 +1,23 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Online.API +{ + public enum APIRequestCompletionState + { + /// + /// Not yet run or currently waiting on response. + /// + Waiting, + + /// + /// Ran to completion. + /// + Completed, + + /// + /// Cancelled or failed due to error. + /// + Failed + } +} diff --git a/osu.Game/Online/PollingComponent.cs b/osu.Game/Online/PollingComponent.cs index 3d19f2ab09..806c0047e7 100644 --- a/osu.Game/Online/PollingComponent.cs +++ b/osu.Game/Online/PollingComponent.cs @@ -70,7 +70,7 @@ namespace osu.Game.Online return true; } - // not ennough time has passed since the last poll. we do want to schedule a poll to happen, though. + // not enough time has passed since the last poll. we do want to schedule a poll to happen, though. scheduleNextPoll(); return false; } From 5fc13975643b0d517634358c70a56dad974737b5 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sun, 18 Jul 2021 09:55:40 +0800 Subject: [PATCH 0504/2442] Apply suggestion from code review Co-authored-by: frenzibyte --- osu.Game/Overlays/Changelog/ChangelogHeader.cs | 2 +- osu.Game/Overlays/News/NewsHeader.cs | 3 ++- osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs | 1 - osu.Game/Overlays/Wiki/WikiHeader.cs | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index a282c77bd6..56e852fe80 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Changelog public ChangelogUpdateStreamControl Streams; - public LocalisableString ListingString => LayoutStrings.HeaderChangelogIndex; + public static LocalisableString ListingString => LayoutStrings.HeaderChangelogIndex; private Box streamsBackground; diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs index b6e49811ad..7e10d855fe 100644 --- a/osu.Game/Overlays/News/NewsHeader.cs +++ b/osu.Game/Overlays/News/NewsHeader.cs @@ -12,7 +12,8 @@ namespace osu.Game.Overlays.News { public class NewsHeader : BreadcrumbControlOverlayHeader { - public LocalisableString FrontPageString => osu.Game.Resources.Localisation.Web.NewsStrings.IndexTitleInfo; + public static LocalisableString FrontPageString => NewsStrings.IndexTitleInfo; + public Action ShowFrontPage; private readonly Bindable article = new Bindable(); diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs index 92750d8806..ba88eec6aa 100644 --- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs @@ -72,5 +72,4 @@ namespace osu.Game.Overlays.Rankings } } } - } diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index f24d59a8d3..286711c518 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -14,7 +14,8 @@ namespace osu.Game.Overlays.Wiki public class WikiHeader : BreadcrumbControlOverlayHeader { private const string index_path = "Main_Page"; - public LocalisableString IndexPageString => LayoutStrings.HeaderHelpIndex; + + public static LocalisableString IndexPageString => LayoutStrings.HeaderHelpIndex; public readonly Bindable WikiPageData = new Bindable(); From 57eed886018810be9f83c198ee8240b25ffa3595 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sun, 18 Jul 2021 10:12:24 +0800 Subject: [PATCH 0505/2442] symbol renaming Co-authored-by: frenzibyte --- ...nStrings.cs => NamedOverlayComponentStrings.cs} | 14 +++++++------- .../BeatmapListing/BeatmapListingHeader.cs | 2 +- osu.Game/Overlays/Changelog/ChangelogHeader.cs | 2 +- .../Overlays/Dashboard/DashboardOverlayHeader.cs | 2 +- osu.Game/Overlays/News/NewsHeader.cs | 2 +- .../Overlays/Rankings/RankingsOverlayHeader.cs | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) rename osu.Game/Localisation/{HeaderDescriptionStrings.cs => NamedOverlayComponentStrings.cs} (50%) diff --git a/osu.Game/Localisation/HeaderDescriptionStrings.cs b/osu.Game/Localisation/NamedOverlayComponentStrings.cs similarity index 50% rename from osu.Game/Localisation/HeaderDescriptionStrings.cs rename to osu.Game/Localisation/NamedOverlayComponentStrings.cs index 6ed703191a..6cd19a0977 100644 --- a/osu.Game/Localisation/HeaderDescriptionStrings.cs +++ b/osu.Game/Localisation/NamedOverlayComponentStrings.cs @@ -5,34 +5,34 @@ using osu.Framework.Localisation; namespace osu.Game.Localisation { - public static class HeaderDescriptionStrings + public static class NamedOverlayComponentStrings { - private const string prefix = @"osu.Game.Resources.Localisation.HeaderDescription"; + private const string prefix = @"osu.Game.Resources.Localisation.NamedOverlayComponent"; /// /// "browse for new beatmaps" /// - public static LocalisableString BeatmapListing => new TranslatableString(getKey(@"beatmap_listing"), @"browse for new beatmaps"); + public static LocalisableString BeatmapListingDescription => new TranslatableString(getKey(@"beatmap_listing"), @"browse for new beatmaps"); /// /// "track recent dev updates in the osu! ecosystem" /// - public static LocalisableString Changelog => new TranslatableString(getKey(@"changelog"), @"track recent dev updates in the osu! ecosystem"); + public static LocalisableString ChangelogDescription => new TranslatableString(getKey(@"changelog"), @"track recent dev updates in the osu! ecosystem"); /// /// "view your friends and other information" /// - public static LocalisableString Dashboard => new TranslatableString(getKey(@"dashboard"), @"view your friends and other information"); + public static LocalisableString DashboardDescription => new TranslatableString(getKey(@"dashboard"), @"view your friends and other information"); /// /// "find out who's the best right now" /// - public static LocalisableString Rankings => new TranslatableString(getKey(@"rankings"), @"find out who's the best right now"); + public static LocalisableString RankingsDescription => new TranslatableString(getKey(@"rankings"), @"find out who's the best right now"); /// /// "get up-to-date on community happenings" /// - public static LocalisableString News => new TranslatableString(getKey(@"news"), @"get up-to-date on community happenings"); + public static LocalisableString NewsDescription => new TranslatableString(getKey(@"news"), @"get up-to-date on community happenings"); private static string getKey(string key) => $"{prefix}:{key}"; } diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs index 48ecbce7c1..3568fe9e4f 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs @@ -15,7 +15,7 @@ namespace osu.Game.Overlays.BeatmapListing public BeatmapListingTitle() { Title = PageTitleStrings.MainBeatmapsetsControllerIndex; - Description = HeaderDescriptionStrings.BeatmapListing; + Description = NamedOverlayComponentStrings.BeatmapListingDescription; IconTexture = "Icons/Hexacons/beatmap"; } } diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index 56e852fe80..ef174298c2 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -118,7 +118,7 @@ namespace osu.Game.Overlays.Changelog public ChangelogHeaderTitle() { Title = LayoutStrings.MenuHomeChangelogIndex; - Description = HeaderDescriptionStrings.Changelog; + Description = NamedOverlayComponentStrings.ChangelogDescription; IconTexture = "Icons/Hexacons/devtools"; } } diff --git a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs index 284e95ad84..5e69caad6e 100644 --- a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs +++ b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs @@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Dashboard public DashboardTitle() { Title = HomeStrings.UserTitle; - Description = HeaderDescriptionStrings.Dashboard; + Description = NamedOverlayComponentStrings.DashboardDescription; IconTexture = "Icons/Hexacons/social"; } } diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs index 7e10d855fe..23fab6faaf 100644 --- a/osu.Game/Overlays/News/NewsHeader.cs +++ b/osu.Game/Overlays/News/NewsHeader.cs @@ -65,7 +65,7 @@ namespace osu.Game.Overlays.News public NewsHeaderTitle() { Title = LayoutStrings.MenuHomeNewsIndex; - Description = HeaderDescriptionStrings.News; + Description = NamedOverlayComponentStrings.NewsDescription; IconTexture = "Icons/Hexacons/news"; } } diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs index ba88eec6aa..812c2f2182 100644 --- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Rankings public RankingsTitle() { Title = LayoutStrings.MenuRankingsDefault; - Description = HeaderDescriptionStrings.Rankings; + Description = NamedOverlayComponentStrings.RankingsDescription; IconTexture = "Icons/Hexacons/rankings"; } } From 765881d8b0c2c89b6d0934f9d734d81670047643 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Mon, 19 Jul 2021 19:23:26 +0800 Subject: [PATCH 0506/2442] Move strings --- .../NamedOverlayComponentStrings.cs | 22 +++++++++++++++++++ .../BeatmapListing/BeatmapListingHeader.cs | 3 +-- .../Overlays/Changelog/ChangelogHeader.cs | 2 +- .../Dashboard/DashboardOverlayHeader.cs | 2 +- osu.Game/Overlays/News/NewsHeader.cs | 2 +- .../Rankings/RankingsOverlayHeader.cs | 2 +- osu.Game/Overlays/Wiki/WikiHeader.cs | 5 +++-- 7 files changed, 30 insertions(+), 8 deletions(-) diff --git a/osu.Game/Localisation/NamedOverlayComponentStrings.cs b/osu.Game/Localisation/NamedOverlayComponentStrings.cs index 6cd19a0977..0167a1b030 100644 --- a/osu.Game/Localisation/NamedOverlayComponentStrings.cs +++ b/osu.Game/Localisation/NamedOverlayComponentStrings.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Localisation { @@ -9,31 +10,52 @@ namespace osu.Game.Localisation { private const string prefix = @"osu.Game.Resources.Localisation.NamedOverlayComponent"; + /// + public static LocalisableString BeatmapListingTitle => PageTitleStrings.MainBeatmapsetsControllerIndex; + /// /// "browse for new beatmaps" /// public static LocalisableString BeatmapListingDescription => new TranslatableString(getKey(@"beatmap_listing"), @"browse for new beatmaps"); + /// + public static LocalisableString ChangelogTitle => PageTitleStrings.MainChangelogControllerDefault; + /// /// "track recent dev updates in the osu! ecosystem" /// public static LocalisableString ChangelogDescription => new TranslatableString(getKey(@"changelog"), @"track recent dev updates in the osu! ecosystem"); + /// + public static LocalisableString DashboardTitle => PageTitleStrings.MainHomeControllerIndex; + /// /// "view your friends and other information" /// public static LocalisableString DashboardDescription => new TranslatableString(getKey(@"dashboard"), @"view your friends and other information"); + /// + public static LocalisableString RankingsTitle => PageTitleStrings.MainRankingControllerDefault; + /// /// "find out who's the best right now" /// public static LocalisableString RankingsDescription => new TranslatableString(getKey(@"rankings"), @"find out who's the best right now"); + /// + public static LocalisableString NewsTitle => PageTitleStrings.MainNewsControllerDefault; + /// /// "get up-to-date on community happenings" /// public static LocalisableString NewsDescription => new TranslatableString(getKey(@"news"), @"get up-to-date on community happenings"); + /// + public static LocalisableString WikiTitle => LayoutStrings.MenuHelpGetWiki; + + /// + public static LocalisableString WikiDescription => PageTitleStrings.MainWikiControllerDefault; + private static string getKey(string key) => $"{prefix}:{key}"; } } diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs index 3568fe9e4f..fa2ee0d735 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Localisation; -using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapListing { @@ -14,7 +13,7 @@ namespace osu.Game.Overlays.BeatmapListing { public BeatmapListingTitle() { - Title = PageTitleStrings.MainBeatmapsetsControllerIndex; + Title = NamedOverlayComponentStrings.BeatmapListingTitle; Description = NamedOverlayComponentStrings.BeatmapListingDescription; IconTexture = "Icons/Hexacons/beatmap"; } diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index ef174298c2..8707883ddd 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -117,7 +117,7 @@ namespace osu.Game.Overlays.Changelog { public ChangelogHeaderTitle() { - Title = LayoutStrings.MenuHomeChangelogIndex; + Title = NamedOverlayComponentStrings.ChangelogTitle; Description = NamedOverlayComponentStrings.ChangelogDescription; IconTexture = "Icons/Hexacons/devtools"; } diff --git a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs index 5e69caad6e..05c9f30ff3 100644 --- a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs +++ b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Dashboard { public DashboardTitle() { - Title = HomeStrings.UserTitle; + Title = NamedOverlayComponentStrings.DashboardTitle; Description = NamedOverlayComponentStrings.DashboardDescription; IconTexture = "Icons/Hexacons/social"; } diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs index 23fab6faaf..bf739e1e8e 100644 --- a/osu.Game/Overlays/News/NewsHeader.cs +++ b/osu.Game/Overlays/News/NewsHeader.cs @@ -64,7 +64,7 @@ namespace osu.Game.Overlays.News { public NewsHeaderTitle() { - Title = LayoutStrings.MenuHomeNewsIndex; + Title = NamedOverlayComponentStrings.NewsTitle; Description = NamedOverlayComponentStrings.NewsDescription; IconTexture = "Icons/Hexacons/news"; } diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs index 812c2f2182..b6c16d398d 100644 --- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs @@ -33,7 +33,7 @@ namespace osu.Game.Overlays.Rankings { public RankingsTitle() { - Title = LayoutStrings.MenuRankingsDefault; + Title = NamedOverlayComponentStrings.RankingsTitle; Description = NamedOverlayComponentStrings.RankingsDescription; IconTexture = "Icons/Hexacons/rankings"; } diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index 286711c518..4c90551d35 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Localisation; +using osu.Game.Localisation; using osu.Game.Online.API.Requests.Responses; using osu.Game.Resources.Localisation.Web; @@ -76,8 +77,8 @@ namespace osu.Game.Overlays.Wiki { public WikiHeaderTitle() { - Title = LayoutStrings.MenuHelpGetWiki; - Description = PageTitleStrings.MainWikiControllerDefault; + Title = NamedOverlayComponentStrings.WikiTitle; + Description = NamedOverlayComponentStrings.WikiDescription; IconTexture = "Icons/Hexacons/wiki"; } } From 3168a927dc691e6025355cc872fba67c577a3a2c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 20:50:30 +0900 Subject: [PATCH 0507/2442] Fix possible exception --- .../Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 561fa220c8..f35671a761 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -326,7 +326,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { // the room may not be left immediately after a disconnection due to async flow, // so checking the IsConnected status is also required. - if (client.Room == null || !client.IsConnected.Value) + if (client?.Room == null || !client.IsConnected.Value) { // room has not been created yet; exit immediately. return base.OnExiting(next); From 05295241b878dc5ac51d4851765af77295fda6f8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 20:55:14 +0900 Subject: [PATCH 0508/2442] Add room joining tests --- .../Multiplayer/TestSceneMultiplayer.cs | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 4ea628cb55..92f9c5733f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Testing; @@ -22,6 +23,7 @@ using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens; using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Lounge; +using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; @@ -85,6 +87,50 @@ namespace osu.Game.Tests.Visual.Multiplayer // used to test the flow of multiplayer from visual tests. } + [Test] + public void TestCreateRoomWithoutPassword() + { + createRoom(() => new Room + { + Name = { Value = "Test Room" }, + Playlist = + { + new PlaylistItem + { + Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + } + } + }); + } + + [Test] + public void TestJoinRoomWithoutPassword() + { + AddStep("create room", () => + { + API.Queue(new CreateRoomRequest(new Room + { + Name = { Value = "Test Room" }, + Playlist = + { + new PlaylistItem + { + Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + } + } + })); + }); + + AddStep("refresh rooms", () => multiplayerScreen.RoomManager.Filter.Value = new FilterCriteria()); + AddStep("select room", () => InputManager.Key(Key.Down)); + AddStep("join room", () => InputManager.Key(Key.Enter)); + + AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); + AddUntilStep("wait for join", () => client.Room != null); + } + [Test] public void TestCreateRoomWithPassword() { @@ -105,6 +151,39 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("room has password", () => client.APIRoom?.Password.Value == "password"); } + [Test] + public void TestJoinRoomWithPassword() + { + AddStep("create room", () => + { + API.Queue(new CreateRoomRequest(new Room + { + Name = { Value = "Test Room" }, + Password = { Value = "password" }, + Playlist = + { + new PlaylistItem + { + Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + } + } + })); + }); + + AddStep("refresh rooms", () => multiplayerScreen.RoomManager.Filter.Value = new FilterCriteria()); + AddStep("select room", () => InputManager.Key(Key.Down)); + AddStep("join room", () => InputManager.Key(Key.Enter)); + + DrawableRoom.PasswordEntryPopover passwordEntryPopover = null; + AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null); + AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password"); + AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().Click()); + + AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); + AddUntilStep("wait for join", () => client.Room != null); + } + [Test] public void TestLocalPasswordUpdatedWhenMultiplayerSettingsChange() { From a001e4aa166675a81c8beacc065284aebc158871 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 20:55:25 +0900 Subject: [PATCH 0509/2442] Fix web request failing if password is null --- osu.Game/Online/Rooms/JoinRoomRequest.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Rooms/JoinRoomRequest.cs b/osu.Game/Online/Rooms/JoinRoomRequest.cs index 0111882d9a..eab508ab7d 100644 --- a/osu.Game/Online/Rooms/JoinRoomRequest.cs +++ b/osu.Game/Online/Rooms/JoinRoomRequest.cs @@ -22,7 +22,10 @@ namespace osu.Game.Online.Rooms { var req = base.CreateWebRequest(); req.Method = HttpMethod.Put; - req.AddParameter("password", Password); + + if (!string.IsNullOrEmpty(Password)) + req.AddParameter("password", Password); + return req; } From 3a4da6b86795c22cabe94028a75f99875574d3a4 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Mon, 19 Jul 2021 20:02:39 +0800 Subject: [PATCH 0510/2442] use same code style Co-authored-by: frenzibyte --- osu.Game/Localisation/NamedOverlayComponentStrings.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Localisation/NamedOverlayComponentStrings.cs b/osu.Game/Localisation/NamedOverlayComponentStrings.cs index 0167a1b030..6694d8c008 100644 --- a/osu.Game/Localisation/NamedOverlayComponentStrings.cs +++ b/osu.Game/Localisation/NamedOverlayComponentStrings.cs @@ -51,10 +51,12 @@ namespace osu.Game.Localisation public static LocalisableString NewsDescription => new TranslatableString(getKey(@"news"), @"get up-to-date on community happenings"); /// - public static LocalisableString WikiTitle => LayoutStrings.MenuHelpGetWiki; + public static LocalisableString WikiTitle => PageTitleStrings.MainWikiControllerDefault; - /// - public static LocalisableString WikiDescription => PageTitleStrings.MainWikiControllerDefault; + /// + /// "knowledge base" + /// + public static LocalisableString WikiDescription => new TranslatableString(getKey(@"wiki"), @"knowledge base"); private static string getKey(string key) => $"{prefix}:{key}"; } From 81d0a9bd9c4c5acc5050c0dbbaa23eccbfac0b89 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 21:05:36 +0900 Subject: [PATCH 0511/2442] Fix item ordering --- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index dcbac47505..6f84793744 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -16,8 +16,6 @@ namespace osu.Game.Rulesets.Catch.UI { public class CatchPlayfield : ScrollingPlayfield { - private readonly BeatmapDifficulty difficulty; - /// /// The width of the playfield. /// The horizontal movement of the catcher is confined in the area of this width. @@ -37,6 +35,8 @@ namespace osu.Game.Rulesets.Catch.UI internal CatcherArea CatcherArea { get; private set; } + private readonly BeatmapDifficulty difficulty; + public CatchPlayfield(BeatmapDifficulty difficulty) { this.difficulty = difficulty; From 0a43e54dfc5d605d45f55cc1327cb95056fdc262 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 21:24:22 +0900 Subject: [PATCH 0512/2442] Fix request failing due to parameters --- osu.Game/Online/Rooms/JoinRoomRequest.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Online/Rooms/JoinRoomRequest.cs b/osu.Game/Online/Rooms/JoinRoomRequest.cs index eab508ab7d..b2d772cac7 100644 --- a/osu.Game/Online/Rooms/JoinRoomRequest.cs +++ b/osu.Game/Online/Rooms/JoinRoomRequest.cs @@ -22,13 +22,10 @@ namespace osu.Game.Online.Rooms { var req = base.CreateWebRequest(); req.Method = HttpMethod.Put; - - if (!string.IsNullOrEmpty(Password)) - req.AddParameter("password", Password); - return req; } - protected override string Target => $"rooms/{Room.RoomID.Value}/users/{User.Id}"; + // Todo: Password needs to be specified here rather than via AddParameter() because this is a PUT request. May be a framework bug. + protected override string Target => $"rooms/{Room.RoomID.Value}/users/{User.Id}?password={Password}"; } } From fe7aa73aad810d08608dc8722a53de9587c9dc02 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Mon, 19 Jul 2021 20:45:03 +0800 Subject: [PATCH 0513/2442] Add localisation for BeatmapSetHeader --- osu.Game/Localisation/NamedOverlayComponentStrings.cs | 3 +++ osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Localisation/NamedOverlayComponentStrings.cs b/osu.Game/Localisation/NamedOverlayComponentStrings.cs index 6694d8c008..f40b97e605 100644 --- a/osu.Game/Localisation/NamedOverlayComponentStrings.cs +++ b/osu.Game/Localisation/NamedOverlayComponentStrings.cs @@ -18,6 +18,9 @@ namespace osu.Game.Localisation /// public static LocalisableString BeatmapListingDescription => new TranslatableString(getKey(@"beatmap_listing"), @"browse for new beatmaps"); + /// + public static LocalisableString BeatmapSetTitle => PageTitleStrings.MainBeatmapsetsControllerShow; + /// public static LocalisableString ChangelogTitle => PageTitleStrings.MainChangelogControllerDefault; diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs index 4b26b02a8e..4fd24d9819 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs @@ -7,6 +7,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Effects; using osu.Game.Beatmaps; +using osu.Game.Localisation; using osu.Game.Rulesets; using osuTK; using osuTK.Graphics; @@ -54,7 +55,7 @@ namespace osu.Game.Overlays.BeatmapSet { public BeatmapHeaderTitle() { - Title = "beatmap info"; + Title = NamedOverlayComponentStrings.BeatmapSetTitle; IconTexture = "Icons/Hexacons/beatmap"; } } From 892d858d5f4852a65cb8e2af022745a0b12a618d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 22:23:31 +0900 Subject: [PATCH 0514/2442] Fix compile error --- osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 951b00376e..54c01e2c20 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -249,7 +249,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { new OsuMenuItem("Create copy", MenuItemType.Standard, () => { - parentScreen?.OpenNewRoom(Room.CreateCopy()); + parentScreen?.OpenNewRoom(Room.DeepClone()); }) }; From 57a99886d5f3fd2da33b9ce8c620dde142338856 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 22:31:01 +0900 Subject: [PATCH 0515/2442] Fix password icon not disappearing when no password --- .../Visual/Multiplayer/TestSceneRoomStatus.cs | 26 +++++++++++++++++++ .../Lounge/Components/DrawableRoom.cs | 19 +++++++------- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs index cec40635f3..6f1b34b078 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs @@ -2,8 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; +using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Framework.Utils; using osu.Game.Online.Rooms; using osu.Game.Online.Rooms.RoomStatuses; using osu.Game.Screens.OnlinePlay.Lounge.Components; @@ -47,5 +51,27 @@ namespace osu.Game.Tests.Visual.Multiplayer } }; } + + [Test] + public void TestEnableAndDisablePassword() + { + DrawableRoom drawableRoom = null; + Room room = null; + + AddStep("create room", () => Child = drawableRoom = new DrawableRoom(room = new Room + { + Name = { Value = "Room with password" }, + Status = { Value = new RoomStatusOpen() }, + Category = { Value = RoomCategory.Realtime }, + }) { MatchingFilter = true }); + + AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType().Single().Alpha)); + + AddStep("set password", () => room.Password.Value = "password"); + AddAssert("password icon visible", () => Precision.AlmostEquals(1, drawableRoom.ChildrenOfType().Single().Alpha)); + + AddStep("unset password", () => room.Password.Value = string.Empty); + AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType().Single().Alpha)); + } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 54c01e2c20..236408851f 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -58,8 +58,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components [Resolved(canBeNull: true)] private LoungeSubScreen lounge { get; set; } - private Container content; - public readonly Room Room; private SelectionState state; @@ -105,6 +103,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components public bool FilteringActive { get; set; } + private PasswordProtectedIcon passwordIcon; + + private readonly Bindable hasPassword = new Bindable(); + public DrawableRoom(Room room) { Room = room; @@ -138,7 +140,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding(SELECTION_BORDER_WIDTH), - Child = content = new Container + Child = new Container { RelativeSizeAxes = Axes.Both, Masking = true, @@ -214,15 +216,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components }, }, }, + passwordIcon = new PasswordProtectedIcon { Alpha = 0 } }, }, }, }; - - if (Room.HasPassword.Value) - { - content.Add(new PasswordProtectedIcon()); - } } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) @@ -241,6 +239,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components this.FadeInFromZero(transition_duration); else Alpha = 0; + + hasPassword.BindTo(Room.HasPassword); + hasPassword.BindValueChanged(v => passwordIcon.Alpha = v.NewValue ? 1 : 0, true); } public Popover GetPopover() => new PasswordEntryPopover(Room) { JoinRequested = lounge.Join }; @@ -313,7 +314,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } - private class PasswordProtectedIcon : CompositeDrawable + public class PasswordProtectedIcon : CompositeDrawable { [BackgroundDependencyLoader] private void load(OsuColour colours) From 7956f73f629641a178f5b0015cdc43de3d2412e1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 22:31:53 +0900 Subject: [PATCH 0516/2442] Move initial content into step --- .../Visual/Multiplayer/TestSceneRoomStatus.cs | 64 ++++++++++--------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs index 6f1b34b078..8c4133418c 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomStatus.cs @@ -16,40 +16,44 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneRoomStatus : OsuTestScene { - public TestSceneRoomStatus() + [Test] + public void TestMultipleStatuses() { - Child = new FillFlowContainer + AddStep("create rooms", () => { - RelativeSizeAxes = Axes.Both, - Width = 0.5f, - Children = new Drawable[] + Child = new FillFlowContainer { - new DrawableRoom(new Room + RelativeSizeAxes = Axes.Both, + Width = 0.5f, + Children = new Drawable[] { - Name = { Value = "Open - ending in 1 day" }, - Status = { Value = new RoomStatusOpen() }, - EndDate = { Value = DateTimeOffset.Now.AddDays(1) } - }) { MatchingFilter = true }, - new DrawableRoom(new Room - { - Name = { Value = "Playing - ending in 1 day" }, - Status = { Value = new RoomStatusPlaying() }, - EndDate = { Value = DateTimeOffset.Now.AddDays(1) } - }) { MatchingFilter = true }, - new DrawableRoom(new Room - { - Name = { Value = "Ended" }, - Status = { Value = new RoomStatusEnded() }, - EndDate = { Value = DateTimeOffset.Now } - }) { MatchingFilter = true }, - new DrawableRoom(new Room - { - Name = { Value = "Open" }, - Status = { Value = new RoomStatusOpen() }, - Category = { Value = RoomCategory.Realtime } - }) { MatchingFilter = true }, - } - }; + new DrawableRoom(new Room + { + Name = { Value = "Open - ending in 1 day" }, + Status = { Value = new RoomStatusOpen() }, + EndDate = { Value = DateTimeOffset.Now.AddDays(1) } + }) { MatchingFilter = true }, + new DrawableRoom(new Room + { + Name = { Value = "Playing - ending in 1 day" }, + Status = { Value = new RoomStatusPlaying() }, + EndDate = { Value = DateTimeOffset.Now.AddDays(1) } + }) { MatchingFilter = true }, + new DrawableRoom(new Room + { + Name = { Value = "Ended" }, + Status = { Value = new RoomStatusEnded() }, + EndDate = { Value = DateTimeOffset.Now } + }) { MatchingFilter = true }, + new DrawableRoom(new Room + { + Name = { Value = "Open" }, + Status = { Value = new RoomStatusOpen() }, + Category = { Value = RoomCategory.Realtime } + }) { MatchingFilter = true }, + } + }; + }); } [Test] From 6a55cb9df0e529a137c6fbd430bffb2676afbe40 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Jul 2021 22:52:05 +0900 Subject: [PATCH 0517/2442] Revert unintended change It's a deeper issue with ScreenStack (see: https://github.com/ppy/osu-framework/issues/4619). --- .../Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index f35671a761..561fa220c8 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -326,7 +326,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { // the room may not be left immediately after a disconnection due to async flow, // so checking the IsConnected status is also required. - if (client?.Room == null || !client.IsConnected.Value) + if (client.Room == null || !client.IsConnected.Value) { // room has not been created yet; exit immediately. return base.OnExiting(next); From 8cc1630655e96bf01d9f253bb5526fabed9d9c2b Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 22:15:35 +0900 Subject: [PATCH 0518/2442] Add initial juice stream editing --- .../Blueprints/Components/EditablePath.cs | 159 ++++++++++++++++++ .../Components/SelectionEditablePath.cs | 90 ++++++++++ .../Edit/Blueprints/Components/VertexPiece.cs | 31 ++++ .../Edit/Blueprints/Components/VertexState.cs | 18 ++ .../JuiceStreamSelectionBlueprint.cs | 56 +++++- 5 files changed, 352 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs create mode 100644 osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs create mode 100644 osu.Game.Rulesets.Catch/Edit/Blueprints/Components/VertexPiece.cs create mode 100644 osu.Game.Rulesets.Catch/Edit/Blueprints/Components/VertexState.cs diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs new file mode 100644 index 0000000000..a51f18ff33 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs @@ -0,0 +1,159 @@ +// 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 JetBrains.Annotations; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.UI.Scrolling; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components +{ + public abstract class EditablePath : CompositeDrawable + { + public int PathId => path.InvalidationID; + + public IReadOnlyList Vertices => path.Vertices; + + public int VertexCount => path.Vertices.Count; + + protected readonly Func PositionToDistance; + + protected IReadOnlyList VertexStates => vertexStates; + + private readonly JuiceStreamPath path = new JuiceStreamPath(); + + // Invariant: `path.Vertices.Count == vertexStates.Count` + private readonly List vertexStates = new List + { + new VertexState { IsFixed = true } + }; + + private readonly List previousVertexStates = new List(); + + [Resolved(CanBeNull = true)] + [CanBeNull] + private IBeatSnapProvider beatSnapProvider { get; set; } + + protected EditablePath(Func positionToDistance) + { + PositionToDistance = positionToDistance; + + Anchor = Anchor.BottomLeft; + } + + public void UpdateFrom(ScrollingHitObjectContainer hitObjectContainer, JuiceStream hitObject) + { + while (path.Vertices.Count < InternalChildren.Count) + RemoveInternal(InternalChildren[^1]); + + while (InternalChildren.Count < path.Vertices.Count) + AddInternal(new VertexPiece()); + + double distanceToYFactor = -hitObjectContainer.LengthAtTime(hitObject.StartTime, hitObject.StartTime + 1 / hitObject.Velocity); + + for (int i = 0; i < VertexCount; i++) + { + var piece = (VertexPiece)InternalChildren[i]; + var vertex = path.Vertices[i]; + piece.Position = new Vector2(vertex.X, (float)(vertex.Distance * distanceToYFactor)); + piece.UpdateFrom(vertexStates[i]); + } + } + + public void InitializeFromHitObject(JuiceStream hitObject) + { + var sliderPath = hitObject.Path; + path.ConvertFromSliderPath(sliderPath); + + // If the original slider path has non-linear type segments, resample the vertices at nested hit object times to reduce the number of vertices. + if (sliderPath.ControlPoints.Any(p => p.Type.Value != null && p.Type.Value != PathType.Linear)) + { + path.ResampleVertices(hitObject.NestedHitObjects + .Skip(1).TakeWhile(h => !(h is Fruit)) // Only droplets in the first span are used. + .Select(h => (h.StartTime - hitObject.StartTime) * hitObject.Velocity)); + } + + vertexStates.Clear(); + vertexStates.AddRange(path.Vertices.Select((_, i) => new VertexState + { + IsFixed = i == 0 + })); + } + + public void UpdateHitObjectFromPath(JuiceStream hitObject) + { + path.ConvertToSliderPath(hitObject.Path, hitObject.LegacyConvertedY); + + if (beatSnapProvider == null) return; + + double endTime = hitObject.StartTime + path.Distance / hitObject.Velocity; + double snappedEndTime = beatSnapProvider.SnapTime(endTime, hitObject.StartTime); + hitObject.Path.ExpectedDistance.Value = (snappedEndTime - hitObject.StartTime) * hitObject.Velocity; + } + + public Vector2 ToRelativePosition(Vector2 screenSpacePosition) + { + return ToLocalSpace(screenSpacePosition) - new Vector2(0, DrawHeight); + } + + protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false; + + protected void MoveSelectedVertices(double distanceDelta, float xDelta) + { + // Because the vertex list may be reordered due to distance change, the state list must be reordered as well. + previousVertexStates.Clear(); + previousVertexStates.AddRange(vertexStates); + + // We will recreate the path from scratch. Note that `Clear` leaves the first vertex. + int vertexCount = VertexCount; + path.Clear(); + vertexStates.RemoveRange(1, vertexCount - 1); + + for (int i = 1; i < vertexCount; i++) + { + var state = previousVertexStates[i]; + double distance = state.VertexBeforeChange.Distance; + if (state.IsSelected) + distance += distanceDelta; + + int newIndex = path.InsertVertex(Math.Max(0, distance)); + vertexStates.Insert(newIndex, state); + } + + // First, restore positions of the non-selected vertices. + for (int i = 0; i < vertexCount; i++) + { + if (!vertexStates[i].IsSelected && !vertexStates[i].IsFixed) + path.SetVertexPosition(i, vertexStates[i].VertexBeforeChange.X); + } + + // Then, move the selected vertices. + for (int i = 0; i < vertexCount; i++) + { + if (vertexStates[i].IsSelected && !vertexStates[i].IsFixed) + path.SetVertexPosition(i, vertexStates[i].VertexBeforeChange.X + xDelta); + } + + // Finally, correct the position of fixed vertices. + correctFixedVertexPositions(); + } + + private void correctFixedVertexPositions() + { + for (int i = 0; i < VertexCount; i++) + { + if (vertexStates[i].IsFixed) + path.SetVertexPosition(i, vertexStates[i].VertexBeforeChange.X); + } + } + } +} diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs new file mode 100644 index 0000000000..a6dabca48f --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs @@ -0,0 +1,90 @@ +// 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.Linq; +using JetBrains.Annotations; +using osu.Framework.Allocation; +using osu.Framework.Input.Events; +using osu.Game.Screens.Edit; +using osuTK; +using osuTK.Input; + +namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components +{ + public class SelectionEditablePath : EditablePath + { + // To handle when the editor is scrolled while dragging. + private Vector2 dragStartPosition; + + [Resolved(CanBeNull = true)] + [CanBeNull] + private IEditorChangeHandler changeHandler { get; set; } + + public SelectionEditablePath(Func positionToDistance) + : base(positionToDistance) + { + } + + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => InternalChildren.Any(d => d.ReceivePositionalInputAt(screenSpacePos)); + + protected override bool OnMouseDown(MouseDownEvent e) + { + int index = getMouseTargetVertex(e.ScreenSpaceMouseDownPosition); + + if (index == -1) + return false; + + if (e.ControlPressed) + VertexStates[index].IsSelected = !VertexStates[index].IsSelected; + else if (!VertexStates[index].IsSelected) + selectOnly(index); + + // Don't inhabit right click, to show the context menu + return e.Button != MouseButton.Right; + } + + protected override bool OnDragStart(DragStartEvent e) + { + if (e.Button != MouseButton.Left || getMouseTargetVertex(e.ScreenSpaceMouseDownPosition) == -1) return false; + + dragStartPosition = ToRelativePosition(e.ScreenSpaceMouseDownPosition); + + for (int i = 0; i < VertexCount; i++) + VertexStates[i].VertexBeforeChange = Vertices[i]; + + changeHandler?.BeginChange(); + return true; + } + + protected override void OnDrag(DragEvent e) + { + Vector2 mousePosition = ToLocalSpace(e.ScreenSpaceMousePosition) - new Vector2(0, DrawHeight); + double distanceDelta = PositionToDistance(mousePosition.Y) - PositionToDistance(dragStartPosition.Y); + float xDelta = mousePosition.X - dragStartPosition.X; + MoveSelectedVertices(distanceDelta, xDelta); + } + + protected override void OnDragEnd(DragEndEvent e) + { + changeHandler?.EndChange(); + } + + private int getMouseTargetVertex(Vector2 screenSpacePosition) + { + for (int i = InternalChildren.Count - 1; i >= 0; i--) + { + if (i < VertexCount && InternalChildren[i].ReceivePositionalInputAt(screenSpacePosition)) + return i; + } + + return -1; + } + + private void selectOnly(int index) + { + for (int i = 0; i < VertexCount; i++) + VertexStates[i].IsSelected = i == index; + } + } +} diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/VertexPiece.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/VertexPiece.cs new file mode 100644 index 0000000000..5ef86b6074 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/VertexPiece.cs @@ -0,0 +1,31 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components +{ + public class VertexPiece : Circle + { + [Resolved] + private OsuColour osuColour { get; set; } + + public VertexPiece() + { + Anchor = Anchor.BottomLeft; + Origin = Anchor.Centre; + Size = new Vector2(15); + } + + public void UpdateFrom(VertexState state) + { + Colour = state.IsSelected ? osuColour.Yellow.Lighten(1) : osuColour.Yellow; + Alpha = state.IsFixed ? 0.5f : 1; + } + } +} diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/VertexState.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/VertexState.cs new file mode 100644 index 0000000000..55abbf7475 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/VertexState.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Catch.Objects; + +#nullable enable + +namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components +{ + public class VertexState + { + public bool IsSelected { get; set; } + + public bool IsFixed { get; set; } + + public JuiceStreamPathVertex VertexBeforeChange { get; set; } + } +} diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs index 0614c4c24d..6b4fd19a8d 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Caching; using osu.Framework.Graphics; @@ -9,6 +10,7 @@ using osu.Framework.Graphics.Primitives; using osu.Game.Rulesets.Catch.Edit.Blueprints.Components; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Objects; +using osu.Game.Screens.Edit; using osuTK; namespace osu.Game.Rulesets.Catch.Edit.Blueprints @@ -26,13 +28,24 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints private readonly Cached pathCache = new Cached(); + private readonly SelectionEditablePath editablePath; + + private int lastEditablePathId = -1; + + private int lastSliderPathVersion = -1; + + [Resolved(CanBeNull = true)] + [CanBeNull] + private EditorBeatmap editorBeatmap { get; set; } + public JuiceStreamSelectionBlueprint(JuiceStream hitObject) : base(hitObject) { InternalChildren = new Drawable[] { scrollingPath = new ScrollingPath(), - nestedOutlineContainer = new NestedOutlineContainer() + nestedOutlineContainer = new NestedOutlineContainer(), + editablePath = new SelectionEditablePath(positionToDistance) }; } @@ -49,7 +62,13 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints if (!IsSelected) return; - nestedOutlineContainer.Position = scrollingPath.Position = CatchHitObjectUtils.GetStartPosition(HitObjectContainer, HitObject); + if (editablePath.PathId != lastEditablePathId) + updateHitObjectFromPath(); + + Vector2 startPosition = CatchHitObjectUtils.GetStartPosition(HitObjectContainer, HitObject); + editablePath.Position = nestedOutlineContainer.Position = scrollingPath.Position = startPosition; + + editablePath.UpdateFrom(HitObjectContainer, HitObject); if (pathCache.IsValid) return; @@ -59,10 +78,19 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints pathCache.Validate(); } + protected override void OnSelected() + { + initializeJuiceStreamPath(); + base.OnSelected(); + } + private void onDefaultsApplied(HitObject _) { computeObjectBounds(); pathCache.Invalidate(); + + if (lastSliderPathVersion != HitObject.Path.Version.Value) + initializeJuiceStreamPath(); } private void computeObjectBounds() @@ -81,6 +109,30 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints return new RectangleF(left, top, right - left, bottom - top).Inflate(objectRadius); } + private double positionToDistance(float relativeYPosition) + { + double time = HitObjectContainer.TimeAtPosition(relativeYPosition, HitObject.StartTime); + return (time - HitObject.StartTime) * HitObject.Velocity; + } + + private void initializeJuiceStreamPath() + { + editablePath.InitializeFromHitObject(HitObject); + + // Record the current ID to update the hit object only when a change is made to the path. + lastEditablePathId = editablePath.PathId; + lastSliderPathVersion = HitObject.Path.Version.Value; + } + + private void updateHitObjectFromPath() + { + editablePath.UpdateHitObjectFromPath(HitObject); + editorBeatmap?.Update(HitObject); + + lastEditablePathId = editablePath.PathId; + lastSliderPathVersion = HitObject.Path.Version.Value; + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); From 01f5258a26a3d1c3314b852a3c114edf068e8702 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 22:33:03 +0900 Subject: [PATCH 0519/2442] Add tests of juice stream selection blueprint --- .../CatchSelectionBlueprintTestScene.cs | 50 ++++- .../TestSceneJuiceStreamSelectionBlueprint.cs | 207 ++++++++++++++++-- 2 files changed, 240 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs b/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs index dcdc32145b..a458771550 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs @@ -1,10 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Catch.Beatmaps; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Screens.Edit; using osu.Game.Tests.Visual; +using osuTK; namespace osu.Game.Rulesets.Catch.Tests.Editor { @@ -14,11 +21,52 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor protected override Container Content => contentContainer; + [Cached(typeof(EditorBeatmap))] + [Cached(typeof(IBeatSnapProvider))] + protected readonly EditorBeatmap EditorBeatmap; + private readonly CatchEditorTestSceneContainer contentContainer; protected CatchSelectionBlueprintTestScene() { - base.Content.Add(contentContainer = new CatchEditorTestSceneContainer()); + EditorBeatmap = new EditorBeatmap(new CatchBeatmap()); + EditorBeatmap.BeatmapInfo.BaseDifficulty.CircleSize = 0; + EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint + { + BeatLength = 100 + }); + + base.Content.Add(new EditorBeatmapDependencyContainer(EditorBeatmap, new BindableBeatDivisor()) + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + EditorBeatmap, + contentContainer = new CatchEditorTestSceneContainer() + }, + }); + } + + protected void AddMouseMoveStep(double time, float x) => AddStep($"move to time={time}, x={x}", () => + { + float y = HitObjectContainer.PositionAtTime(time); + Vector2 pos = HitObjectContainer.ToScreenSpace(new Vector2(x, y + HitObjectContainer.DrawHeight)); + InputManager.MoveMouseTo(pos); + }); + + private class EditorBeatmapDependencyContainer : Container + { + [Cached] + private readonly EditorClock editorClock; + + [Cached] + private readonly BindableBeatDivisor beatDivisor; + + public EditorBeatmapDependencyContainer(IBeatmap beatmap, BindableBeatDivisor beatDivisor) + { + editorClock = new EditorClock(beatmap, beatDivisor); + this.beatDivisor = beatDivisor; + } } } } diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs index 1b96175020..702ec38fb4 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs @@ -1,38 +1,213 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Testing; +using osu.Framework.Timing; +using osu.Framework.Utils; using osu.Game.Rulesets.Catch.Edit.Blueprints; +using osu.Game.Rulesets.Catch.Edit.Blueprints.Components; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osuTK; +using osuTK.Input; namespace osu.Game.Rulesets.Catch.Tests.Editor { public class TestSceneJuiceStreamSelectionBlueprint : CatchSelectionBlueprintTestScene { - public TestSceneJuiceStreamSelectionBlueprint() + private JuiceStream hitObject; + + private readonly ManualClock manualClock = new ManualClock(); + + [SetUp] + public void SetUp() => Schedule(() => { - var hitObject = new JuiceStream + EditorBeatmap.Clear(); + Content.Clear(); + + manualClock.CurrentTime = 0; + Content.Clock = new FramedClock(manualClock); + + InputManager.ReleaseButton(MouseButton.Left); + InputManager.ReleaseKey(Key.ShiftLeft); + InputManager.ReleaseKey(Key.ControlLeft); + }); + + [Test] + public void TestBasicComponentLayout() + { + double[] times = { 100, 300, 500 }; + float[] positions = { 100, 200, 100 }; + addBlueprintStep(times, positions); + + for (int i = 0; i < times.Length; i++) + addVertexCheckStep(times.Length, i, times[i], positions[i]); + + AddAssert("correct outline count", () => { - OriginalX = 100, - StartTime = 100, - Path = new SliderPath(PathType.PerfectCurve, new[] + var expected = hitObject.NestedHitObjects.Count(h => !(h is TinyDroplet)); + return this.ChildrenOfType().Count() == expected; + }); + AddAssert("correct vertex piece count", () => + this.ChildrenOfType().Count() == times.Length); + + AddAssert("first vertex is semitransparent", () => + Precision.DefinitelyBigger(1, this.ChildrenOfType().First().Alpha)); + } + + [Test] + public void TestVertexDrag() + { + double[] times = { 100, 400, 700 }; + float[] positions = { 100, 100, 100 }; + addBlueprintStep(times, positions); + + addDragStartStep(times[1], positions[1]); + + AddMouseMoveStep(500, 150); + addVertexCheckStep(3, 1, 500, 150); + + addDragEndStep(); + addDragStartStep(times[2], positions[2]); + + AddMouseMoveStep(300, 50); + addVertexCheckStep(3, 1, 300, 50); + addVertexCheckStep(3, 2, 500, 150); + + AddMouseMoveStep(-100, 100); + addVertexCheckStep(3, 1, times[0], positions[0]); + } + + [Test] + public void TestMultipleDrag() + { + double[] times = { 100, 300, 500, 700 }; + float[] positions = { 100, 100, 100, 100 }; + addBlueprintStep(times, positions); + + AddMouseMoveStep(times[1], positions[1]); + AddStep("press left", () => InputManager.PressButton(MouseButton.Left)); + AddStep("release left", () => InputManager.ReleaseButton(MouseButton.Left)); + AddStep("hold control", () => InputManager.PressKey(Key.ControlLeft)); + addDragStartStep(times[2], positions[2]); + + AddMouseMoveStep(times[2] - 50, positions[2] - 50); + addVertexCheckStep(4, 1, times[1] - 50, positions[1] - 50); + addVertexCheckStep(4, 2, times[2] - 50, positions[2] - 50); + } + + [Test] + public void TestClampedPositionIsRestored() + { + const double velocity = 0.25; + double[] times = { 100, 500, 700 }; + float[] positions = { 100, 100, 100 }; + addBlueprintStep(times, positions, velocity); + + addDragStartStep(times[1], positions[1]); + + AddMouseMoveStep(times[1], 200); + addVertexCheckStep(3, 1, times[1], 200); + addVertexCheckStep(3, 2, times[2], 150); + + AddMouseMoveStep(times[1], 100); + addVertexCheckStep(3, 1, times[1], 100); + // Stored position is restored. + addVertexCheckStep(3, 2, times[2], positions[2]); + + AddMouseMoveStep(times[1], 300); + addDragEndStep(); + addDragStartStep(times[1], 300); + + AddMouseMoveStep(times[1], 100); + // Position is different because a changed position is committed when the previous drag is ended. + addVertexCheckStep(3, 2, times[2], 250); + } + + [Test] + public void TestScrollWhileDrag() + { + double[] times = { 300, 500 }; + float[] positions = { 100, 100 }; + addBlueprintStep(times, positions); + + addDragStartStep(times[1], positions[1]); + // This mouse move is necessary to start drag and capture the input. + AddMouseMoveStep(times[1], positions[1] + 50); + + AddStep("scroll playfield", () => manualClock.CurrentTime += 200); + AddMouseMoveStep(times[1] + 200, positions[1] + 100); + addVertexCheckStep(2, 1, times[1] + 200, positions[1] + 100); + } + + [Test] + public void TestUpdateFromHitObject() + { + double[] times = { 100, 300 }; + float[] positions = { 200, 200 }; + addBlueprintStep(times, positions); + + AddStep("update hit object path", () => + { + hitObject.Path = new SliderPath(PathType.PerfectCurve, new[] { Vector2.Zero, - new Vector2(200, 100), + new Vector2(100, 100), new Vector2(0, 200), - }), - }; - var controlPoint = new ControlPointInfo(); - controlPoint.Add(0, new TimingControlPoint - { - BeatLength = 100 + }); + EditorBeatmap.Update(hitObject); }); - hitObject.ApplyDefaults(controlPoint, new BeatmapDifficulty { CircleSize = 0 }); - AddBlueprint(new JuiceStreamSelectionBlueprint(hitObject)); + AddAssert("path is updated", () => getVertices().Count > 2); } + + private void addBlueprintStep(double time, float x, SliderPath sliderPath, double velocity) => AddStep("add selection blueprint", () => + { + hitObject = new JuiceStream + { + StartTime = time, + X = x, + Path = sliderPath, + }; + EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = velocity; + EditorBeatmap.Add(hitObject); + EditorBeatmap.Update(hitObject); + Assert.That(hitObject.Velocity, Is.EqualTo(velocity)); + AddBlueprint(new JuiceStreamSelectionBlueprint(hitObject)); + }); + + private void addBlueprintStep(double[] times, float[] positions, double velocity = 0.5) + { + var path = new JuiceStreamPath(); + for (int i = 1; i < times.Length; i++) + path.Add((times[i] - times[0]) * velocity, positions[i] - positions[0]); + + var sliderPath = new SliderPath(); + path.ConvertToSliderPath(sliderPath, 0); + addBlueprintStep(times[0], positions[0], sliderPath, velocity); + } + + private IReadOnlyList getVertices() => this.ChildrenOfType().Single().Vertices; + + private void addVertexCheckStep(int count, int index, double time, float x) => AddAssert($"vertex {index} of {count} at {time}, {x}", () => + { + double expectedDistance = (time - hitObject.StartTime) * hitObject.Velocity; + float expectedX = x - hitObject.OriginalX; + var vertices = getVertices(); + return vertices.Count == count && + Precision.AlmostEquals(vertices[index].Distance, expectedDistance, 1e-3) && + Precision.AlmostEquals(vertices[index].X, expectedX); + }); + + private void addDragStartStep(double time, float x) + { + AddMouseMoveStep(time, x); + AddStep("start dragging", () => InputManager.PressButton(MouseButton.Left)); + } + + private void addDragEndStep() => AddStep("end dragging", () => InputManager.ReleaseButton(MouseButton.Left)); } } From 08f8d4e65e2144d39c1fffd4d6a56e020e5c153b Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 22:43:28 +0900 Subject: [PATCH 0520/2442] Implement vertex addition in juice stream selection blueprint --- .../TestSceneJuiceStreamSelectionBlueprint.cs | 29 +++++++++++++++ .../Blueprints/Components/EditablePath.cs | 13 +++++++ .../Components/SelectionEditablePath.cs | 7 ++++ .../JuiceStreamSelectionBlueprint.cs | 36 +++++++++++++++++++ 4 files changed, 85 insertions(+) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs index 702ec38fb4..7a8db79fa3 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs @@ -164,6 +164,24 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor AddAssert("path is updated", () => getVertices().Count > 2); } + [Test] + public void TestAddVertex() + { + double[] times = { 100, 700 }; + float[] positions = { 200, 200 }; + addBlueprintStep(times, positions, 0.2); + + addAddVertexSteps(500, 150); + addVertexCheckStep(3, 1, 500, 150); + + addAddVertexSteps(90, 220); + addVertexCheckStep(4, 1, times[0], positions[0]); + + addAddVertexSteps(750, 180); + addVertexCheckStep(5, 4, 750, 180); + AddAssert("duration is changed", () => Precision.AlmostEquals(hitObject.Duration, 800 - times[0], 1e-3)); + } + private void addBlueprintStep(double time, float x, SliderPath sliderPath, double velocity) => AddStep("add selection blueprint", () => { hitObject = new JuiceStream @@ -209,5 +227,16 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor } private void addDragEndStep() => AddStep("end dragging", () => InputManager.ReleaseButton(MouseButton.Left)); + + private void addAddVertexSteps(double time, float x) + { + AddMouseMoveStep(time, x); + AddStep("add vertex", () => + { + InputManager.PressKey(Key.ControlLeft); + InputManager.Click(MouseButton.Left); + InputManager.ReleaseKey(Key.ControlLeft); + }); + } } } diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs index a51f18ff33..4768689178 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; @@ -107,6 +108,18 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false; + protected int AddVertex(double distance, float x) + { + int index = path.InsertVertex(distance); + path.SetVertexPosition(index, x); + vertexStates.Insert(index, new VertexState()); + + correctFixedVertexPositions(); + + Debug.Assert(vertexStates.Count == VertexCount); + return index; + } + protected void MoveSelectedVertices(double distanceDelta, float xDelta) { // Because the vertex list may be reordered due to distance change, the state list must be reordered as well. diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs index a6dabca48f..7152f6108f 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs @@ -26,6 +26,13 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components { } + public void AddVertex(Vector2 relativePosition) + { + double distance = Math.Max(0, PositionToDistance(relativePosition.Y)); + int index = AddVertex(distance, relativePosition.X); + selectOnly(index); + } + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => InternalChildren.Any(d => d.ReceivePositionalInputAt(screenSpacePos)); protected override bool OnMouseDown(MouseDownEvent e) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs index 6b4fd19a8d..defb5eae8b 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs @@ -1,17 +1,22 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Caching; using osu.Framework.Graphics; using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; +using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Catch.Edit.Blueprints.Components; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Objects; using osu.Game.Screens.Edit; using osuTK; +using osuTK.Input; namespace osu.Game.Rulesets.Catch.Edit.Blueprints { @@ -19,6 +24,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints { public override Quad SelectionQuad => HitObjectContainer.ToScreenSpace(getBoundingBox().Offset(new Vector2(0, HitObjectContainer.DrawHeight))); + public override MenuItem[] ContextMenuItems => getContextMenuItems().ToArray(); + private float minNestedX; private float maxNestedX; @@ -34,6 +41,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints private int lastSliderPathVersion = -1; + private Vector2 rightMouseDownPosition; + [Resolved(CanBeNull = true)] [CanBeNull] private EditorBeatmap editorBeatmap { get; set; } @@ -84,6 +93,25 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints base.OnSelected(); } + protected override bool OnMouseDown(MouseDownEvent e) + { + if (!IsSelected) return base.OnMouseDown(e); + + switch (e.Button) + { + case MouseButton.Left when e.ControlPressed: + editablePath.AddVertex(editablePath.ToRelativePosition(e.ScreenSpaceMouseDownPosition)); + return true; + + case MouseButton.Right: + // Record the mouse position to be used in the "add vertex" action. + rightMouseDownPosition = editablePath.ToRelativePosition(e.ScreenSpaceMouseDownPosition); + break; + } + + return base.OnMouseDown(e); + } + private void onDefaultsApplied(HitObject _) { computeObjectBounds(); @@ -133,6 +161,14 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints lastSliderPathVersion = HitObject.Path.Version.Value; } + private IEnumerable getContextMenuItems() + { + yield return new OsuMenuItem("Add vertex", MenuItemType.Standard, () => + { + editablePath.AddVertex(rightMouseDownPosition); + }); + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); From 85864587040d8ebb3955d26405b662a059469f08 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 22:46:32 +0900 Subject: [PATCH 0521/2442] Implement vertex deletion in juice stream selection blueprint --- .../TestSceneJuiceStreamSelectionBlueprint.cs | 29 +++++++++++++++++ .../Blueprints/Components/EditablePath.cs | 18 +++++++++++ .../Components/SelectionEditablePath.cs | 31 ++++++++++++++++++- 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs index 7a8db79fa3..2c1b95a726 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs @@ -182,6 +182,24 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor AddAssert("duration is changed", () => Precision.AlmostEquals(hitObject.Duration, 800 - times[0], 1e-3)); } + [Test] + public void TestDeleteVertex() + { + double[] times = { 100, 300, 500 }; + float[] positions = { 100, 200, 150 }; + addBlueprintStep(times, positions); + + addDeleteVertexSteps(times[1], positions[1]); + addVertexCheckStep(2, 1, times[2], positions[2]); + + // The first vertex cannot be deleted. + addDeleteVertexSteps(times[0], positions[0]); + addVertexCheckStep(2, 0, times[0], positions[0]); + + addDeleteVertexSteps(times[2], positions[2]); + addVertexCheckStep(1, 0, times[0], positions[0]); + } + private void addBlueprintStep(double time, float x, SliderPath sliderPath, double velocity) => AddStep("add selection blueprint", () => { hitObject = new JuiceStream @@ -238,5 +256,16 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor InputManager.ReleaseKey(Key.ControlLeft); }); } + + private void addDeleteVertexSteps(double time, float x) + { + AddMouseMoveStep(time, x); + AddStep("delete vertex", () => + { + InputManager.PressKey(Key.ShiftLeft); + InputManager.Click(MouseButton.Left); + InputManager.ReleaseKey(Key.ShiftLeft); + }); + } } } diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs index 4768689178..8aaeef045f 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs @@ -120,6 +120,24 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components return index; } + protected bool RemoveVertex(int index) + { + if (index < 0 || index >= path.Vertices.Count) + return false; + + if (vertexStates[index].IsFixed) + return false; + + path.RemoveVertices((_, i) => i == index); + + vertexStates.RemoveAt(index); + if (vertexStates.Count == 0) + vertexStates.Add(new VertexState()); + + Debug.Assert(vertexStates.Count == VertexCount); + return true; + } + protected void MoveSelectedVertices(double distanceDelta, float xDelta) { // Because the vertex list may be reordered due to distance change, the state list must be reordered as well. diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs index 7152f6108f..3acb7f43a6 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs @@ -2,18 +2,24 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; +using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Edit; using osuTK; using osuTK.Input; namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components { - public class SelectionEditablePath : EditablePath + public class SelectionEditablePath : EditablePath, IHasContextMenu { + public MenuItem[] ContextMenuItems => getContextMenuItems().ToArray(); + // To handle when the editor is scrolled while dragging. private Vector2 dragStartPosition; @@ -42,6 +48,12 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components if (index == -1) return false; + if (e.Button == MouseButton.Left && e.ShiftPressed) + { + RemoveVertex(index); + return true; + } + if (e.ControlPressed) VertexStates[index].IsSelected = !VertexStates[index].IsSelected; else if (!VertexStates[index].IsSelected) @@ -88,10 +100,27 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components return -1; } + private IEnumerable getContextMenuItems() + { + int selectedCount = VertexStates.Count(state => state.IsSelected); + + if (selectedCount != 0) + yield return new OsuMenuItem($"Delete selected {(selectedCount == 1 ? "vertex" : $"{selectedCount} vertices")}", MenuItemType.Destructive, deleteSelectedVertices); + } + private void selectOnly(int index) { for (int i = 0; i < VertexCount; i++) VertexStates[i].IsSelected = i == index; } + + private void deleteSelectedVertices() + { + for (int i = VertexCount - 1; i >= 0; i--) + { + if (VertexStates[i].IsSelected) + RemoveVertex(i); + } + } } } From 9db5847344bf28319547b48ebc602bbb26cc89bf Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 19 Jul 2021 22:49:03 +0900 Subject: [PATCH 0522/2442] Add test that a slider path is resampled when the path is edited --- .../TestSceneJuiceStreamSelectionBlueprint.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs index 2c1b95a726..f5ef5c5e18 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs @@ -200,6 +200,21 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor addVertexCheckStep(1, 0, times[0], positions[0]); } + [Test] + public void TestVertexResampling() + { + addBlueprintStep(100, 100, new SliderPath(PathType.PerfectCurve, new[] + { + Vector2.Zero, + new Vector2(100, 100), + new Vector2(50, 200), + }), 0.5); + AddAssert("1 vertex per 1 nested HO", () => getVertices().Count == hitObject.NestedHitObjects.Count); + AddAssert("slider path not yet changed", () => hitObject.Path.ControlPoints[0].Type.Value == PathType.PerfectCurve); + addAddVertexSteps(150, 150); + AddAssert("slider path change to linear", () => hitObject.Path.ControlPoints[0].Type.Value == PathType.Linear); + } + private void addBlueprintStep(double time, float x, SliderPath sliderPath, double velocity) => AddStep("add selection blueprint", () => { hitObject = new JuiceStream From 9ea1f5900a78ca02bdb11efa37fd3a87b6f71889 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Jul 2021 01:05:36 +0900 Subject: [PATCH 0523/2442] Don't consider version suffixes when checking for updates This is just to make life easier for me with deploys for now. --- osu.Game/Updater/SimpleUpdateManager.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Updater/SimpleUpdateManager.cs b/osu.Game/Updater/SimpleUpdateManager.cs index 50572a7867..e0409e34df 100644 --- a/osu.Game/Updater/SimpleUpdateManager.cs +++ b/osu.Game/Updater/SimpleUpdateManager.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Newtonsoft.Json; using osu.Framework; @@ -41,11 +42,16 @@ namespace osu.Game.Updater var latest = releases.ResponseObject; - if (latest.TagName != version) + // avoid any discrepancies due to build suffixes for now. + // eventually we will want to support release streams and consider these. + version = version.Split('-').First(); + var latestTagName = latest.TagName.Split('-').First(); + + if (latestTagName != version) { Notifications.Post(new SimpleNotification { - Text = $"A newer release of osu! has been found ({version} → {latest.TagName}).\n\n" + Text = $"A newer release of osu! has been found ({version} → {latestTagName}).\n\n" + "Click here to download the new version, which can be installed over the top of your existing installation", Icon = FontAwesome.Solid.Upload, Activated = () => From ccc782ea7eb718aa46248244590760aecd29954c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 20 Jul 2021 02:24:02 +0300 Subject: [PATCH 0524/2442] Add `description` to key names of description strings --- .../Localisation/NamedOverlayComponentStrings.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Localisation/NamedOverlayComponentStrings.cs b/osu.Game/Localisation/NamedOverlayComponentStrings.cs index f40b97e605..b09e128a2b 100644 --- a/osu.Game/Localisation/NamedOverlayComponentStrings.cs +++ b/osu.Game/Localisation/NamedOverlayComponentStrings.cs @@ -16,7 +16,7 @@ namespace osu.Game.Localisation /// /// "browse for new beatmaps" /// - public static LocalisableString BeatmapListingDescription => new TranslatableString(getKey(@"beatmap_listing"), @"browse for new beatmaps"); + public static LocalisableString BeatmapListingDescription => new TranslatableString(getKey(@"beatmap_listing_description"), @"browse for new beatmaps"); /// public static LocalisableString BeatmapSetTitle => PageTitleStrings.MainBeatmapsetsControllerShow; @@ -27,7 +27,7 @@ namespace osu.Game.Localisation /// /// "track recent dev updates in the osu! ecosystem" /// - public static LocalisableString ChangelogDescription => new TranslatableString(getKey(@"changelog"), @"track recent dev updates in the osu! ecosystem"); + public static LocalisableString ChangelogDescription => new TranslatableString(getKey(@"changelog_description"), @"track recent dev updates in the osu! ecosystem"); /// public static LocalisableString DashboardTitle => PageTitleStrings.MainHomeControllerIndex; @@ -35,7 +35,7 @@ namespace osu.Game.Localisation /// /// "view your friends and other information" /// - public static LocalisableString DashboardDescription => new TranslatableString(getKey(@"dashboard"), @"view your friends and other information"); + public static LocalisableString DashboardDescription => new TranslatableString(getKey(@"dashboard_description"), @"view your friends and other information"); /// public static LocalisableString RankingsTitle => PageTitleStrings.MainRankingControllerDefault; @@ -43,7 +43,7 @@ namespace osu.Game.Localisation /// /// "find out who's the best right now" /// - public static LocalisableString RankingsDescription => new TranslatableString(getKey(@"rankings"), @"find out who's the best right now"); + public static LocalisableString RankingsDescription => new TranslatableString(getKey(@"rankings_description"), @"find out who's the best right now"); /// public static LocalisableString NewsTitle => PageTitleStrings.MainNewsControllerDefault; @@ -51,7 +51,7 @@ namespace osu.Game.Localisation /// /// "get up-to-date on community happenings" /// - public static LocalisableString NewsDescription => new TranslatableString(getKey(@"news"), @"get up-to-date on community happenings"); + public static LocalisableString NewsDescription => new TranslatableString(getKey(@"news_description"), @"get up-to-date on community happenings"); /// public static LocalisableString WikiTitle => PageTitleStrings.MainWikiControllerDefault; @@ -59,7 +59,7 @@ namespace osu.Game.Localisation /// /// "knowledge base" /// - public static LocalisableString WikiDescription => new TranslatableString(getKey(@"wiki"), @"knowledge base"); + public static LocalisableString WikiDescription => new TranslatableString(getKey(@"wiki_description"), @"knowledge base"); private static string getKey(string key) => $"{prefix}:{key}"; } From 944bf2c4f946eeed89ea7e1b29cf654eab1dbbc3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 20 Jul 2021 02:25:15 +0300 Subject: [PATCH 0525/2442] Fix incorrect property xmldoc inherited --- osu.Game/Localisation/NamedOverlayComponentStrings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Localisation/NamedOverlayComponentStrings.cs b/osu.Game/Localisation/NamedOverlayComponentStrings.cs index b09e128a2b..f160ee57c7 100644 --- a/osu.Game/Localisation/NamedOverlayComponentStrings.cs +++ b/osu.Game/Localisation/NamedOverlayComponentStrings.cs @@ -53,7 +53,7 @@ namespace osu.Game.Localisation /// public static LocalisableString NewsDescription => new TranslatableString(getKey(@"news_description"), @"get up-to-date on community happenings"); - /// + /// public static LocalisableString WikiTitle => PageTitleStrings.MainWikiControllerDefault; /// From 456f4e6f1fce118405e0947f73705e90d1c30306 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 20 Jul 2021 02:29:35 +0300 Subject: [PATCH 0526/2442] Move `RankingsScope` to own file and fix mapper strings --- .../Rankings/RankingsOverlayHeader.cs | 36 ---------------- osu.Game/Overlays/Rankings/RankingsScope.cs | 42 +++++++++++++++++++ 2 files changed, 42 insertions(+), 36 deletions(-) create mode 100644 osu.Game/Overlays/Rankings/RankingsScope.cs diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs index b6c16d398d..c0f77049de 100644 --- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs @@ -1,13 +1,10 @@ // 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 osu.Framework.Graphics; using osu.Framework.Bindables; -using osu.Framework.Localisation; using osu.Game.Localisation; using osu.Game.Rulesets; -using osu.Game.Resources.Localisation.Web; using osu.Game.Users; namespace osu.Game.Overlays.Rankings @@ -39,37 +36,4 @@ namespace osu.Game.Overlays.Rankings } } } - - [LocalisableEnum(typeof(RankingsScopeEnumLocalisationMapper))] - public enum RankingsScope - { - Performance, - Spotlights, - Score, - Country - } - - public class RankingsScopeEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(RankingsScope value) - { - switch (value) - { - case RankingsScope.Performance: - return LayoutStrings.MenuRankingsIndex; - - case RankingsScope.Spotlights: - return LayoutStrings.MenuRankingsCharts; - - case RankingsScope.Score: - return LayoutStrings.MenuRankingsScore; - - case RankingsScope.Country: - return LayoutStrings.MenuRankingsCountry; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/Rankings/RankingsScope.cs b/osu.Game/Overlays/Rankings/RankingsScope.cs new file mode 100644 index 0000000000..684408d3a2 --- /dev/null +++ b/osu.Game/Overlays/Rankings/RankingsScope.cs @@ -0,0 +1,42 @@ +// 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 osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; + +namespace osu.Game.Overlays.Rankings +{ + [LocalisableEnum(typeof(RankingsScopeEnumLocalisationMapper))] + public enum RankingsScope + { + Performance, + Spotlights, + Score, + Country + } + + public class RankingsScopeEnumLocalisationMapper : EnumLocalisationMapper + { + public override LocalisableString Map(RankingsScope value) + { + switch (value) + { + case RankingsScope.Performance: + return RankingsStrings.TypePerformance; + + case RankingsScope.Spotlights: + return RankingsStrings.TypeCharts; + + case RankingsScope.Score: + return RankingsStrings.TypeScore; + + case RankingsScope.Country: + return RankingsStrings.TypeCountry; + + default: + throw new ArgumentOutOfRangeException(nameof(value), value, null); + } + } + } +} From 51b056bf664332c963a05494071dab530be774ce Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 20 Jul 2021 02:36:04 +0300 Subject: [PATCH 0527/2442] Fix `WikiHeader` setting `string.Empty` rather than `null` on breadcrumb That's supposed to be fixed already in the base PR, but somehow it continued to exist here. --- osu.Game/Overlays/Wiki/WikiHeader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index 29ab385cd8..b57bffb3d8 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -38,7 +38,7 @@ namespace osu.Game.Overlays.Wiki return; TabControl.Clear(); - Current.Value = string.Empty; + Current.Value = null; TabControl.AddItem(IndexPageString); From 6a8c16d3ef9e215a2021b801f4dd4471dbb031f6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 20 Jul 2021 03:29:32 +0300 Subject: [PATCH 0528/2442] Fix news/wiki tests checking against incorrect constant string --- osu.Game.Tests/Visual/Online/TestSceneNewsHeader.cs | 4 ++-- osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsHeader.cs index 78288bf6e4..994c4fce53 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsHeader.cs @@ -29,7 +29,7 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestControl() { - AddAssert("Front page selected", () => header.Current.Value == "frontpage"); + AddAssert("Front page selected", () => header.Current.Value == NewsHeader.FrontPageString); AddAssert("1 tab total", () => header.TabCount == 1); AddStep("Set article 1", () => header.SetArticle("1")); @@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual.Online AddAssert("2 tabs total", () => header.TabCount == 2); AddStep("Set front page", () => header.SetFrontPage()); - AddAssert("Front page selected", () => header.Current.Value == "frontpage"); + AddAssert("Front page selected", () => header.Current.Value == NewsHeader.FrontPageString); AddAssert("1 tab total", () => header.TabCount == 1); } diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs index e7e6030c66..08e61d19f4 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs @@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestWikiHeader() { - AddAssert("Current is index", () => checkCurrent("index")); + AddAssert("Current is index", () => checkCurrent(WikiHeader.IndexPageString)); AddStep("Change wiki page data", () => wikiPageData.Value = new APIWikiPage { @@ -54,8 +54,8 @@ namespace osu.Game.Tests.Visual.Online AddAssert("Current is welcome", () => checkCurrent("Welcome")); AddAssert("Check breadcrumb", checkBreadcrumb); - AddStep("Change current to index", () => header.Current.Value = "index"); - AddAssert("Current is index", () => checkCurrent("index")); + AddStep("Change current to index", () => header.Current.Value = WikiHeader.IndexPageString); + AddAssert("Current is index", () => checkCurrent(WikiHeader.IndexPageString)); AddStep("Change wiki page data", () => wikiPageData.Value = new APIWikiPage { @@ -71,7 +71,7 @@ namespace osu.Game.Tests.Visual.Online AddAssert("Check breadcrumb", checkBreadcrumb); } - private bool checkCurrent(string expectedCurrent) => header.Current.Value == expectedCurrent; + private bool checkCurrent(LocalisableString expectedCurrent) => header.Current.Value == expectedCurrent; private bool checkBreadcrumb() { From a387d8df740d28acb4d5f285913537711e2db912 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Tue, 20 Jul 2021 10:30:40 +0800 Subject: [PATCH 0529/2442] Use `BeatSyncClock` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 918b9b1c94..f682860498 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -374,8 +374,7 @@ namespace osu.Game.Rulesets.Osu.Mods int timeSignature = (int)timingPoint.TimeSignature; // play metronome from one measure before the first object. - // TODO: Use BeatSyncClock from https://github.com/ppy/osu/pull/13894. - if (Clock.CurrentTime < firstHitTime - timingPoint.BeatLength * timeSignature) + if (BeatSyncClock.CurrentTime < firstHitTime - timingPoint.BeatLength * timeSignature) return; sample.Frequency.Value = beatIndex % timeSignature == 0 ? 1 : 0.5f; From 1c6a13fca79aacd28a269effe010b4332a064264 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Tue, 20 Jul 2021 10:31:19 +0800 Subject: [PATCH 0530/2442] Disallow mistimed event firing --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index f682860498..e21d1da009 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -353,6 +353,7 @@ namespace osu.Game.Rulesets.Osu.Mods public TargetBeatContainer(double firstHitTime) { this.firstHitTime = firstHitTime; + AllowMistimedEventFiring = false; Divisor = 1; } From 662822a40c295a627d428423156ef3b17bbcc857 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Jul 2021 14:46:22 +0900 Subject: [PATCH 0531/2442] Avoid showing time of play on results screen when autoplay Closes https://github.com/ppy/osu/issues/13940. --- .../TestScenePlaylistsResultsScreen.cs | 9 +- .../TestSceneExpandedPanelMiddleContent.cs | 27 +- .../BeatmapSet/Scores/TopScoreUserSection.cs | 5 +- .../Sections/Ranks/DrawableProfileScore.cs | 5 +- osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs | 2 +- osu.Game/Scoring/ScoreInfo.cs | 4 +- .../Expanded/ExpandedPanelMiddleContent.cs | 273 +++++++++--------- 7 files changed, 183 insertions(+), 142 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs index 61d49e4018..95dab61d7b 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Net; using JetBrains.Annotations; @@ -226,11 +227,13 @@ namespace osu.Game.Tests.Visual.Playlists private MultiplayerScore createUserResponse([NotNull] ScoreInfo userScore) { + Debug.Assert(userScore.Date != null); + var multiplayerUserScore = new MultiplayerScore { ID = (int)(userScore.OnlineScoreID ?? currentScoreId++), Accuracy = userScore.Accuracy, - EndedAt = userScore.Date, + EndedAt = userScore.Date.Value, Passed = userScore.Passed, Rank = userScore.Rank, Position = 200, @@ -251,7 +254,7 @@ namespace osu.Game.Tests.Visual.Playlists { ID = currentScoreId++, Accuracy = userScore.Accuracy, - EndedAt = userScore.Date, + EndedAt = userScore.Date.Value, Passed = true, Rank = userScore.Rank, MaxCombo = userScore.MaxCombo, @@ -269,7 +272,7 @@ namespace osu.Game.Tests.Visual.Playlists { ID = currentScoreId++, Accuracy = userScore.Accuracy, - EndedAt = userScore.Date, + EndedAt = userScore.Date.Value, Passed = true, Rank = userScore.Rank, MaxCombo = userScore.MaxCombo, diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs index 591095252f..46450cebc9 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs @@ -12,6 +12,7 @@ using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Scoring; using osu.Game.Screens.Ranking; @@ -50,9 +51,33 @@ namespace osu.Game.Tests.Visual.Ranking AddAssert("mapped by text not present", () => this.ChildrenOfType().All(spriteText => !containsAny(spriteText.Text.ToString(), "mapped", "by"))); + + AddAssert("play time displayed", () => this.ChildrenOfType().Any()); } - private void showPanel(ScoreInfo score) => Child = new ExpandedPanelMiddleContentContainer(score); + [Test] + public void TestWithNullDate() + { + AddStep("show autoplay score", () => + { + var ruleset = new OsuRuleset(); + + var mods = new Mod[] { ruleset.GetAutoplayMod() }; + var beatmap = createTestBeatmap(null); + + showPanel(new TestScoreInfo(ruleset.RulesetInfo) + { + Mods = mods, + Beatmap = beatmap, + Date = null, + }); + }); + + AddAssert("play time not displayed", () => !this.ChildrenOfType().Any()); + } + + private void showPanel(ScoreInfo score) => + Child = new ExpandedPanelMiddleContentContainer(score); private BeatmapInfo createTestBeatmap(User author) { diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs index 736366fb5c..324d82b9c2 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -136,9 +137,11 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { set { + Debug.Assert(value.Date != null); + avatar.User = value.User; flag.Country = value.User.Country; - achievedOn.Date = value.Date; + achievedOn.Date = value.Date.Value; usernameText.Clear(); usernameText.AddUserLink(value.User); diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs index 713303285a..ec02273a99 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Diagnostics; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; @@ -44,6 +45,8 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks [BackgroundDependencyLoader] private void load() { + Debug.Assert(Score.Date != null); + AddInternal(new ProfileItemContainer { Children = new Drawable[] @@ -92,7 +95,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular), Colour = colours.Yellow }, - new DrawableDate(Score.Date, 12) + new DrawableDate(Score.Date.Value, 12) { Colour = colourProvider.Foreground1 } diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs index 288552879c..0a405a9933 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs @@ -59,7 +59,7 @@ namespace osu.Game.Scoring.Legacy sw.Write((int)score.ScoreInfo.Ruleset.CreateInstance().ConvertToLegacyMods(score.ScoreInfo.Mods)); sw.Write(getHpGraphFormatted()); - sw.Write(score.ScoreInfo.Date.DateTime); + sw.Write((score.ScoreInfo.Date ?? DateTimeOffset.Now).DateTime); sw.WriteByteArray(createReplayData()); sw.Write((long)0); writeModSpecificData(score.ScoreInfo, sw); diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index a0c4d5a026..d2df0058d8 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; +using JetBrains.Annotations; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using osu.Game.Beatmaps; @@ -155,7 +156,8 @@ namespace osu.Game.Scoring public long? OnlineScoreID { get; set; } [JsonIgnore] - public DateTimeOffset Date { get; set; } + [CanBeNull] // may be null in cases of autoplay. + public DateTimeOffset? Date { get; set; } [JsonProperty("statistics")] public Dictionary Statistics = new Dictionary(); diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index 4d3f7a4184..dd053c11f9 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -1,6 +1,7 @@ // 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 osu.Framework.Allocation; @@ -79,162 +80,155 @@ namespace osu.Game.Screens.Ranking.Expanded var starDifficulty = beatmapDifficultyCache.GetDifficultyAsync(beatmap, score.Ruleset, score.Mods).Result; - InternalChildren = new Drawable[] + AddInternal(new FillFlowContainer { - new FillFlowContainer + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(20), + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(20), - Children = new Drawable[] + new FillFlowContainer { - new FillFlowContainer + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new Drawable[] { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new Drawable[] + new OsuSpriteText { - new OsuSpriteText + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = new RomanisableString(metadata.TitleUnicode, metadata.Title), + Font = OsuFont.Torus.With(size: 20, weight: FontWeight.SemiBold), + MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2, + Truncate = true, + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = new RomanisableString(metadata.ArtistUnicode, metadata.Artist), + Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold), + MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2, + Truncate = true, + }, + new Container + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Margin = new MarginPadding { Top = 40 }, + RelativeSizeAxes = Axes.X, + Height = 230, + Child = new AccuracyCircle(score, withFlair) { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = new RomanisableString(metadata.TitleUnicode, metadata.Title), - Font = OsuFont.Torus.With(size: 20, weight: FontWeight.SemiBold), - MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2, - Truncate = true, - }, - new OsuSpriteText + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + } + }, + scoreCounter = new TotalScoreCounter + { + Margin = new MarginPadding { Top = 0, Bottom = 5 }, + Current = { Value = 0 }, + Alpha = 0, + AlwaysPresent = true + }, + starAndModDisplay = new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(5, 0), + Children = new Drawable[] { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = new RomanisableString(metadata.ArtistUnicode, metadata.Artist), - Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold), - MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2, - Truncate = true, - }, - new Container - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Margin = new MarginPadding { Top = 40 }, - RelativeSizeAxes = Axes.X, - Height = 230, - Child = new AccuracyCircle(score, withFlair) + new StarRatingDisplay(starDifficulty) { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit, - } - }, - scoreCounter = new TotalScoreCounter + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft + }, + } + }, + new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] { - Margin = new MarginPadding { Top = 0, Bottom = 5 }, - Current = { Value = 0 }, - Alpha = 0, - AlwaysPresent = true - }, - starAndModDisplay = new FillFlowContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Both, - Spacing = new Vector2(5, 0), - Children = new Drawable[] + new OsuSpriteText { - new StarRatingDisplay(starDifficulty) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft - }, - } - }, - new FillFlowContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Direction = FillDirection.Vertical, - AutoSizeAxes = Axes.Both, - Children = new Drawable[] + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = beatmap.Version, + Font = OsuFont.Torus.With(size: 16, weight: FontWeight.SemiBold), + }, + new OsuTextFlowContainer(s => s.Font = OsuFont.Torus.With(size: 12)) { - new OsuSpriteText + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + }.With(t => + { + if (!string.IsNullOrEmpty(creator)) { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = beatmap.Version, - Font = OsuFont.Torus.With(size: 16, weight: FontWeight.SemiBold), - }, - new OsuTextFlowContainer(s => s.Font = OsuFont.Torus.With(size: 12)) - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - }.With(t => - { - if (!string.IsNullOrEmpty(creator)) - { - t.AddText("mapped by "); - t.AddText(creator, s => s.Font = s.Font.With(weight: FontWeight.SemiBold)); - } - }) - } - }, - } - }, - new FillFlowContainer + t.AddText("mapped by "); + t.AddText(creator, s => s.Font = s.Font.With(weight: FontWeight.SemiBold)); + } + }) + } + }, + } + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5), + Children = new Drawable[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5), - Children = new Drawable[] + new GridContainer { - new GridContainer + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Content = new[] { topStatistics.Cast().ToArray() }, + RowDimensions = new[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Content = new[] { topStatistics.Cast().ToArray() }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - } - }, - new GridContainer + new Dimension(GridSizeMode.AutoSize), + } + }, + new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Content = new[] { bottomStatistics.Where(s => s.Result <= HitResult.Perfect).ToArray() }, + RowDimensions = new[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Content = new[] { bottomStatistics.Where(s => s.Result <= HitResult.Perfect).ToArray() }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - } - }, - new GridContainer + new Dimension(GridSizeMode.AutoSize), + } + }, + new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Content = new[] { bottomStatistics.Where(s => s.Result > HitResult.Perfect).ToArray() }, + RowDimensions = new[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Content = new[] { bottomStatistics.Where(s => s.Result > HitResult.Perfect).ToArray() }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - } + new Dimension(GridSizeMode.AutoSize), } } } } - }, - new OsuSpriteText - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), - Text = $"Played on {score.Date.ToLocalTime():d MMMM yyyy HH:mm}" } - }; + }); + + if (score.Date != null) + AddInternal(new PlayedOnText(score.Date.Value)); if (score.Mods.Any()) { @@ -276,5 +270,16 @@ namespace osu.Game.Screens.Ranking.Expanded FinishTransforms(true); }); } + + public class PlayedOnText : OsuSpriteText + { + public PlayedOnText(DateTimeOffset time) + { + Anchor = Anchor.BottomCentre; + Origin = Anchor.BottomCentre; + Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold); + Text = $"Played on {time.ToLocalTime():d MMMM yyyy HH:mm}"; + } + } } } From fe414b942f69230c473398b8ee2e199c06449a84 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Jul 2021 15:44:32 +0900 Subject: [PATCH 0532/2442] Ensure online play subscreen is loaded before forwarding `OnExiting` Closes https://github.com/ppy/osu-framework/issues/4619 (actually not a framework issue; the framework correctly guards against this scenario, see https://github.com/ppy/osu-framework/blob/4e29504384bb78bbccc0d492de58124f17270eb2/osu.Framework/Screens/ScreenStack.cs#L277). Added the assertions to be very explicit about the nested stack's state at this point. Both of those events can only be triggered if the stack has a loaded screen (as far as I can tell), making this check unnecessary in those cases. --- osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index ceee002c6e..3aef05c26e 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; @@ -222,7 +223,9 @@ namespace osu.Game.Screens.OnlinePlay this.FadeIn(250); this.ScaleTo(1, 250, Easing.OutSine); - screenStack.CurrentScreen?.OnResuming(last); + Debug.Assert(screenStack.CurrentScreen != null); + screenStack.CurrentScreen.OnResuming(last); + base.OnResuming(last); UpdatePollingRate(isIdle.Value); @@ -233,14 +236,16 @@ namespace osu.Game.Screens.OnlinePlay this.ScaleTo(1.1f, 250, Easing.InSine); this.FadeOut(250); - screenStack.CurrentScreen?.OnSuspending(next); + Debug.Assert(screenStack.CurrentScreen != null); + screenStack.CurrentScreen.OnSuspending(next); UpdatePollingRate(isIdle.Value); } public override bool OnExiting(IScreen next) { - if (screenStack.CurrentScreen?.OnExiting(next) == true) + var subScreen = screenStack.CurrentScreen as Drawable; + if (subScreen?.IsLoaded == true && screenStack.CurrentScreen.OnExiting(next)) return true; RoomManager.PartRoom(); From a1001542f34f20727df86b244184afd68141ce49 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 20 Jul 2021 10:28:24 +0300 Subject: [PATCH 0533/2442] Update outdated test cases --- .../TestSceneLegacyBeatmapSkin.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs index 13df5070d0..a009c560cb 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs @@ -86,9 +86,8 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(false, false)] public void TestLegacyOffsetWithBeatmapColours(bool userHasCustomColours, bool useBeatmapSkin) { - TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true, getHitCirclesWithLegacyOffsets()); - base.TestBeatmapComboColours(userHasCustomColours, useBeatmapSkin); - + PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true, getHitCirclesWithLegacyOffsets())); + ConfigureTest(useBeatmapSkin, true, userHasCustomColours); assertCorrectObjectComboColours("is beatmap skin colours with legacy offsets applied", TestBeatmapSkin.Colours, (i, obj) => i + 1 + obj.LegacyBeatmapComboOffset); @@ -98,9 +97,8 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(false)] public void TestLegacyOffsetWithIgnoredBeatmapColours(bool useBeatmapSkin) { - TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true, getHitCirclesWithLegacyOffsets()); - base.TestBeatmapComboColoursOverride(useBeatmapSkin); - + PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true, getHitCirclesWithLegacyOffsets())); + ConfigureTest(useBeatmapSkin, false, true); assertCorrectObjectComboColours("is user skin colours without legacy offsets applied", TestSkin.Colours, (i, _) => i + 1); From 4113eae6ade3cc1d55819d0d19cde3fda4458877 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Jul 2021 16:23:48 +0900 Subject: [PATCH 0534/2442] Add test coverage of fail scenario --- .../Multiplayer/TestSceneMultiplayer.cs | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 92f9c5733f..f39455dc85 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -104,6 +104,36 @@ namespace osu.Game.Tests.Visual.Multiplayer }); } + [Test] + public void TestExitMidJoin() + { + Room room = null; + + AddStep("create room", () => + { + room = new Room + { + Name = { Value = "Test Room" }, + Playlist = + { + new PlaylistItem + { + Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + } + } + }; + }); + + AddStep("refresh rooms", () => multiplayerScreen.RoomManager.Filter.Value = new FilterCriteria()); + AddStep("select room", () => InputManager.Key(Key.Down)); + AddStep("join room and immediately exit", () => + { + multiplayerScreen.ChildrenOfType().Single().Open(room); + Schedule(() => Stack.CurrentScreen.Exit()); + }); + } + [Test] public void TestJoinRoomWithoutPassword() { @@ -179,9 +209,6 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null); AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password"); AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().Click()); - - AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); - AddUntilStep("wait for join", () => client.Room != null); } [Test] From d87ea741edb57bae885f714ec3e81158ce1e4999 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Jul 2021 16:38:39 +0900 Subject: [PATCH 0535/2442] Fix `OnlinePlayScreen` sub-screens not loaded asynchronously --- .../Screens/OnlinePlay/OnlinePlayScreen.cs | 67 +++++++++++-------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index ceee002c6e..a4bcad5776 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -30,17 +30,19 @@ namespace osu.Game.Screens.OnlinePlay [Cached] public abstract class OnlinePlayScreen : OsuScreen, IHasSubScreenStack { - public override bool CursorVisible => (screenStack.CurrentScreen as IOnlinePlaySubScreen)?.CursorVisible ?? true; + public override bool CursorVisible => (screenStack?.CurrentScreen as IOnlinePlaySubScreen)?.CursorVisible ?? true; // this is required due to PlayerLoader eventually being pushed to the main stack // while leases may be taken out by a subscreen. public override bool DisallowExternalBeatmapRulesetChanges => true; - private readonly MultiplayerWaveContainer waves; + private MultiplayerWaveContainer waves; - private readonly OsuButton createButton; - private readonly LoungeSubScreen loungeSubScreen; - private readonly ScreenStack screenStack; + private OsuButton createButton; + + private ScreenStack screenStack; + + private LoungeSubScreen loungeSubScreen; private readonly IBindable isIdle = new BindableBool(); @@ -54,7 +56,7 @@ namespace osu.Game.Screens.OnlinePlay private readonly Bindable currentFilter = new Bindable(new FilterCriteria()); [Cached] - private OngoingOperationTracker ongoingOperationTracker { get; set; } + private readonly OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker(); [Resolved(CanBeNull = true)] private MusicController music { get; set; } @@ -65,11 +67,14 @@ namespace osu.Game.Screens.OnlinePlay [Resolved] protected IAPIProvider API { get; private set; } + [Resolved(CanBeNull = true)] + private IdleTracker idleTracker { get; set; } + [Resolved(CanBeNull = true)] private OsuLogo logo { get; set; } - private readonly Drawable header; - private readonly Drawable headerBackground; + private Drawable header; + private Drawable headerBackground; protected OnlinePlayScreen() { @@ -78,6 +83,14 @@ namespace osu.Game.Screens.OnlinePlay RelativeSizeAxes = Axes.Both; Padding = new MarginPadding { Horizontal = -HORIZONTAL_OVERFLOW_PADDING }; + RoomManager = CreateRoomManager(); + } + + private readonly IBindable apiState = new Bindable(); + + [BackgroundDependencyLoader] + private void load() + { var backgroundColour = Color4Extensions.FromHex(@"3e3a44"); InternalChild = waves = new MultiplayerWaveContainer @@ -144,27 +157,14 @@ namespace osu.Game.Screens.OnlinePlay }; button.Action = () => OpenNewRoom(); }), - RoomManager = CreateRoomManager(), - ongoingOperationTracker = new OngoingOperationTracker() + RoomManager, + ongoingOperationTracker, } }; - screenStack.ScreenPushed += screenPushed; - screenStack.ScreenExited += screenExited; - - screenStack.Push(loungeSubScreen = CreateLounge()); - } - - private readonly IBindable apiState = new Bindable(); - - [BackgroundDependencyLoader(true)] - private void load(IdleTracker idleTracker) - { - apiState.BindTo(API.State); - apiState.BindValueChanged(onlineStateChanged, true); - - if (idleTracker != null) - isIdle.BindTo(idleTracker.IsIdle); + // a lot of the functionality in this class depends on loungeSubScreen being in a ready to go state. + // as such, we intentionally load this inline so it is ready alongside this screen. + LoadComponent(loungeSubScreen = CreateLounge()); } private void onlineStateChanged(ValueChangedEvent state) => Schedule(() => @@ -179,7 +179,20 @@ namespace osu.Game.Screens.OnlinePlay protected override void LoadComplete() { base.LoadComplete(); - isIdle.BindValueChanged(idle => UpdatePollingRate(idle.NewValue), true); + + screenStack.ScreenPushed += screenPushed; + screenStack.ScreenExited += screenExited; + + screenStack.Push(loungeSubScreen); + + apiState.BindTo(API.State); + apiState.BindValueChanged(onlineStateChanged, true); + + if (idleTracker != null) + { + isIdle.BindTo(idleTracker.IsIdle); + isIdle.BindValueChanged(idle => UpdatePollingRate(idle.NewValue), true); + } } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) From 26cc4af87c03fe513f4b5d31e27fb61db7245519 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Jul 2021 16:44:51 +0900 Subject: [PATCH 0536/2442] Revert unintended changes --- osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index f39455dc85..36dd9c2de3 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -209,6 +209,9 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null); AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password"); AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().Click()); + + AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); + AddUntilStep("wait for join", () => client.Room != null); } [Test] From 1a8ab77f21eaf4a22c12c586b205b418a3259b7a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Jul 2021 19:03:29 +0900 Subject: [PATCH 0537/2442] Revert nullability change --- .../Visual/Playlists/TestScenePlaylistsResultsScreen.cs | 9 +++------ .../Overlays/BeatmapSet/Scores/TopScoreUserSection.cs | 5 +---- .../Profile/Sections/Ranks/DrawableProfileScore.cs | 5 +---- osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs | 2 +- osu.Game/Scoring/ScoreInfo.cs | 4 +--- 5 files changed, 7 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs index 95dab61d7b..61d49e4018 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Net; using JetBrains.Annotations; @@ -227,13 +226,11 @@ namespace osu.Game.Tests.Visual.Playlists private MultiplayerScore createUserResponse([NotNull] ScoreInfo userScore) { - Debug.Assert(userScore.Date != null); - var multiplayerUserScore = new MultiplayerScore { ID = (int)(userScore.OnlineScoreID ?? currentScoreId++), Accuracy = userScore.Accuracy, - EndedAt = userScore.Date.Value, + EndedAt = userScore.Date, Passed = userScore.Passed, Rank = userScore.Rank, Position = 200, @@ -254,7 +251,7 @@ namespace osu.Game.Tests.Visual.Playlists { ID = currentScoreId++, Accuracy = userScore.Accuracy, - EndedAt = userScore.Date.Value, + EndedAt = userScore.Date, Passed = true, Rank = userScore.Rank, MaxCombo = userScore.MaxCombo, @@ -272,7 +269,7 @@ namespace osu.Game.Tests.Visual.Playlists { ID = currentScoreId++, Accuracy = userScore.Accuracy, - EndedAt = userScore.Date.Value, + EndedAt = userScore.Date, Passed = true, Rank = userScore.Rank, MaxCombo = userScore.MaxCombo, diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs index 324d82b9c2..736366fb5c 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Diagnostics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -137,11 +136,9 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { set { - Debug.Assert(value.Date != null); - avatar.User = value.User; flag.Country = value.User.Country; - achievedOn.Date = value.Date.Value; + achievedOn.Date = value.Date; usernameText.Clear(); usernameText.AddUserLink(value.User); diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs index ec02273a99..713303285a 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.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.Diagnostics; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; @@ -45,8 +44,6 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks [BackgroundDependencyLoader] private void load() { - Debug.Assert(Score.Date != null); - AddInternal(new ProfileItemContainer { Children = new Drawable[] @@ -95,7 +92,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular), Colour = colours.Yellow }, - new DrawableDate(Score.Date.Value, 12) + new DrawableDate(Score.Date, 12) { Colour = colourProvider.Foreground1 } diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs index 0a405a9933..288552879c 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs @@ -59,7 +59,7 @@ namespace osu.Game.Scoring.Legacy sw.Write((int)score.ScoreInfo.Ruleset.CreateInstance().ConvertToLegacyMods(score.ScoreInfo.Mods)); sw.Write(getHpGraphFormatted()); - sw.Write((score.ScoreInfo.Date ?? DateTimeOffset.Now).DateTime); + sw.Write(score.ScoreInfo.Date.DateTime); sw.WriteByteArray(createReplayData()); sw.Write((long)0); writeModSpecificData(score.ScoreInfo, sw); diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index d2df0058d8..a0c4d5a026 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; -using JetBrains.Annotations; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using osu.Game.Beatmaps; @@ -156,8 +155,7 @@ namespace osu.Game.Scoring public long? OnlineScoreID { get; set; } [JsonIgnore] - [CanBeNull] // may be null in cases of autoplay. - public DateTimeOffset? Date { get; set; } + public DateTimeOffset Date { get; set; } [JsonProperty("statistics")] public Dictionary Statistics = new Dictionary(); From 8e1f8c28bdd147191d38af99cf536e09690c6015 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Jul 2021 19:05:08 +0900 Subject: [PATCH 0538/2442] Use `default` value to denote no play date, rather than `null` --- .../Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs | 4 ++-- .../Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs index 46450cebc9..10fcd3cffd 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs @@ -56,7 +56,7 @@ namespace osu.Game.Tests.Visual.Ranking } [Test] - public void TestWithNullDate() + public void TestWithDefaultDate() { AddStep("show autoplay score", () => { @@ -69,7 +69,7 @@ namespace osu.Game.Tests.Visual.Ranking { Mods = mods, Beatmap = beatmap, - Date = null, + Date = default, }); }); diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index dd053c11f9..e10fe5726d 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -227,8 +227,8 @@ namespace osu.Game.Screens.Ranking.Expanded } }); - if (score.Date != null) - AddInternal(new PlayedOnText(score.Date.Value)); + if (score.Date != default) + AddInternal(new PlayedOnText(score.Date)); if (score.Mods.Any()) { From 9c4fbf45e9d906cc41c0cc2db28b6267b029c644 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Jul 2021 19:36:12 +0900 Subject: [PATCH 0539/2442] Add the ability to enter and exit the skin editor via on-screen buttons --- osu.Game/OsuGame.cs | 2 +- .../Overlays/Settings/Sections/SkinSection.cs | 10 ++++- osu.Game/Skinning/Editor/SkinEditor.cs | 7 +++ osu.Game/Skinning/Editor/SkinEditorOverlay.cs | 44 +++++++++++++------ 4 files changed, 46 insertions(+), 17 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 8119df43ac..6741c1cd13 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -757,7 +757,7 @@ namespace osu.Game loadComponentSingleFile(userProfile = new UserProfileOverlay(), overlayContent.Add, true); loadComponentSingleFile(beatmapSetOverlay = new BeatmapSetOverlay(), overlayContent.Add, true); loadComponentSingleFile(wikiOverlay = new WikiOverlay(), overlayContent.Add, true); - loadComponentSingleFile(skinEditor = new SkinEditorOverlay(screenContainer), overlayContent.Add); + loadComponentSingleFile(skinEditor = new SkinEditorOverlay(screenContainer), overlayContent.Add, true); loadComponentSingleFile(new LoginOverlay { diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 316837d27d..e89f3424d9 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -13,6 +13,7 @@ using osu.Framework.Logging; using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Skinning; +using osu.Game.Skinning.Editor; using osuTK; namespace osu.Game.Overlays.Settings.Sections @@ -57,14 +58,19 @@ namespace osu.Game.Overlays.Settings.Sections private IBindable> managerUpdated; private IBindable> managerRemoved; - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) + [BackgroundDependencyLoader(permitNulls: true)] + private void load(OsuConfigManager config, SkinEditorOverlay skinEditor) { FlowContent.Spacing = new Vector2(0, 5); Children = new Drawable[] { skinDropdown = new SkinSettingsDropdown(), + new SettingsButton + { + Text = "Skin layout editor", + Action = () => skinEditor?.Toggle(), + }, new ExportSkinButton(), new SettingsSlider { diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 07a94cac7a..8de64eecb8 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -56,6 +56,13 @@ namespace osu.Game.Skinning.Editor RelativeSizeAxes = Axes.Both, Children = new Drawable[] { + new TriangleButton + { + Margin = new MarginPadding(10), + Text = "Close", + Width = 100, + Action = Hide, + }, headerText = new OsuTextFlowContainer { TextAnchor = Anchor.TopCentre, diff --git a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs index 88020896bb..34fac9c9cf 100644 --- a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs +++ b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs @@ -38,28 +38,44 @@ namespace osu.Game.Skinning.Editor { case GlobalAction.Back: if (skinEditor?.State.Value == Visibility.Visible) - { - skinEditor.ToggleVisibility(); - return true; - } - - break; + Hide(); + return true; case GlobalAction.ToggleSkinEditor: - if (skinEditor == null) - { - LoadComponentAsync(skinEditor = new SkinEditor(target), AddInternal); - skinEditor.State.BindValueChanged(editorVisibilityChanged); - } - else - skinEditor.ToggleVisibility(); - + Toggle(); return true; } return false; } + public void Toggle() + { + if (skinEditor == null) + Show(); + else + skinEditor.ToggleVisibility(); + } + + public override void Hide() + { + base.Hide(); + skinEditor.Hide(); + } + + public override void Show() + { + base.Show(); + + if (skinEditor == null) + { + LoadComponentAsync(skinEditor = new SkinEditor(target), AddInternal); + skinEditor.State.BindValueChanged(editorVisibilityChanged); + } + else + skinEditor.Show(); + } + private void editorVisibilityChanged(ValueChangedEvent visibility) { if (visibility.NewValue == Visibility.Visible) From 59457743e57bda7b2e17c5b1afd88dcba581e2da Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Jul 2021 19:41:52 +0900 Subject: [PATCH 0540/2442] Move further to the right to avoid overlap with toolbox listing --- osu.Game/Skinning/Editor/SkinEditor.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 8de64eecb8..6c2310ce50 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -56,13 +56,6 @@ namespace osu.Game.Skinning.Editor RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new TriangleButton - { - Margin = new MarginPadding(10), - Text = "Close", - Width = 100, - Action = Hide, - }, headerText = new OsuTextFlowContainer { TextAnchor = Anchor.TopCentre, @@ -95,6 +88,13 @@ namespace osu.Game.Skinning.Editor Children = new Drawable[] { new SkinBlueprintContainer(targetScreen), + new TriangleButton + { + Margin = new MarginPadding(10), + Text = "Close", + Width = 100, + Action = Hide, + }, new FillFlowContainer { Direction = FillDirection.Horizontal, From 16a2e63bd43e8ae2060151599ca23d136418ac38 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 20 Jul 2021 19:44:02 +0900 Subject: [PATCH 0541/2442] Use existing localisation --- osu.Game/Skinning/Editor/SkinEditor.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 6c2310ce50..8052f82c93 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -14,6 +14,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; +using osu.Game.Resources.Localisation.Web; using osuTK; namespace osu.Game.Skinning.Editor @@ -91,7 +92,7 @@ namespace osu.Game.Skinning.Editor new TriangleButton { Margin = new MarginPadding(10), - Text = "Close", + Text = CommonStrings.ButtonsClose, Width = 100, Action = Hide, }, From 9d92b795fac46560120cb35cfb9222459c8e8b1d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 20 Jul 2021 14:15:43 +0300 Subject: [PATCH 0542/2442] Revert making `ComboOffset`s legacy and define `BeatmapSkinComboIndex` instead --- .../Beatmaps/CatchBeatmapConverter.cs | 3 ++ .../Objects/CatchHitObject.cs | 4 ++ .../TestSceneLegacyBeatmapSkin.cs | 4 +- .../Beatmaps/OsuBeatmapConverter.cs | 6 +-- .../Beatmaps/OsuBeatmapProcessor.cs | 18 --------- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 16 +++++--- .../Formats/LegacyBeatmapDecoderTest.cs | 39 +++++++++++++++---- .../TestSceneHitObjectAccentColour.cs | 3 ++ osu.Game/Beatmaps/BeatmapProcessor.cs | 2 + .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 5 +-- .../Objects/Legacy/Catch/ConvertHit.cs | 2 + .../Legacy/Catch/ConvertHitObjectParser.cs | 14 +++++++ .../Objects/Legacy/Catch/ConvertSlider.cs | 2 + .../Objects/Legacy/Catch/ConvertSpinner.cs | 2 + .../Rulesets/Objects/Legacy/Osu/ConvertHit.cs | 6 +-- .../Legacy/Osu/ConvertHitObjectParser.cs | 4 +- .../Objects/Legacy/Osu/ConvertSlider.cs | 6 +-- .../Objects/Legacy/Osu/ConvertSpinner.cs | 2 + osu.Game/Rulesets/Objects/Types/IHasCombo.cs | 5 +++ .../Objects/Types/IHasComboInformation.cs | 6 +++ .../Types/IHasLegacyBeatmapComboOffset.cs | 24 ------------ osu.Game/Skinning/LegacyBeatmapSkin.cs | 7 +--- 22 files changed, 102 insertions(+), 78 deletions(-) delete mode 100644 osu.Game/Rulesets/Objects/Types/IHasLegacyBeatmapComboOffset.cs diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs index fca2291f41..7774a7da09 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs @@ -39,6 +39,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps RepeatCount = curveData.RepeatCount, X = xPositionData?.X ?? 0, NewCombo = comboData?.NewCombo ?? false, + ComboOffset = comboData?.ComboOffset ?? 0, LegacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0, LegacyConvertedY = yPositionData?.Y ?? CatchHitObject.DEFAULT_LEGACY_CONVERT_Y }.Yield(); @@ -50,6 +51,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps Samples = obj.Samples, Duration = endTime.Duration, NewCombo = comboData?.NewCombo ?? false, + ComboOffset = comboData?.ComboOffset ?? 0, }.Yield(); default: @@ -58,6 +60,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps StartTime = obj.StartTime, Samples = obj.Samples, NewCombo = comboData?.NewCombo ?? false, + ComboOffset = comboData?.ComboOffset ?? 0, X = xPositionData?.X ?? 0, LegacyConvertedY = yPositionData?.Y ?? CatchHitObject.DEFAULT_LEGACY_CONVERT_Y }.Yield(); diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index f0515ee314..bad558fcad 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -77,6 +77,8 @@ namespace osu.Game.Rulesets.Catch.Objects public virtual bool NewCombo { get; set; } + public int ComboOffset { get; set; } + public Bindable IndexInCurrentComboBindable { get; } = new Bindable(); public int IndexInCurrentCombo @@ -93,6 +95,8 @@ namespace osu.Game.Rulesets.Catch.Objects set => ComboIndexBindable.Value = value; } + public int BeatmapSkinComboIndex { get; set; } + public Bindable LastInComboBindable { get; } = new Bindable(); /// diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs index a009c560cb..f514753890 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs @@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Tests ConfigureTest(useBeatmapSkin, true, userHasCustomColours); assertCorrectObjectComboColours("is beatmap skin colours with legacy offsets applied", TestBeatmapSkin.Colours, - (i, obj) => i + 1 + obj.LegacyBeatmapComboOffset); + (i, obj) => i + 1 + obj.ComboOffset); } [TestCase(true)] @@ -136,7 +136,7 @@ namespace osu.Game.Rulesets.Osu.Tests StartTime = i, Position = new Vector2(256, 192), NewCombo = true, - LegacyBeatmapComboOffset = i, + ComboOffset = i, }); } diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs index d812f86938..a2fc4848af 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps RepeatCount = curveData.RepeatCount, Position = positionData?.Position ?? Vector2.Zero, NewCombo = comboData?.NewCombo ?? false, - LegacyBeatmapComboOffset = (original as IHasLegacyBeatmapComboOffset)?.LegacyBeatmapComboOffset ?? 0, + ComboOffset = comboData?.ComboOffset ?? 0, LegacyLastTickOffset = (original as IHasLegacyLastTickOffset)?.LegacyLastTickOffset, // prior to v8, speed multipliers don't adjust for how many ticks are generated over the same distance. // this results in more (or less) ticks being generated in ()) - { - if (obj.NewCombo) - obj.LegacyBeatmapComboIndex = (lastObj?.LegacyBeatmapComboIndex ?? 0) + obj.LegacyBeatmapComboOffset + 1; - else if (lastObj != null) - obj.LegacyBeatmapComboIndex = lastObj.LegacyBeatmapComboIndex; - - lastObj = obj; - } - } - public override void PostProcess() { base.PostProcess(); diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index 6de061978c..db8a02aa0f 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -14,7 +14,7 @@ using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects { - public abstract class OsuHitObject : HitObject, IHasComboInformation, IHasPosition, IHasLegacyBeatmapComboOffset + public abstract class OsuHitObject : HitObject, IHasComboInformation, IHasPosition { /// /// The radius of hit objects (ie. the radius of a ). @@ -73,6 +73,14 @@ namespace osu.Game.Rulesets.Osu.Objects public virtual bool NewCombo { get; set; } + public readonly Bindable ComboOffsetBindable = new Bindable(); + + public int ComboOffset + { + get => ComboOffsetBindable.Value; + set => ComboOffsetBindable.Value = value; + } + public Bindable IndexInCurrentComboBindable { get; } = new Bindable(); public virtual int IndexInCurrentCombo @@ -89,6 +97,8 @@ namespace osu.Game.Rulesets.Osu.Objects set => ComboIndexBindable.Value = value; } + public int BeatmapSkinComboIndex { get; set; } + public Bindable LastInComboBindable { get; } = new Bindable(); public bool LastInCombo @@ -97,10 +107,6 @@ namespace osu.Game.Rulesets.Osu.Objects set => LastInComboBindable.Value = value; } - public int LegacyBeatmapComboOffset { get; set; } - - public int LegacyBeatmapComboIndex { get; set; } - protected OsuHitObject() { StackHeightBindable.BindValueChanged(height => diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index be88bb9b43..42f89a758e 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -14,6 +14,8 @@ using osu.Game.Rulesets.Objects.Types; using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Timing; using osu.Game.IO; +using osu.Game.Rulesets.Catch; +using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Legacy; @@ -308,7 +310,7 @@ namespace osu.Game.Tests.Beatmaps.Formats } [Test] - public void TestDecodeLegacyBeatmapComboOffsets() + public void TestDecodeBeatmapComboOffsetsOsu() { var decoder = new LegacyBeatmapDecoder(); @@ -321,12 +323,35 @@ namespace osu.Game.Tests.Beatmaps.Formats new OsuBeatmapProcessor(converted).PreProcess(); new OsuBeatmapProcessor(converted).PostProcess(); - Assert.AreEqual(4, ((IHasLegacyBeatmapComboOffset)converted.HitObjects.ElementAt(0)).LegacyBeatmapComboIndex); - Assert.AreEqual(5, ((IHasLegacyBeatmapComboOffset)converted.HitObjects.ElementAt(2)).LegacyBeatmapComboIndex); - Assert.AreEqual(5, ((IHasLegacyBeatmapComboOffset)converted.HitObjects.ElementAt(4)).LegacyBeatmapComboIndex); - Assert.AreEqual(6, ((IHasLegacyBeatmapComboOffset)converted.HitObjects.ElementAt(6)).LegacyBeatmapComboIndex); - Assert.AreEqual(11, ((IHasLegacyBeatmapComboOffset)converted.HitObjects.ElementAt(8)).LegacyBeatmapComboIndex); - Assert.AreEqual(14, ((IHasLegacyBeatmapComboOffset)converted.HitObjects.ElementAt(11)).LegacyBeatmapComboIndex); + Assert.AreEqual(4, ((IHasComboInformation)converted.HitObjects.ElementAt(0)).BeatmapSkinComboIndex); + Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(2)).BeatmapSkinComboIndex); + Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(4)).BeatmapSkinComboIndex); + Assert.AreEqual(6, ((IHasComboInformation)converted.HitObjects.ElementAt(6)).BeatmapSkinComboIndex); + Assert.AreEqual(11, ((IHasComboInformation)converted.HitObjects.ElementAt(8)).BeatmapSkinComboIndex); + Assert.AreEqual(14, ((IHasComboInformation)converted.HitObjects.ElementAt(11)).BeatmapSkinComboIndex); + } + } + + [Test] + public void TestDecodeBeatmapComboOffsetsCatch() + { + var decoder = new LegacyBeatmapDecoder(); + + using (var resStream = TestResources.OpenResource("hitobject-combo-offset.osu")) + using (var stream = new LineBufferedReader(resStream)) + { + var beatmap = decoder.Decode(stream); + + var converted = new CatchBeatmapConverter(beatmap, new CatchRuleset()).Convert(); + new CatchBeatmapProcessor(converted).PreProcess(); + new CatchBeatmapProcessor(converted).PostProcess(); + + Assert.AreEqual(4, ((IHasComboInformation)converted.HitObjects.ElementAt(0)).BeatmapSkinComboIndex); + Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(2)).BeatmapSkinComboIndex); + Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(4)).BeatmapSkinComboIndex); + Assert.AreEqual(6, ((IHasComboInformation)converted.HitObjects.ElementAt(6)).BeatmapSkinComboIndex); + Assert.AreEqual(11, ((IHasComboInformation)converted.HitObjects.ElementAt(8)).BeatmapSkinComboIndex); + Assert.AreEqual(14, ((IHasComboInformation)converted.HitObjects.ElementAt(11)).BeatmapSkinComboIndex); } } diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs index 3dd13da6c1..f6f482e254 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs @@ -82,6 +82,7 @@ namespace osu.Game.Tests.Gameplay private class TestHitObjectWithCombo : ConvertHitObject, IHasComboInformation { public bool NewCombo { get; set; } + public int ComboOffset => 0; public Bindable IndexInCurrentComboBindable { get; } = new Bindable(); @@ -99,6 +100,8 @@ namespace osu.Game.Tests.Gameplay set => ComboIndexBindable.Value = value; } + public int BeatmapSkinComboIndex { get; set; } + public Bindable LastInComboBindable { get; } = new Bindable(); public bool LastInCombo diff --git a/osu.Game/Beatmaps/BeatmapProcessor.cs b/osu.Game/Beatmaps/BeatmapProcessor.cs index f75f04b26f..9121222bd7 100644 --- a/osu.Game/Beatmaps/BeatmapProcessor.cs +++ b/osu.Game/Beatmaps/BeatmapProcessor.cs @@ -38,6 +38,7 @@ namespace osu.Game.Beatmaps { obj.IndexInCurrentCombo = 0; obj.ComboIndex = (lastObj?.ComboIndex ?? 0) + 1; + obj.BeatmapSkinComboIndex = (lastObj?.BeatmapSkinComboIndex ?? 0) + obj.ComboOffset + 1; if (lastObj != null) lastObj.LastInCombo = true; @@ -46,6 +47,7 @@ namespace osu.Game.Beatmaps { obj.IndexInCurrentCombo = lastObj.IndexInCurrentCombo + 1; obj.ComboIndex = lastObj.ComboIndex; + obj.BeatmapSkinComboIndex = lastObj.BeatmapSkinComboIndex; } lastObj = obj; diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index fed8af5b95..f14f6ec10c 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -289,11 +289,10 @@ namespace osu.Game.Beatmaps.Formats if (hitObject is IHasCombo combo) { + type = (LegacyHitObjectType)(combo.ComboOffset << 4); + if (combo.NewCombo) type |= LegacyHitObjectType.NewCombo; - - if (hitObject is IHasLegacyBeatmapComboOffset comboOffset) - type |= (LegacyHitObjectType)(comboOffset.LegacyBeatmapComboOffset << 4); } switch (hitObject) diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs index 0b0a18ba12..12b4812824 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs @@ -18,5 +18,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch public Vector2 Position { get; set; } public bool NewCombo { get; set; } + + public int ComboOffset { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs index d304ea8d39..c29179f749 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs @@ -18,16 +18,21 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch } private bool forceNewCombo; + private int extraComboOffset; protected override HitObject CreateHit(Vector2 position, bool newCombo, int comboOffset) { newCombo |= forceNewCombo; + comboOffset += extraComboOffset; + forceNewCombo = false; + extraComboOffset = 0; return new ConvertHit { Position = position, NewCombo = newCombo, + ComboOffset = comboOffset }; } @@ -35,12 +40,16 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch List> nodeSamples) { newCombo |= forceNewCombo; + comboOffset += extraComboOffset; + forceNewCombo = false; + extraComboOffset = 0; return new ConvertSlider { Position = position, NewCombo = FirstObject || newCombo, + ComboOffset = comboOffset, Path = new SliderPath(controlPoints, length), NodeSamples = nodeSamples, RepeatCount = repeatCount @@ -49,6 +58,11 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double duration) { + // Convert spinners don't create the new combo themselves, but force the next non-spinner hitobject to create a new combo + // Their combo offset is still added to that next hitobject's combo index + forceNewCombo |= FormatVersion <= 8 || newCombo; + extraComboOffset += comboOffset; + return new ConvertSpinner { Duration = duration diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs index 6edf15478f..fb1afed3b4 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs @@ -18,5 +18,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch public Vector2 Position { get; set; } public bool NewCombo { get; set; } + + public int ComboOffset { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs index 3355bb3ee5..014494ec54 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs @@ -17,5 +17,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch public float X => 256; // Required for CatchBeatmapConverter public bool NewCombo { get; set; } + + public int ComboOffset { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs index 03b103b8fe..069366bad3 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu /// /// Legacy osu! Hit-type, used for parsing Beatmaps. /// - internal sealed class ConvertHit : ConvertHitObject, IHasPosition, IHasCombo, IHasLegacyBeatmapComboOffset + internal sealed class ConvertHit : ConvertHitObject, IHasPosition, IHasCombo { public Vector2 Position { get; set; } @@ -19,8 +19,6 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu public bool NewCombo { get; set; } - public int LegacyBeatmapComboOffset { get; set; } - - int IHasLegacyBeatmapComboOffset.LegacyBeatmapComboIndex { get; set; } + public int ComboOffset { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs index 451b714266..75ecab0b8f 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu { Position = position, NewCombo = FirstObject || newCombo, - LegacyBeatmapComboOffset = comboOffset + ComboOffset = comboOffset }; } @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu { Position = position, NewCombo = FirstObject || newCombo, - LegacyBeatmapComboOffset = comboOffset, + ComboOffset = comboOffset, Path = new SliderPath(controlPoints, length), NodeSamples = nodeSamples, RepeatCount = repeatCount diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs index c76c5a3f67..e947690668 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu /// /// Legacy osu! Slider-type, used for parsing Beatmaps. /// - internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasPosition, IHasCombo, IHasLegacyBeatmapComboOffset + internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasPosition, IHasCombo { public Vector2 Position { get; set; } @@ -19,8 +19,6 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu public bool NewCombo { get; set; } - public int LegacyBeatmapComboOffset { get; set; } - - int IHasLegacyBeatmapComboOffset.LegacyBeatmapComboIndex { get; set; } + public int ComboOffset { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs index 328a380a96..e9e5ca8c94 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs @@ -22,5 +22,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu public float Y => Position.Y; public bool NewCombo { get; set; } + + public int ComboOffset { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasCombo.cs b/osu.Game/Rulesets/Objects/Types/IHasCombo.cs index 7288684d27..d1a4683a1d 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasCombo.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasCombo.cs @@ -12,5 +12,10 @@ namespace osu.Game.Rulesets.Objects.Types /// Whether the HitObject starts a new combo. /// bool NewCombo { get; } + + /// + /// When starting a new combo, the offset of the new combo relative to the current one. + /// + int ComboOffset { get; } } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs index 03e6f76cca..46d7bef498 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs @@ -26,6 +26,12 @@ namespace osu.Game.Rulesets.Objects.Types /// int ComboIndex { get; set; } + /// + /// A with the of this and all previous hitobjects applied to it. + /// This is used primarily for beatmap skins during combo colour retrieval, rather than the regular . + /// + int BeatmapSkinComboIndex { get; set; } + /// /// Whether the HitObject starts a new combo. /// diff --git a/osu.Game/Rulesets/Objects/Types/IHasLegacyBeatmapComboOffset.cs b/osu.Game/Rulesets/Objects/Types/IHasLegacyBeatmapComboOffset.cs deleted file mode 100644 index 1a39192438..0000000000 --- a/osu.Game/Rulesets/Objects/Types/IHasLegacyBeatmapComboOffset.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Game.Rulesets.Objects.Types -{ - /// - /// A type of that has a combo index with arbitrary offsets applied to use when retrieving legacy beatmap combo colours. - /// This is done in stable for hitobjects to skip combo colours from the beatmap skin (known as "colour hax"). - /// See https://osu.ppy.sh/wiki/en/osu%21_File_Formats/Osu_%28file_format%29#type for more information. - /// - public interface IHasLegacyBeatmapComboOffset - { - /// - /// The legacy offset of the new combo relative to the current one, when starting a new combo. - /// - int LegacyBeatmapComboOffset { get; } - - /// - /// The combo index with the applied, - /// to use for legacy beatmap skins to decide on the combo colour. - /// - int LegacyBeatmapComboIndex { get; set; } - } -} diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 36c9b635c2..5b46a21381 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -63,12 +63,7 @@ namespace osu.Game.Skinning } protected override IBindable GetComboColour(IHasComboColours source, int comboIndex, IHasComboInformation combo) - { - if (combo is IHasLegacyBeatmapComboOffset legacyBeatmapCombo) - return base.GetComboColour(source, legacyBeatmapCombo.LegacyBeatmapComboIndex, combo); - - return base.GetComboColour(source, comboIndex, combo); - } + => base.GetComboColour(source, combo.BeatmapSkinComboIndex, combo); public override ISample GetSample(ISampleInfo sampleInfo) { From 300b3f22b1e943aad5d62da0cc3a7db34b116e53 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 20 Jul 2021 14:35:21 +0300 Subject: [PATCH 0543/2442] Remove no longer correct usage of "legacy" in offsets --- osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs index f514753890..f2f68e805c 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs @@ -84,22 +84,22 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(true, false)] [TestCase(false, true)] [TestCase(false, false)] - public void TestLegacyOffsetWithBeatmapColours(bool userHasCustomColours, bool useBeatmapSkin) + public void TestComboOffsetWithBeatmapColours(bool userHasCustomColours, bool useBeatmapSkin) { PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true, getHitCirclesWithLegacyOffsets())); ConfigureTest(useBeatmapSkin, true, userHasCustomColours); - assertCorrectObjectComboColours("is beatmap skin colours with legacy offsets applied", + assertCorrectObjectComboColours("is beatmap skin colours with combo offsets applied", TestBeatmapSkin.Colours, (i, obj) => i + 1 + obj.ComboOffset); } [TestCase(true)] [TestCase(false)] - public void TestLegacyOffsetWithIgnoredBeatmapColours(bool useBeatmapSkin) + public void TestComboOffsetWithIgnoredBeatmapColours(bool useBeatmapSkin) { PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true, getHitCirclesWithLegacyOffsets())); ConfigureTest(useBeatmapSkin, false, true); - assertCorrectObjectComboColours("is user skin colours without legacy offsets applied", + assertCorrectObjectComboColours("is user skin colours without combo offsets applied", TestSkin.Colours, (i, _) => i + 1); } From 1e634d9db091ea1ba71ddfa90a4ffd06c5e4aa71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 20 Jul 2021 21:48:38 +0200 Subject: [PATCH 0544/2442] Adjust user beatmap sections on profile overlay to match web --- osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs | 4 ++-- .../Sections/Beatmaps/PaginatedBeatmapContainer.cs | 8 ++++---- osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs | 4 ++-- osu.Game/Users/User.cs | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs index fb1385793f..e2c0ed4301 100644 --- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs @@ -26,9 +26,9 @@ namespace osu.Game.Online.API.Requests public enum BeatmapSetType { Favourite, - RankedAndApproved, + Ranked, Loved, - Unranked, + Pending, Graveyard } } diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index ec64371a5d..8657e356c9 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -46,11 +46,11 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps case BeatmapSetType.Loved: return user.LovedBeatmapsetCount; - case BeatmapSetType.RankedAndApproved: - return user.RankedAndApprovedBeatmapsetCount; + case BeatmapSetType.Ranked: + return user.RankedBeatmapsetCount; - case BeatmapSetType.Unranked: - return user.UnrankedBeatmapsetCount; + case BeatmapSetType.Pending: + return user.PendingBeatmapsetCount; default: return 0; diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs index 843ab531be..af6ab4aad1 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs @@ -19,9 +19,9 @@ namespace osu.Game.Overlays.Profile.Sections Children = new[] { new PaginatedBeatmapContainer(BeatmapSetType.Favourite, User, UsersStrings.ShowExtraBeatmapsFavouriteTitle), - new PaginatedBeatmapContainer(BeatmapSetType.RankedAndApproved, User, UsersStrings.ShowExtraBeatmapsRankedTitle), + new PaginatedBeatmapContainer(BeatmapSetType.Ranked, User, UsersStrings.ShowExtraBeatmapsRankedTitle), new PaginatedBeatmapContainer(BeatmapSetType.Loved, User, UsersStrings.ShowExtraBeatmapsLovedTitle), - new PaginatedBeatmapContainer(BeatmapSetType.Unranked, User, UsersStrings.ShowExtraBeatmapsPendingTitle), + new PaginatedBeatmapContainer(BeatmapSetType.Pending, User, UsersStrings.ShowExtraBeatmapsPendingTitle), new PaginatedBeatmapContainer(BeatmapSetType.Graveyard, User, UsersStrings.ShowExtraBeatmapsGraveyardTitle) }; } diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index 2e04693e82..20c23153f0 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -138,11 +138,11 @@ namespace osu.Game.Users [JsonProperty(@"loved_beatmapset_count")] public int LovedBeatmapsetCount; - [JsonProperty(@"ranked_and_approved_beatmapset_count")] - public int RankedAndApprovedBeatmapsetCount; + [JsonProperty(@"ranked_beatmapset_count")] + public int RankedBeatmapsetCount; - [JsonProperty(@"unranked_beatmapset_count")] - public int UnrankedBeatmapsetCount; + [JsonProperty(@"pending_beatmapset_count")] + public int PendingBeatmapsetCount; [JsonProperty(@"scores_best_count")] public int ScoresBestCount; From bfec87b0829294c59500c1e4cc204686c2bc7371 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Tue, 20 Jul 2021 22:25:17 +0200 Subject: [PATCH 0545/2442] Let TimelineBlueprintContainer only accept positional input within timeline quad --- .../Compose/Components/Timeline/TimelineBlueprintContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 8e0b39513a..69db191cce 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -35,8 +35,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private Bindable placement; private SelectionBlueprint placementBlueprint; - // We want children to be able to be clicked and dragged, regardless of this drawable's size - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; + // We want children within the timeline to be interactable + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => timeline.ScreenSpaceDrawQuad.Contains(screenSpacePos); public TimelineBlueprintContainer(HitObjectComposer composer) : base(composer) From a8cf6a6854231bc2f7fbeffee45d97460c2dd812 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Tue, 20 Jul 2021 23:00:58 +0200 Subject: [PATCH 0546/2442] Fix slight Y position offset in HandleDrag --- .../Compose/Components/Timeline/TimelineBlueprintContainer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 69db191cce..a5210132a3 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -309,7 +309,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public override bool HandleDrag(MouseButtonEvent e) { // The dragbox should only be active if the mouseDownPosition.Y is within this drawable's bounds. - if (DrawRectangle.Top > e.MouseDownPosition.Y || DrawRectangle.Bottom < e.MouseDownPosition.Y) + float localY = ToLocalSpace(e.ScreenSpaceMouseDownPosition).Y; + if (DrawRectangle.Top > localY || DrawRectangle.Bottom < localY) return false; selectionStart ??= e.MouseDownPosition.X / timeline.CurrentZoom; From db9cf443c7b5c2d76b06fe40f4993ad2bccc20a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 21 Jul 2021 00:04:51 +0200 Subject: [PATCH 0547/2442] Allow confirming room password by pressing Enter --- .../TestSceneMultiplayerLoungeSubScreen.cs | 16 ++++++++++++++++ .../OnlinePlay/Lounge/Components/DrawableRoom.cs | 1 + 2 files changed, 17 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs index de46d9e25a..4ea635fd3e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs @@ -80,6 +80,22 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("room join password correct", () => lastJoinedPassword == "password"); } + [Test] + public void TestJoinRoomWithPasswordViaKeyboardOnly() + { + DrawableRoom.PasswordEntryPopover passwordEntryPopover = null; + + AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true)); + AddStep("select room", () => InputManager.Key(Key.Down)); + AddStep("attempt join room", () => InputManager.Key(Key.Enter)); + AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null); + AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password"); + AddStep("press enter", () => InputManager.Key(Key.Enter)); + + AddAssert("room join requested", () => lastJoinedRoom == RoomManager.Rooms.First()); + AddAssert("room join password correct", () => lastJoinedPassword == "password"); + } + private void onRoomJoined(Room room, string password) { lastJoinedRoom = room; diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 236408851f..940ae873ec 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -390,6 +390,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components base.LoadComplete(); Schedule(() => GetContainingInputManager().ChangeFocus(passwordTextbox)); + passwordTextbox.OnCommit += (_, __) => JoinRequested?.Invoke(room, passwordTextbox.Text); } } } From 37393a84320ca08d3ace82a07978a27cf3b1746a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 21 Jul 2021 03:12:44 +0300 Subject: [PATCH 0548/2442] Allow defining custom storage name for debug builds of osu!lazer --- osu.Desktop/Program.cs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 5fb09c0cef..b17fe87538 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -23,7 +23,16 @@ namespace osu.Desktop // Back up the cwd before DesktopGameHost changes it var cwd = Environment.CurrentDirectory; - using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true)) + string gameName = @"osu"; + + if (DebugUtils.IsDebugBuild) + { + var customNameArg = args.SingleOrDefault(s => s.StartsWith(@"--name=", StringComparison.Ordinal)); + if (customNameArg != null) + gameName = customNameArg.Replace(@"--name=", string.Empty); + } + + using (DesktopGameHost host = Host.GetSuitableHost(gameName, true)) { host.ExceptionThrown += handleException; @@ -48,16 +57,10 @@ namespace osu.Desktop return 0; } - switch (args.FirstOrDefault() ?? string.Empty) - { - default: - host.Run(new OsuGameDesktop(args)); - break; - - case "--tournament": - host.Run(new TournamentGame()); - break; - } + if (args.Contains("--tournament")) + host.Run(new TournamentGame()); + else + host.Run(new OsuGameDesktop(args)); return 0; } From ca3dfb2498c589fc0019e50c0e161e8142ec2739 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 21 Jul 2021 12:53:48 +0900 Subject: [PATCH 0549/2442] Fix comment --- .../Edit/Blueprints/Components/SelectionEditablePath.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs index 3acb7f43a6..b643ca9680 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components else if (!VertexStates[index].IsSelected) selectOnly(index); - // Don't inhabit right click, to show the context menu + // Don't inhibit right click, to show the context menu return e.Button != MouseButton.Right; } From 97fba5df583e1b471fba7d8840cc180ae94b4a20 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 21 Jul 2021 12:59:42 +0900 Subject: [PATCH 0550/2442] Use existing method for the same code --- .../Edit/Blueprints/Components/SelectionEditablePath.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs index b643ca9680..6c08b59e09 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs @@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components protected override void OnDrag(DragEvent e) { - Vector2 mousePosition = ToLocalSpace(e.ScreenSpaceMousePosition) - new Vector2(0, DrawHeight); + Vector2 mousePosition = ToRelativePosition(e.ScreenSpaceMousePosition); double distanceDelta = PositionToDistance(mousePosition.Y) - PositionToDistance(dragStartPosition.Y); float xDelta = mousePosition.X - dragStartPosition.X; MoveSelectedVertices(distanceDelta, xDelta); From cc0110aa5267262a1dfea8185af8d423227e742b Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 21 Jul 2021 13:17:13 +0900 Subject: [PATCH 0551/2442] Add doc comment to `VertexState` --- .../Edit/Blueprints/Components/VertexState.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/VertexState.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/VertexState.cs index 55abbf7475..3f240c7944 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/VertexState.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/VertexState.cs @@ -7,12 +7,25 @@ using osu.Game.Rulesets.Catch.Objects; namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components { + /// + /// Holds the state of a vertex in the path of a . + /// public class VertexState { + /// + /// Whether the vertex is selected. + /// public bool IsSelected { get; set; } + /// + /// Whether the vertex can be moved or deleted. + /// public bool IsFixed { get; set; } + /// + /// The position of the vertex before a vertex moving operation starts. + /// This is used to implement "memory-less" moving operations (only the final position matters) to improve UX. + /// public JuiceStreamPathVertex VertexBeforeChange { get; set; } } } From 2b0d5300750ff2f6a78f541c51be1a2ade92c131 Mon Sep 17 00:00:00 2001 From: Anton Kovalyov Date: Tue, 20 Jul 2021 21:18:24 -0700 Subject: [PATCH 0552/2442] Eliminate Overlay.KeyBinding namespace and move everything to Settings.Section.Input --- osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs | 2 +- .../Sections/Input}/GlobalKeyBindingsSection.cs | 2 +- .../Overlays/{ => Settings/Sections/Input}/KeyBindingPanel.cs | 4 ++-- .../{KeyBinding => Settings/Sections/Input}/KeyBindingRow.cs | 2 +- .../Sections/Input}/KeyBindingsSubsection.cs | 2 +- .../Sections/Input}/RulesetBindingsSection.cs | 2 +- .../Sections/Input}/VariantBindingsSubsection.cs | 2 +- osu.Game/Overlays/SettingsOverlay.cs | 1 + 8 files changed, 9 insertions(+), 8 deletions(-) rename osu.Game/Overlays/{KeyBinding => Settings/Sections/Input}/GlobalKeyBindingsSection.cs (98%) rename osu.Game/Overlays/{ => Settings/Sections/Input}/KeyBindingPanel.cs (89%) rename osu.Game/Overlays/{KeyBinding => Settings/Sections/Input}/KeyBindingRow.cs (99%) rename osu.Game/Overlays/{KeyBinding => Settings/Sections/Input}/KeyBindingsSubsection.cs (97%) rename osu.Game/Overlays/{KeyBinding => Settings/Sections/Input}/RulesetBindingsSection.cs (94%) rename osu.Game/Overlays/{KeyBinding => Settings/Sections/Input}/VariantBindingsSubsection.cs (93%) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index acf9deb3cb..54293485cb 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -8,7 +8,7 @@ using osu.Framework.Testing; using osu.Framework.Threading; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; -using osu.Game.Overlays.KeyBinding; +using osu.Game.Overlays.Settings.Sections.Input; using osuTK.Input; namespace osu.Game.Tests.Visual.Settings diff --git a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs b/osu.Game/Overlays/Settings/Sections/Input/GlobalKeyBindingsSection.cs similarity index 98% rename from osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs rename to osu.Game/Overlays/Settings/Sections/Input/GlobalKeyBindingsSection.cs index 6ea4209cce..4918c3b97c 100644 --- a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/GlobalKeyBindingsSection.cs @@ -7,7 +7,7 @@ using osu.Framework.Localisation; using osu.Game.Input.Bindings; using osu.Game.Overlays.Settings; -namespace osu.Game.Overlays.KeyBinding +namespace osu.Game.Overlays.Settings.Sections.Input { public class GlobalKeyBindingsSection : SettingsSection { diff --git a/osu.Game/Overlays/KeyBindingPanel.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingPanel.cs similarity index 89% rename from osu.Game/Overlays/KeyBindingPanel.cs rename to osu.Game/Overlays/Settings/Sections/Input/KeyBindingPanel.cs index 928bd080fa..86c31ee1b2 100644 --- a/osu.Game/Overlays/KeyBindingPanel.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingPanel.cs @@ -4,11 +4,11 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Input.Bindings; -using osu.Game.Overlays.KeyBinding; using osu.Game.Overlays.Settings; +using osu.Game.Overlays.Settings.Sections.Input; using osu.Game.Rulesets; -namespace osu.Game.Overlays +namespace osu.Game.Overlays.Settings.Sections.Input { public class KeyBindingPanel : SettingsSubPanel { diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs similarity index 99% rename from osu.Game/Overlays/KeyBinding/KeyBindingRow.cs rename to osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs index ef620df171..4f7deebb5b 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs @@ -24,7 +24,7 @@ using osuTK; using osuTK.Graphics; using osuTK.Input; -namespace osu.Game.Overlays.KeyBinding +namespace osu.Game.Overlays.Settings.Sections.Input { public class KeyBindingRow : Container, IFilterable { diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs similarity index 97% rename from osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs rename to osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs index 1fdc1b6574..beee1650a5 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs @@ -13,7 +13,7 @@ using osu.Game.Overlays.Settings; using osu.Game.Rulesets; using osuTK; -namespace osu.Game.Overlays.KeyBinding +namespace osu.Game.Overlays.Settings.Sections.Input { public abstract class KeyBindingsSubsection : SettingsSubsection { diff --git a/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs b/osu.Game/Overlays/Settings/Sections/Input/RulesetBindingsSection.cs similarity index 94% rename from osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs rename to osu.Game/Overlays/Settings/Sections/Input/RulesetBindingsSection.cs index 332fb6c8fc..d2fb811e73 100644 --- a/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/RulesetBindingsSection.cs @@ -7,7 +7,7 @@ using osu.Game.Graphics; using osu.Game.Overlays.Settings; using osu.Game.Rulesets; -namespace osu.Game.Overlays.KeyBinding +namespace osu.Game.Overlays.Settings.Sections.Input { public class RulesetBindingsSection : SettingsSection { diff --git a/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs b/osu.Game/Overlays/Settings/Sections/Input/VariantBindingsSubsection.cs similarity index 93% rename from osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs rename to osu.Game/Overlays/Settings/Sections/Input/VariantBindingsSubsection.cs index 7618a42282..a0f069b3bb 100644 --- a/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/VariantBindingsSubsection.cs @@ -4,7 +4,7 @@ using osu.Framework.Localisation; using osu.Game.Rulesets; -namespace osu.Game.Overlays.KeyBinding +namespace osu.Game.Overlays.Settings.Sections.Input { public class VariantBindingsSubsection : KeyBindingsSubsection { diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index 8c21880cc6..54b780615d 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Overlays.Settings; using osu.Game.Overlays.Settings.Sections; +using osu.Game.Overlays.Settings.Sections.Input; using osuTK.Graphics; using System.Collections.Generic; using System.Linq; From cd447f03059e2e66b7c306dd455ab5028d1ec90e Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 21 Jul 2021 13:27:07 +0900 Subject: [PATCH 0553/2442] Add some doc comment to `JuiceStreamSelectionBlueprint` --- .../Edit/Blueprints/JuiceStreamSelectionBlueprint.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs index defb5eae8b..890d059d19 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamSelectionBlueprint.cs @@ -37,8 +37,16 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints private readonly SelectionEditablePath editablePath; + /// + /// The of the corresponding the current of the hit object. + /// When the path is edited, the change is detected and the of the hit object is updated. + /// private int lastEditablePathId = -1; + /// + /// The of the current of the hit object. + /// When the of the hit object is changed by external means, the change is detected and the is re-initialized. + /// private int lastSliderPathVersion = -1; private Vector2 rightMouseDownPosition; From ebd555129f81e46ec6f73eeeaa4f49e619723fc6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 21 Jul 2021 07:53:24 +0300 Subject: [PATCH 0554/2442] Change to `int`-only debug client ID --- osu.Desktop/Program.cs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index b17fe87538..0bcbc696bc 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -24,12 +24,23 @@ namespace osu.Desktop var cwd = Environment.CurrentDirectory; string gameName = @"osu"; + bool tournamentClient = false; - if (DebugUtils.IsDebugBuild) + foreach (var arg in args.Select(s => s.Split('='))) { - var customNameArg = args.SingleOrDefault(s => s.StartsWith(@"--name=", StringComparison.Ordinal)); - if (customNameArg != null) - gameName = customNameArg.Replace(@"--name=", string.Empty); + switch (arg[0]) + { + case "--tournament": + tournamentClient = true; + break; + + case "--debug-client-id": + if (!DebugUtils.IsDebugBuild) + break; + + gameName = $"{gameName}-{int.Parse(arg[1])}"; + break; + } } using (DesktopGameHost host = Host.GetSuitableHost(gameName, true)) @@ -57,7 +68,7 @@ namespace osu.Desktop return 0; } - if (args.Contains("--tournament")) + if (tournamentClient) host.Run(new TournamentGame()); else host.Run(new OsuGameDesktop(args)); From bfad044b0018aefcf51d559a4ac182d653cb7960 Mon Sep 17 00:00:00 2001 From: Anton Kovalyov Date: Tue, 20 Jul 2021 21:57:55 -0700 Subject: [PATCH 0555/2442] Remove unused imports. --- .../Settings/Sections/Input/GlobalKeyBindingsSection.cs | 1 - osu.Game/Overlays/Settings/Sections/Input/KeyBindingPanel.cs | 2 -- .../Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs | 1 - .../Overlays/Settings/Sections/Input/RulesetBindingsSection.cs | 1 - 4 files changed, 5 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/GlobalKeyBindingsSection.cs b/osu.Game/Overlays/Settings/Sections/Input/GlobalKeyBindingsSection.cs index 4918c3b97c..9898a50320 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/GlobalKeyBindingsSection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/GlobalKeyBindingsSection.cs @@ -5,7 +5,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Input.Bindings; -using osu.Game.Overlays.Settings; namespace osu.Game.Overlays.Settings.Sections.Input { diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingPanel.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingPanel.cs index 86c31ee1b2..7cdc739b7c 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingPanel.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingPanel.cs @@ -4,8 +4,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Input.Bindings; -using osu.Game.Overlays.Settings; -using osu.Game.Overlays.Settings.Sections.Input; using osu.Game.Rulesets; namespace osu.Game.Overlays.Settings.Sections.Input diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs index beee1650a5..d65684fd37 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics; using osu.Game.Database; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; -using osu.Game.Overlays.Settings; using osu.Game.Rulesets; using osuTK; diff --git a/osu.Game/Overlays/Settings/Sections/Input/RulesetBindingsSection.cs b/osu.Game/Overlays/Settings/Sections/Input/RulesetBindingsSection.cs index d2fb811e73..81a4d7eccd 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/RulesetBindingsSection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/RulesetBindingsSection.cs @@ -4,7 +4,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; -using osu.Game.Overlays.Settings; using osu.Game.Rulesets; namespace osu.Game.Overlays.Settings.Sections.Input From 4148d3fdac4437e4f4246c66d908e18979c9e4cf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Jul 2021 15:02:15 +0900 Subject: [PATCH 0556/2442] Add a bit more safety to argument parsing logic --- osu.Desktop/Program.cs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 0bcbc696bc..0b81e2d127 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -17,18 +17,25 @@ namespace osu.Desktop { public static class Program { + private const string base_game_name = @"osu"; + [STAThread] public static int Main(string[] args) { // Back up the cwd before DesktopGameHost changes it var cwd = Environment.CurrentDirectory; - string gameName = @"osu"; + string gameName = base_game_name; bool tournamentClient = false; - foreach (var arg in args.Select(s => s.Split('='))) + foreach (var arg in args) { - switch (arg[0]) + var split = arg.Split('='); + + var key = split[0]; + var val = split[1]; + + switch (key) { case "--tournament": tournamentClient = true; @@ -36,9 +43,12 @@ namespace osu.Desktop case "--debug-client-id": if (!DebugUtils.IsDebugBuild) - break; + throw new InvalidOperationException("Cannot use this argument in a non-debug build."); - gameName = $"{gameName}-{int.Parse(arg[1])}"; + if (!int.TryParse(val, out int clientID)) + throw new ArgumentException("Provided client ID must be an integer."); + + gameName = $"{base_game_name}-{clientID}"; break; } } From 3a968977cb249abc8945b123f24cc7a5f42a8083 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Jul 2021 15:09:50 +0900 Subject: [PATCH 0557/2442] Add rider configuration for dual-client testing --- .run/Dual client test.run.xml | 7 +++++++ .run/osu! (Second Client).run.xml | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 .run/Dual client test.run.xml create mode 100644 .run/osu! (Second Client).run.xml diff --git a/.run/Dual client test.run.xml b/.run/Dual client test.run.xml new file mode 100644 index 0000000000..e112aa3d5d --- /dev/null +++ b/.run/Dual client test.run.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.run/osu! (Second Client).run.xml b/.run/osu! (Second Client).run.xml new file mode 100644 index 0000000000..599b4b986b --- /dev/null +++ b/.run/osu! (Second Client).run.xml @@ -0,0 +1,20 @@ + + + + \ No newline at end of file From 3dddcf3582247e3325f38d032664df9b614a69f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Jul 2021 15:11:01 +0900 Subject: [PATCH 0558/2442] Remove unused using statement --- osu.Desktop/Program.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 0b81e2d127..cbee1694ba 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using System.Linq; using System.Threading; using System.Threading.Tasks; using osu.Framework; From 0118c3638c0ba2285569a6698163cfe112a644d9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 21 Jul 2021 15:59:02 +0900 Subject: [PATCH 0559/2442] Fix beatmap listing continually paginating for null cursors --- .../Overlays/BeatmapListing/BeatmapListingFilterControl.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs index 650d105911..3d8c2b1fe6 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs @@ -189,6 +189,10 @@ namespace osu.Game.Overlays.BeatmapListing private void performRequest() { + // If the previous request returned a null cursor, the API is indicating we can't paginate further (maybe there are no more beatmaps left). + if (lastResponse != null && lastResponse.Cursor == null) + return; + getSetsRequest = new SearchBeatmapSetsRequest( searchControl.Query.Value, searchControl.Ruleset.Value, From 1bff4373b3779c6d418b7bbe5a3e94f88cdf0510 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 21 Jul 2021 15:59:25 +0900 Subject: [PATCH 0560/2442] Allow specifying flipping support of selection box different from scaling --- .../Edit/OsuSelectionHandler.cs | 4 +- .../Editing/TestSceneComposeSelectBox.cs | 2 + .../Edit/Compose/Components/SelectionBox.cs | 58 ++++++++++++++++--- .../Skinning/Editor/SkinSelectionHandler.cs | 2 + 4 files changed, 56 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 57d0cd859d..c2c2226af0 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -35,8 +35,8 @@ namespace osu.Game.Rulesets.Osu.Edit Quad quad = selectedMovableObjects.Length > 0 ? getSurroundingQuad(selectedMovableObjects) : new Quad(); SelectionBox.CanRotate = quad.Width > 0 || quad.Height > 0; - SelectionBox.CanScaleX = quad.Width > 0; - SelectionBox.CanScaleY = quad.Height > 0; + SelectionBox.CanFlipX = SelectionBox.CanScaleX = quad.Width > 0; + SelectionBox.CanFlipY = SelectionBox.CanScaleY = quad.Height > 0; SelectionBox.CanReverse = EditorBeatmap.SelectedHitObjects.Count > 1 || EditorBeatmap.SelectedHitObjects.Any(s => s is Slider); } diff --git a/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs b/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs index d5cfeb1878..87dbb90138 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs @@ -35,6 +35,8 @@ namespace osu.Game.Tests.Visual.Editing CanRotate = true, CanScaleX = true, CanScaleY = true, + CanFlipX = true, + CanFlipY = true, OnRotation = handleRotation, OnScale = handleScale diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index dc457b5320..be52a968bb 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -64,7 +64,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private bool canScaleX; /// - /// Whether vertical scale support should be enabled. + /// Whether horizontal scaling support should be enabled. /// public bool CanScaleX { @@ -81,7 +81,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private bool canScaleY; /// - /// Whether horizontal scale support should be enabled. + /// Whether vertical scaling support should be enabled. /// public bool CanScaleY { @@ -95,6 +95,40 @@ namespace osu.Game.Screens.Edit.Compose.Components } } + private bool canFlipX; + + /// + /// Whether horizontal flipping support should be enabled. + /// + public bool CanFlipX + { + get => canFlipX; + set + { + if (canFlipX == value) return; + + canFlipX = value; + recreate(); + } + } + + private bool canFlipY; + + /// + /// Whether vertical flipping support should be enabled. + /// + public bool CanFlipY + { + get => canFlipY; + set + { + if (canFlipY == value) return; + + canFlipY = value; + recreate(); + } + } + private string text; public string Text @@ -142,10 +176,10 @@ namespace osu.Game.Screens.Edit.Compose.Components return CanReverse && runOperationFromHotkey(OnReverse); case Key.H: - return CanScaleX && runOperationFromHotkey(() => OnFlip?.Invoke(Direction.Horizontal) ?? false); + return CanFlipX && runOperationFromHotkey(() => OnFlip?.Invoke(Direction.Horizontal) ?? false); case Key.J: - return CanScaleY && runOperationFromHotkey(() => OnFlip?.Invoke(Direction.Vertical) ?? false); + return CanFlipY && runOperationFromHotkey(() => OnFlip?.Invoke(Direction.Vertical) ?? false); } return base.OnKeyDown(e); @@ -214,6 +248,8 @@ namespace osu.Game.Screens.Edit.Compose.Components if (CanScaleX) addXScaleComponents(); if (CanScaleX && CanScaleY) addFullScaleComponents(); if (CanScaleY) addYScaleComponents(); + if (CanFlipX) addXFlipComponents(); + if (CanFlipY) addYFlipComponents(); if (CanRotate) addRotationComponents(); if (CanReverse) addButton(FontAwesome.Solid.Backward, "Reverse pattern (Ctrl-G)", () => OnReverse?.Invoke()); } @@ -231,8 +267,6 @@ namespace osu.Game.Screens.Edit.Compose.Components private void addYScaleComponents() { - addButton(FontAwesome.Solid.ArrowsAltV, "Flip vertically (Ctrl-J)", () => OnFlip?.Invoke(Direction.Vertical)); - addScaleHandle(Anchor.TopCentre); addScaleHandle(Anchor.BottomCentre); } @@ -247,12 +281,20 @@ namespace osu.Game.Screens.Edit.Compose.Components private void addXScaleComponents() { - addButton(FontAwesome.Solid.ArrowsAltH, "Flip horizontally (Ctrl-H)", () => OnFlip?.Invoke(Direction.Horizontal)); - addScaleHandle(Anchor.CentreLeft); addScaleHandle(Anchor.CentreRight); } + private void addXFlipComponents() + { + addButton(FontAwesome.Solid.ArrowsAltH, "Flip horizontally (Ctrl-H)", () => OnFlip?.Invoke(Direction.Horizontal)); + } + + private void addYFlipComponents() + { + addButton(FontAwesome.Solid.ArrowsAltV, "Flip vertically (Ctrl-J)", () => OnFlip?.Invoke(Direction.Vertical)); + } + private void addButton(IconUsage icon, string tooltip, Action action) { var button = new SelectionBoxButton(icon, tooltip) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 17eb88226d..0790faad34 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -170,6 +170,8 @@ namespace osu.Game.Skinning.Editor SelectionBox.CanRotate = true; SelectionBox.CanScaleX = true; SelectionBox.CanScaleY = true; + SelectionBox.CanFlipX = true; + SelectionBox.CanFlipY = true; SelectionBox.CanReverse = false; } From bcd1a3c23234590ec6f92691080c57ea91f9f0df Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 21 Jul 2021 16:04:07 +0900 Subject: [PATCH 0561/2442] Use existing bool --- .../Overlays/BeatmapListing/BeatmapListingFilterControl.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs index 3d8c2b1fe6..03d36ff5df 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs @@ -189,10 +189,6 @@ namespace osu.Game.Overlays.BeatmapListing private void performRequest() { - // If the previous request returned a null cursor, the API is indicating we can't paginate further (maybe there are no more beatmaps left). - if (lastResponse != null && lastResponse.Cursor == null) - return; - getSetsRequest = new SearchBeatmapSetsRequest( searchControl.Query.Value, searchControl.Ruleset.Value, @@ -212,7 +208,8 @@ namespace osu.Game.Overlays.BeatmapListing { var sets = response.BeatmapSets.Select(responseJson => responseJson.ToBeatmapSet(rulesets)).ToList(); - if (sets.Count == 0) + // If the previous request returned a null cursor, the API is indicating we can't paginate further (maybe there are no more beatmaps left). + if (sets.Count == 0 || response.Cursor == null) noMoreResults = true; if (CurrentPage == 0) From 60f876511d5de96629fe3a2e53b83e02d7e8a34f Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 21 Jul 2021 15:36:34 +0900 Subject: [PATCH 0562/2442] Add function of computing position range occupied by hit objects --- .../Edit/CatchHitObjectUtils.cs | 39 +++++++++++++++++++ osu.Game.Rulesets.Catch/Edit/PositionRange.cs | 33 ++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Edit/PositionRange.cs diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectUtils.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectUtils.cs index beffdf0362..b059926668 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectUtils.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectUtils.cs @@ -1,7 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; +using System.Linq; using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI.Scrolling; using osuTK; @@ -20,5 +23,41 @@ namespace osu.Game.Rulesets.Catch.Edit { return new Vector2(hitObject.OriginalX, hitObjectContainer.PositionAtTime(hitObject.StartTime)); } + + /// + /// Get the range of horizontal position occupied by the hit object. + /// + /// + /// s are excluded and returns . + /// + public static PositionRange GetPositionRange(HitObject hitObject) + { + switch (hitObject) + { + case Fruit fruit: + return new PositionRange(fruit.OriginalX); + + case Droplet droplet: + return droplet is TinyDroplet ? PositionRange.EMPTY : new PositionRange(droplet.OriginalX); + + case JuiceStream _: + return GetPositionRange(hitObject.NestedHitObjects); + + case BananaShower _: + // A banana shower occupies the whole screen width. + return new PositionRange(0, CatchPlayfield.WIDTH); + + default: + return PositionRange.EMPTY; + } + } + + /// + /// Get the range of horizontal position occupied by the hit objects. + /// + /// + /// s are excluded. + /// + public static PositionRange GetPositionRange(IEnumerable hitObjects) => hitObjects.Select(GetPositionRange).Aggregate(PositionRange.EMPTY, PositionRange.Union); } } diff --git a/osu.Game.Rulesets.Catch/Edit/PositionRange.cs b/osu.Game.Rulesets.Catch/Edit/PositionRange.cs new file mode 100644 index 0000000000..6ed1ac2c06 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/PositionRange.cs @@ -0,0 +1,33 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; + +#nullable enable + +namespace osu.Game.Rulesets.Catch.Edit +{ + /// + /// Represents a closed interval of horizontal positions in the playfield. + /// + public readonly struct PositionRange + { + public readonly float Min; + public readonly float Max; + + public PositionRange(float value) + : this(value, value) + { + } + + public PositionRange(float min, float max) + { + Min = min; + Max = max; + } + + public static PositionRange Union(PositionRange a, PositionRange b) => new PositionRange(Math.Min(a.Min, b.Min), Math.Max(a.Max, b.Max)); + + public static readonly PositionRange EMPTY = new PositionRange(float.PositiveInfinity, float.NegativeInfinity); + } +} From 4c8b9c168e4c32320aa3affdd874688663419158 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 21 Jul 2021 15:37:42 +0900 Subject: [PATCH 0563/2442] Use added position range computation in hit object move handling --- .../Edit/CatchSelectionHandler.cs | 46 ++----------------- 1 file changed, 3 insertions(+), 43 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index 7eebf04ca2..ffb342f772 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -59,20 +59,12 @@ namespace osu.Game.Rulesets.Catch.Edit /// The positional movement with the restriction applied. private float limitMovement(float deltaX, IEnumerable movingObjects) { - float minX = float.PositiveInfinity; - float maxX = float.NegativeInfinity; - - foreach (float x in movingObjects.SelectMany(getOriginalPositions)) - { - minX = Math.Min(minX, x); - maxX = Math.Max(maxX, x); - } - + var range = CatchHitObjectUtils.GetPositionRange(movingObjects); // To make an object with position `x` stay in bounds after `deltaX` movement, `0 <= x + deltaX <= WIDTH` should be satisfied. // Subtracting `x`, we get `-x <= deltaX <= WIDTH - x`. // We only need to apply the inequality to extreme values of `x`. - float lowerBound = -minX; - float upperBound = CatchPlayfield.WIDTH - maxX; + float lowerBound = -range.Min; + float upperBound = CatchPlayfield.WIDTH - range.Max; // The inequality may be unsatisfiable if the objects were already out of bounds. // In that case, don't move objects at all. if (lowerBound > upperBound) @@ -80,37 +72,5 @@ namespace osu.Game.Rulesets.Catch.Edit return Math.Clamp(deltaX, lowerBound, upperBound); } - - /// - /// Enumerate X positions that should be contained in-bounds after move offset is applied. - /// - private IEnumerable getOriginalPositions(HitObject hitObject) - { - switch (hitObject) - { - case Fruit fruit: - yield return fruit.OriginalX; - - break; - - case JuiceStream juiceStream: - foreach (var nested in juiceStream.NestedHitObjects.OfType()) - { - // Even if `OriginalX` is outside the playfield, tiny droplets can be moved inside the playfield after the random offset application. - if (!(nested is TinyDroplet)) - yield return nested.OriginalX; - } - - break; - - case BananaShower _: - // A banana shower occupies the whole screen width. - // If the selection contains a banana shower, the selection cannot be moved horizontally. - yield return 0; - yield return CatchPlayfield.WIDTH; - - break; - } - } } } From d2d3214d47958703be35f6117b15bfd1541d2a1d Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 21 Jul 2021 15:47:16 +0900 Subject: [PATCH 0564/2442] Implement horizontal flipping of hit objects in catch editor --- .../Edit/CatchSelectionHandler.cs | 43 +++++++++++++++++++ osu.Game.Rulesets.Catch/Edit/PositionRange.cs | 2 + 2 files changed, 45 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index ffb342f772..50290ae292 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -12,6 +12,7 @@ using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Screens.Edit.Compose.Components; using osuTK; +using Direction = osu.Framework.Graphics.Direction; namespace osu.Game.Rulesets.Catch.Edit { @@ -51,6 +52,26 @@ namespace osu.Game.Rulesets.Catch.Edit return true; } + public override bool HandleFlip(Direction direction) + { + var selectionRange = CatchHitObjectUtils.GetPositionRange(EditorBeatmap.SelectedHitObjects); + + bool changed = false; + EditorBeatmap.PerformOnSelection(h => + { + if (h is CatchHitObject hitObject) + changed |= handleFlip(selectionRange, hitObject); + }); + return changed; + } + + protected override void OnSelectionChanged() + { + base.OnSelectionChanged(); + + SelectionBox.CanFlipX = true; + } + /// /// Limit positional movement of the objects by the constraint that moved objects should stay in bounds. /// @@ -72,5 +93,27 @@ namespace osu.Game.Rulesets.Catch.Edit return Math.Clamp(deltaX, lowerBound, upperBound); } + + private bool handleFlip(PositionRange selectionRange, CatchHitObject hitObject) + { + switch (hitObject) + { + case BananaShower _: + return false; + + case JuiceStream juiceStream: + juiceStream.OriginalX = selectionRange.GetFlippedPosition(juiceStream.OriginalX); + + foreach (var point in juiceStream.Path.ControlPoints) + point.Position.Value *= new Vector2(-1, 1); + + EditorBeatmap.Update(juiceStream); + return true; + + default: + hitObject.OriginalX = selectionRange.GetFlippedPosition(hitObject.OriginalX); + return true; + } + } } } diff --git a/osu.Game.Rulesets.Catch/Edit/PositionRange.cs b/osu.Game.Rulesets.Catch/Edit/PositionRange.cs index 6ed1ac2c06..64e789483a 100644 --- a/osu.Game.Rulesets.Catch/Edit/PositionRange.cs +++ b/osu.Game.Rulesets.Catch/Edit/PositionRange.cs @@ -28,6 +28,8 @@ namespace osu.Game.Rulesets.Catch.Edit public static PositionRange Union(PositionRange a, PositionRange b) => new PositionRange(Math.Min(a.Min, b.Min), Math.Max(a.Max, b.Max)); + public float GetFlippedPosition(float x) => Max - (x - Min); + public static readonly PositionRange EMPTY = new PositionRange(float.PositiveInfinity, float.NegativeInfinity); } } From 1bac471b49bf9cc34154a3e182c5945ba6284d77 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 20 Jul 2021 14:23:34 +0900 Subject: [PATCH 0565/2442] Adapt to `PlatformAction` type change --- .../Components/PathControlPointVisualiser.cs | 4 ++-- .../Visual/Online/TestSceneChatOverlay.cs | 10 +++++----- osu.Game/Graphics/UserInterface/SearchTextBox.cs | 15 +++++---------- osu.Game/Overlays/ChatOverlay.cs | 8 ++++---- .../Edit/Compose/Components/BlueprintContainer.cs | 4 ++-- .../Edit/Compose/Components/SelectionHandler.cs | 4 ++-- osu.Game/Screens/Edit/Compose/ComposeScreen.cs | 2 +- osu.Game/Screens/Edit/Editor.cs | 14 +++++++------- 8 files changed, 28 insertions(+), 33 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index c36768baba..5bbdf9688f 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -129,9 +129,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components public bool OnPressed(PlatformAction action) { - switch (action.ActionMethod) + switch (action) { - case PlatformActionMethod.Delete: + case PlatformAction.Delete: return DeleteSelected(); } diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index a1549dfbce..5e234bdacf 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -330,15 +330,15 @@ namespace osu.Game.Tests.Visual.Online InputManager.ReleaseKey(Key.AltLeft); } - private void pressCloseDocumentKeys() => pressKeysFor(PlatformActionType.DocumentClose); + private void pressCloseDocumentKeys() => pressKeysFor(PlatformAction.DocumentClose); - private void pressNewTabKeys() => pressKeysFor(PlatformActionType.TabNew); + private void pressNewTabKeys() => pressKeysFor(PlatformAction.TabNew); - private void pressRestoreTabKeys() => pressKeysFor(PlatformActionType.TabRestore); + private void pressRestoreTabKeys() => pressKeysFor(PlatformAction.TabRestore); - private void pressKeysFor(PlatformActionType type) + private void pressKeysFor(PlatformAction type) { - var binding = host.PlatformKeyBindings.First(b => ((PlatformAction)b.Action).ActionType == type); + var binding = host.PlatformKeyBindings.First(b => (PlatformAction)b.Action == type); foreach (var k in binding.KeyCombination.Keys) InputManager.PressKey((Key)k); diff --git a/osu.Game/Graphics/UserInterface/SearchTextBox.cs b/osu.Game/Graphics/UserInterface/SearchTextBox.cs index fe92054d25..4a91741ce6 100644 --- a/osu.Game/Graphics/UserInterface/SearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/SearchTextBox.cs @@ -32,20 +32,15 @@ namespace osu.Game.Graphics.UserInterface public override bool OnPressed(PlatformAction action) { - switch (action.ActionType) + switch (action) { - case PlatformActionType.LineEnd: - case PlatformActionType.LineStart: - return false; - + case PlatformAction.MoveBackwardLine: + case PlatformAction.MoveForwardLine: // Shift+delete is handled via PlatformAction on macOS. this is not so useful in the context of a SearchTextBox // as we do not allow arrow key navigation in the first place (ie. the caret should always be at the end of text) // Avoid handling it here to allow other components to potentially consume the shortcut. - case PlatformActionType.CharNext: - if (action.ActionMethod == PlatformActionMethod.Delete) - return false; - - break; + case PlatformAction.DeleteForwardChar: + return false; } return base.OnPressed(action); diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 0aa6108815..0445c63eb4 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -374,17 +374,17 @@ namespace osu.Game.Overlays public bool OnPressed(PlatformAction action) { - switch (action.ActionType) + switch (action) { - case PlatformActionType.TabNew: + case PlatformAction.TabNew: ChannelTabControl.SelectChannelSelectorTab(); return true; - case PlatformActionType.TabRestore: + case PlatformAction.TabRestore: channelManager.JoinLastClosedChannel(); return true; - case PlatformActionType.DocumentClose: + case PlatformAction.DocumentClose: channelManager.LeaveChannel(channelManager.CurrentChannel.Value); return true; } diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 185f029d14..0432cdffc0 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -230,9 +230,9 @@ namespace osu.Game.Screens.Edit.Compose.Components public bool OnPressed(PlatformAction action) { - switch (action.ActionType) + switch (action) { - case PlatformActionType.SelectAll: + case PlatformAction.SelectAll: SelectAll(); return true; } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 8939be925a..1d1d95890f 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -139,9 +139,9 @@ namespace osu.Game.Screens.Edit.Compose.Components public bool OnPressed(PlatformAction action) { - switch (action.ActionMethod) + switch (action) { - case PlatformActionMethod.Delete: + case PlatformAction.Delete: DeleteSelected(); return true; } diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs index b56f9bee14..4a1f1196a9 100644 --- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs +++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs @@ -80,7 +80,7 @@ namespace osu.Game.Screens.Edit.Compose public bool OnPressed(PlatformAction action) { - if (action.ActionType == PlatformActionType.Copy) + if (action == PlatformAction.Copy) host.GetClipboard().SetText(formatSelectionAsString()); return false; diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 71dd47b058..b6dc97a7f6 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -330,29 +330,29 @@ namespace osu.Game.Screens.Edit public bool OnPressed(PlatformAction action) { - switch (action.ActionType) + switch (action) { - case PlatformActionType.Cut: + case PlatformAction.Cut: Cut(); return true; - case PlatformActionType.Copy: + case PlatformAction.Copy: Copy(); return true; - case PlatformActionType.Paste: + case PlatformAction.Paste: Paste(); return true; - case PlatformActionType.Undo: + case PlatformAction.Undo: Undo(); return true; - case PlatformActionType.Redo: + case PlatformAction.Redo: Redo(); return true; - case PlatformActionType.Save: + case PlatformAction.Save: Save(); return true; } From da3499f10edc72ab2170d9fc688d028aadc0b1a8 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 21 Jul 2021 16:27:44 +0900 Subject: [PATCH 0566/2442] Simplify the way of getting catcher --- osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs index db09b2bc6b..163fee49fb 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Tests // this needs to be done within the frame stable context due to how quickly hyperdash state changes occur. Player.DrawableRuleset.FrameStableComponents.OnUpdate += d => { - var catcher = Player.ChildrenOfType().FirstOrDefault()?.MovableCatcher; + var catcher = Player.ChildrenOfType().FirstOrDefault(); if (catcher == null) return; From 94678064ff3a8b8fa56907c06ce50e39489bc8f8 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 21 Jul 2021 16:28:31 +0900 Subject: [PATCH 0567/2442] Rename `CatcherArea.MovableCatcher` to `Catcher` --- .../TestSceneCatcherArea.cs | 6 ++--- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 2 +- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 26 +++++++++---------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs index 5a2e5b60f5..6a518cf0ef 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs @@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Catch.Tests { area.OnNewResult(drawable, new CatchJudgementResult(fruit, new CatchJudgement()) { - Type = area.MovableCatcher.CanCatch(fruit) ? HitResult.Great : HitResult.Miss + Type = area.Catcher.CanCatch(fruit) ? HitResult.Great : HitResult.Miss }); drawable.Expire(); @@ -125,13 +125,13 @@ namespace osu.Game.Rulesets.Catch.Tests Add(droppedObjectContainer); - MovableCatcher = new Catcher(this, droppedObjectContainer, beatmapDifficulty) + Catcher = new Catcher(this, droppedObjectContainer, beatmapDifficulty) { X = CatchPlayfield.CENTER_X }; } - public void ToggleHyperDash(bool status) => MovableCatcher.SetHyperDashState(status ? 2 : 1); + public void ToggleHyperDash(bool status) => Catcher.SetHyperDashState(status ? 2 : 1); } } } diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 6f84793744..b43815a8bd 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Catch.UI { Anchor = Anchor.BottomLeft, Origin = Anchor.TopLeft, - MovableCatcher = Catcher, + Catcher = Catcher, }, trailContainer, HitObjectContainer, diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index a9d0275678..f2c5f96b26 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Catch.UI { public const float CATCHER_SIZE = 106.75f; - public Catcher MovableCatcher + public Catcher Catcher { get => catcher; set @@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Catch.UI public void OnNewResult(DrawableCatchHitObject hitObject, JudgementResult result) { - MovableCatcher.OnNewResult(hitObject, result); + Catcher.OnNewResult(hitObject, result); if (!result.Type.IsScorable()) return; @@ -66,9 +66,9 @@ namespace osu.Game.Rulesets.Catch.UI if (hitObject.HitObject.LastInCombo) { if (result.Judgement is CatchJudgement catchJudgement && catchJudgement.ShouldExplodeFor(result)) - MovableCatcher.Explode(); + Catcher.Explode(); else - MovableCatcher.Drop(); + Catcher.Drop(); } comboDisplay.OnNewResult(hitObject, result); @@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Catch.UI public void OnRevertResult(DrawableCatchHitObject hitObject, JudgementResult result) { comboDisplay.OnRevertResult(hitObject, result); - MovableCatcher.OnRevertResult(hitObject, result); + Catcher.OnRevertResult(hitObject, result); } protected override void Update() @@ -88,27 +88,27 @@ namespace osu.Game.Rulesets.Catch.UI SetCatcherPosition( replayState?.CatcherX ?? - (float)(MovableCatcher.X + MovableCatcher.Speed * currentDirection * Clock.ElapsedFrameTime)); + (float)(Catcher.X + Catcher.Speed * currentDirection * Clock.ElapsedFrameTime)); } protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); - comboDisplay.X = MovableCatcher.X; + comboDisplay.X = Catcher.X; } public void SetCatcherPosition(float X) { - float lastPosition = MovableCatcher.X; + float lastPosition = Catcher.X; float newPosition = Math.Clamp(X, 0, CatchPlayfield.WIDTH); - MovableCatcher.X = newPosition; + Catcher.X = newPosition; if (lastPosition < newPosition) - MovableCatcher.VisualDirection = Direction.Right; + Catcher.VisualDirection = Direction.Right; else if (lastPosition > newPosition) - MovableCatcher.VisualDirection = Direction.Left; + Catcher.VisualDirection = Direction.Left; } public bool OnPressed(CatchAction action) @@ -124,7 +124,7 @@ namespace osu.Game.Rulesets.Catch.UI return true; case CatchAction.Dash: - MovableCatcher.Dashing = true; + Catcher.Dashing = true; return true; } @@ -144,7 +144,7 @@ namespace osu.Game.Rulesets.Catch.UI break; case CatchAction.Dash: - MovableCatcher.Dashing = false; + Catcher.Dashing = false; break; } } From a63065dbe9818115153a27fabeac958d4688dae6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Jul 2021 15:55:51 +0900 Subject: [PATCH 0568/2442] Tidy up `ModDisplay` --- .../UserInterface/TestSceneModDisplay.cs | 6 +-- .../TestSceneModSelectOverlay.cs | 1 - .../Screens/Play/BeatmapMetadataDisplay.cs | 1 - osu.Game/Screens/Play/HUD/ModDisplay.cs | 53 ++++++++----------- osu.Game/Screens/Play/HUDOverlay.cs | 1 - 5 files changed, 24 insertions(+), 38 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModDisplay.cs index 8168faa106..b8f5ee5e86 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModDisplay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModDisplay.cs @@ -11,10 +11,8 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneModDisplay : OsuTestScene { - [TestCase(ExpansionMode.ExpandOnHover)] - [TestCase(ExpansionMode.AlwaysExpanded)] - [TestCase(ExpansionMode.AlwaysContracted)] - public void TestMode(ExpansionMode mode) + [Test] + public void TestMode([Values] ExpansionMode mode) { AddStep("create mod display", () => { diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 3485d7fbc3..1e76c33fca 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -416,7 +416,6 @@ namespace osu.Game.Tests.Visual.UserInterface { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, Position = new Vector2(-5, 25), Current = { BindTarget = modSelect.SelectedMods } } diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index fd1150650c..4265a83ce1 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -172,7 +172,6 @@ namespace osu.Game.Screens.Play { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Both, Margin = new MarginPadding { Top = 20 }, Current = mods }, diff --git a/osu.Game/Screens/Play/HUD/ModDisplay.cs b/osu.Game/Screens/Play/HUD/ModDisplay.cs index 2f7ca74372..36127292c3 100644 --- a/osu.Game/Screens/Play/HUD/ModDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ModDisplay.cs @@ -15,24 +15,26 @@ using osuTK; namespace osu.Game.Screens.Play.HUD { - public class ModDisplay : Container, IHasCurrentValue> + /// + /// Displays a single-line horizontal auto-sized flow of mods. + /// + public class ModDisplay : CompositeDrawable, IHasCurrentValue> { private const int fade_duration = 1000; public ExpansionMode ExpansionMode = ExpansionMode.ExpandOnHover; - private readonly Bindable> current = new Bindable>(); + private readonly BindableWithCurrent> current = new BindableWithCurrent>(); public Bindable> Current { - get => current; + get => current.Current; set { if (value == null) throw new ArgumentNullException(nameof(value)); - current.UnbindBindings(); - current.BindTo(value); + current.Current = value; } } @@ -42,22 +44,10 @@ namespace osu.Game.Screens.Play.HUD { AutoSizeAxes = Axes.Both; - Child = new FillFlowContainer + InternalChild = iconsContainer = new ReverseChildIDFillFlowContainer { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - iconsContainer = new ReverseChildIDFillFlowContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - }, - }, + Direction = FillDirection.Horizontal, }; } @@ -71,22 +61,23 @@ namespace osu.Game.Screens.Play.HUD { base.LoadComplete(); - Current.BindValueChanged(mods => - { - iconsContainer.Clear(); - - if (mods.NewValue != null) - { - foreach (Mod mod in mods.NewValue) - iconsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.6f) }); - - appearTransform(); - } - }, true); + Current.BindValueChanged(updateDisplay, true); iconsContainer.FadeInFromZero(fade_duration, Easing.OutQuint); } + private void updateDisplay(ValueChangedEvent> mods) + { + iconsContainer.Clear(); + + if (mods.NewValue == null) return; + + foreach (Mod mod in mods.NewValue) + iconsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.6f) }); + + appearTransform(); + } + private void appearTransform() { expand(); diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index ffe03815f5..2cf2555b3e 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -282,7 +282,6 @@ namespace osu.Game.Screens.Play { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, }; protected PlayerSettingsOverlay CreatePlayerSettingsOverlay() => new PlayerSettingsOverlay(); From 30777795ce2caf73a9a4c8c3da19b79b5f589112 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 21 Jul 2021 16:40:35 +0900 Subject: [PATCH 0569/2442] Add some doc comment to `CatcherArea` --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index f2c5f96b26..bc0c101805 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -15,6 +15,11 @@ using osuTK; namespace osu.Game.Rulesets.Catch.UI { + /// + /// The horizontal band at the bottom of the playfield the catcher is moving on. + /// It holds a as a child and translates input to the catcher movement. + /// It also holds a combo display that is above the catcher, and judgment results are translated to the catcher and the combo display. + /// public class CatcherArea : Container, IKeyBindingHandler { public const float CATCHER_SIZE = 106.75f; @@ -42,6 +47,9 @@ namespace osu.Game.Rulesets.Catch.UI /// private int currentDirection; + /// + /// must be set before loading. + /// public CatcherArea() { Size = new Vector2(CatchPlayfield.WIDTH, CATCHER_SIZE); From 396ad79d5040a01bec292d034dcf92a0e05e9441 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Jul 2021 16:43:08 +0900 Subject: [PATCH 0570/2442] Remove unnecessary `UnbindAll` call --- osu.Game/Screens/Play/HUD/ModDisplay.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ModDisplay.cs b/osu.Game/Screens/Play/HUD/ModDisplay.cs index 36127292c3..2cf24a18dc 100644 --- a/osu.Game/Screens/Play/HUD/ModDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ModDisplay.cs @@ -51,12 +51,6 @@ namespace osu.Game.Screens.Play.HUD }; } - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - Current.UnbindAll(); - } - protected override void LoadComplete() { base.LoadComplete(); From d9f9ad35be19c7677ce32e49d68c5f1f1bb13fde Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 21 Jul 2021 16:43:24 +0900 Subject: [PATCH 0571/2442] Move catcher base size constant to `Catcher`. --- .../Replays/CatchAutoGenerator.cs | 2 +- osu.Game.Rulesets.Catch/UI/Catcher.cs | 19 ++++++++++++------- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 4 +--- osu.Game.Rulesets.Catch/UI/CatcherTrail.cs | 2 +- .../UI/SkinnableCatcher.cs | 2 +- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index a81703119a..2fc05701db 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Catch.Replays bool impossibleJump = speedRequired > movement_speed * 2; // todo: get correct catcher size, based on difficulty CS. - const float catcher_width_half = CatcherArea.CATCHER_SIZE * 0.3f * 0.5f; + const float catcher_width_half = Catcher.BASE_SIZE * 0.3f * 0.5f; if (lastPosition - catcher_width_half < h.EffectiveX && lastPosition + catcher_width_half > h.EffectiveX) { diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index a671bce590..49508b1caf 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -25,6 +25,16 @@ namespace osu.Game.Rulesets.Catch.UI { public class Catcher : SkinReloadableDrawable { + /// + /// The size of the catcher at 1x scale. + /// + public const float BASE_SIZE = 106.75f; + + /// + /// The width of the catcher which can receive fruit. Equivalent to "catchMargin" in osu-stable. + /// + public const float ALLOWED_CATCH_RANGE = 0.8f; + /// /// The default colour used to tint hyper-dash fruit, along with the moving catcher, its trail /// and end glow/after-image during a hyper-dash. @@ -82,11 +92,6 @@ namespace osu.Game.Rulesets.Catch.UI private set => Body.AnimationState.Value = value; } - /// - /// The width of the catcher which can receive fruit. Equivalent to "catchMargin" in osu-stable. - /// - public const float ALLOWED_CATCH_RANGE = 0.8f; - private bool dashing; public bool Dashing @@ -140,7 +145,7 @@ namespace osu.Game.Rulesets.Catch.UI Origin = Anchor.TopCentre; - Size = new Vector2(CatcherArea.CATCHER_SIZE); + Size = new Vector2(BASE_SIZE); if (difficulty != null) Scale = calculateScale(difficulty); @@ -197,7 +202,7 @@ namespace osu.Game.Rulesets.Catch.UI /// Calculates the width of the area used for attempting catches in gameplay. /// /// The scale of the catcher. - public static float CalculateCatchWidth(Vector2 scale) => CatcherArea.CATCHER_SIZE * Math.Abs(scale.X) * ALLOWED_CATCH_RANGE; + public static float CalculateCatchWidth(Vector2 scale) => BASE_SIZE * Math.Abs(scale.X) * ALLOWED_CATCH_RANGE; /// /// Calculates the width of the area used for attempting catches in gameplay. diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index bc0c101805..de0ace9817 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -22,8 +22,6 @@ namespace osu.Game.Rulesets.Catch.UI /// public class CatcherArea : Container, IKeyBindingHandler { - public const float CATCHER_SIZE = 106.75f; - public Catcher Catcher { get => catcher; @@ -52,7 +50,7 @@ namespace osu.Game.Rulesets.Catch.UI /// public CatcherArea() { - Size = new Vector2(CatchPlayfield.WIDTH, CATCHER_SIZE); + Size = new Vector2(CatchPlayfield.WIDTH, Catcher.BASE_SIZE); Child = comboDisplay = new CatchComboDisplay { RelativeSizeAxes = Axes.None, diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs index c961d98dc5..ff1a7d7a61 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Catch.UI public CatcherTrail() { - Size = new Vector2(CatcherArea.CATCHER_SIZE); + Size = new Vector2(Catcher.BASE_SIZE); Origin = Anchor.TopCentre; Blending = BlendingParameters.Additive; InternalChild = body = new SkinnableCatcher diff --git a/osu.Game.Rulesets.Catch/UI/SkinnableCatcher.cs b/osu.Game.Rulesets.Catch/UI/SkinnableCatcher.cs index fc34ba4c8b..8d707a4beb 100644 --- a/osu.Game.Rulesets.Catch/UI/SkinnableCatcher.cs +++ b/osu.Game.Rulesets.Catch/UI/SkinnableCatcher.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Catch.UI { Anchor = Anchor.TopCentre; // Sets the origin roughly to the centre of the catcher's plate to allow for correct scaling. - OriginPosition = new Vector2(0.5f, 0.06f) * CatcherArea.CATCHER_SIZE; + OriginPosition = new Vector2(0.5f, 0.06f) * Catcher.BASE_SIZE; } } } From 5bee06fd5f3c0ecb1c321c4b02ad42b1b62ece9d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Jul 2021 16:43:29 +0900 Subject: [PATCH 0572/2442] Remove forgotten `AutoSize` specification --- .../Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs index 7e8dcdcfe0..3d67df86e4 100644 --- a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs @@ -135,7 +135,6 @@ namespace osu.Game.Screens.Ranking.Contracted { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - AutoSizeAxes = Axes.Both, ExpansionMode = ExpansionMode.AlwaysExpanded, Current = { Value = score.Mods }, Scale = new Vector2(0.5f), From b65e607941d14432934f561260ff7eedeeefa24e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Jul 2021 15:35:59 +0900 Subject: [PATCH 0573/2442] Add test covering mod displays with too many mods --- .../Ranking/TestSceneContractedPanelMiddleContent.cs | 6 ++++++ .../Ranking/TestSceneExpandedPanelMiddleContent.cs | 11 +++++++++++ osu.Game/Tests/TestScoreInfo.cs | 8 ++++++-- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs index 76cfe75b59..acacdf8644 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneContractedPanelMiddleContent.cs @@ -29,6 +29,12 @@ namespace osu.Game.Tests.Visual.Ranking AddStep("show example score", () => showPanel(CreateWorkingBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo)), new TestScoreInfo(new OsuRuleset().RulesetInfo))); } + [Test] + public void TestExcessMods() + { + AddStep("show excess mods score", () => showPanel(CreateWorkingBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo)), new TestScoreInfo(new OsuRuleset().RulesetInfo, true))); + } + private void showPanel(WorkingBeatmap workingBeatmap, ScoreInfo score) { Child = new ContractedPanelMiddleContentContainer(workingBeatmap, score); diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs index 591095252f..495c369eac 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs @@ -36,6 +36,17 @@ namespace osu.Game.Tests.Visual.Ranking { Beatmap = createTestBeatmap(author) })); + } + + [Test] + public void TestExcessMods() + { + var author = new User { Username = "mapper_name" }; + + AddStep("show excess mods score", () => showPanel(new TestScoreInfo(new OsuRuleset().RulesetInfo, true) + { + Beatmap = createTestBeatmap(author) + })); AddAssert("mapper name present", () => this.ChildrenOfType().Any(spriteText => spriteText.Current.Value == "mapper_name")); } diff --git a/osu.Game/Tests/TestScoreInfo.cs b/osu.Game/Tests/TestScoreInfo.cs index 9090a12d3f..8ce71ace69 100644 --- a/osu.Game/Tests/TestScoreInfo.cs +++ b/osu.Game/Tests/TestScoreInfo.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; @@ -13,7 +14,7 @@ namespace osu.Game.Tests { public class TestScoreInfo : ScoreInfo { - public TestScoreInfo(RulesetInfo ruleset) + public TestScoreInfo(RulesetInfo ruleset, bool excessMods = false) { User = new User { @@ -25,7 +26,10 @@ namespace osu.Game.Tests Beatmap = new TestBeatmap(ruleset).BeatmapInfo; Ruleset = ruleset; RulesetID = ruleset.ID ?? 0; - Mods = new Mod[] { new TestModHardRock(), new TestModDoubleTime() }; + + Mods = excessMods + ? ruleset.CreateInstance().GetAllMods().ToArray() + : new Mod[] { new TestModHardRock(), new TestModDoubleTime() }; TotalScore = 2845370; Accuracy = 0.95; From b910c212309feabfc5578aee96eee3dabc94c8a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Jul 2021 16:38:18 +0900 Subject: [PATCH 0574/2442] Add `ModFlowDisplay` and consume in `ContractedPanelMiddleContent` --- .../UserInterface/TestSceneModFlowDisplay.cs | 49 +++++++++++ osu.Game/Screens/Play/HUD/ModDisplay.cs | 2 +- osu.Game/Screens/Play/HUD/ModFlowDisplay.cs | 83 +++++++++++++++++++ .../ContractedPanelMiddleContent.cs | 7 +- 4 files changed, 137 insertions(+), 4 deletions(-) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneModFlowDisplay.cs create mode 100644 osu.Game/Screens/Play/HUD/ModFlowDisplay.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModFlowDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModFlowDisplay.cs new file mode 100644 index 0000000000..8f057c663b --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModFlowDisplay.cs @@ -0,0 +1,49 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Osu; +using osu.Game.Screens.Play.HUD; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneModFlowDisplay : OsuTestScene + { + private ModFlowDisplay modFlow; + + [SetUp] + public void SetUp() => Schedule(() => + { + Child = modFlow = new ModFlowDisplay + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.None, + Width = 200, + Current = + { + Value = new OsuRuleset().GetAllMods().ToArray(), + } + }; + }); + + [Test] + public void TestWrapping() + { + AddSliderStep("icon size", 0.1f, 2, 1, val => + { + if (modFlow != null) + modFlow.IconScale = val; + }); + + AddSliderStep("flow width", 100, 500, 200, val => + { + if (modFlow != null) + modFlow.Width = val; + }); + } + } +} diff --git a/osu.Game/Screens/Play/HUD/ModDisplay.cs b/osu.Game/Screens/Play/HUD/ModDisplay.cs index 2cf24a18dc..b4a3eb209a 100644 --- a/osu.Game/Screens/Play/HUD/ModDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ModDisplay.cs @@ -16,7 +16,7 @@ using osuTK; namespace osu.Game.Screens.Play.HUD { /// - /// Displays a single-line horizontal auto-sized flow of mods. + /// Displays a single-line horizontal auto-sized flow of mods. For cases where wrapping is required, use instead. /// public class ModDisplay : CompositeDrawable, IHasCurrentValue> { diff --git a/osu.Game/Screens/Play/HUD/ModFlowDisplay.cs b/osu.Game/Screens/Play/HUD/ModFlowDisplay.cs new file mode 100644 index 0000000000..ff3ca6460f --- /dev/null +++ b/osu.Game/Screens/Play/HUD/ModFlowDisplay.cs @@ -0,0 +1,83 @@ +// 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 osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics.Containers; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; +using osuTK; + +namespace osu.Game.Screens.Play.HUD +{ + /// + /// A horizontally wrapping display of mods. For cases where wrapping is not required, use instead. + /// + public class ModFlowDisplay : ReverseChildIDFillFlowContainer, IHasCurrentValue> + { + private const int fade_duration = 1000; + + private readonly BindableWithCurrent> current = new BindableWithCurrent>(); + + public Bindable> Current + { + get => current.Current; + set + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + current.Current = value; + } + } + + private float iconScale = 1; + + public float IconScale + { + get => iconScale; + set + { + iconScale = value; + updateDisplay(); + } + } + + public ModFlowDisplay() + { + Direction = FillDirection.Full; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Current.BindValueChanged(_ => updateDisplay(), true); + + this.FadeInFromZero(fade_duration, Easing.OutQuint); + } + + private void updateDisplay() + { + Clear(); + + if (current.Value == null) return; + + Spacing = new Vector2(0, -12 * iconScale); + + foreach (Mod mod in current.Value) + { + Add(new ModIcon(mod) + { + Scale = new Vector2(0.6f * iconScale), + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }); + } + } + } +} diff --git a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs index 3d67df86e4..20c603295b 100644 --- a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs @@ -131,13 +131,14 @@ namespace osu.Game.Screens.Ranking.Contracted createStatistic("Accuracy", $"{score.Accuracy.FormatAccuracy()}"), } }, - new ModDisplay + new ModFlowDisplay { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - ExpansionMode = ExpansionMode.AlwaysExpanded, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, Current = { Value = score.Mods }, - Scale = new Vector2(0.5f), + IconScale = 0.5f, } } } From 173334383f0cf60ed068db0067b67606f7f510a2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Jul 2021 16:56:09 +0900 Subject: [PATCH 0575/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 171a0862a1..da7a8209a4 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 152ba55e08..0e6cee8f18 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index dc15df6ea6..aa4bec06e3 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 399c3b0be88d2c30ce88b5fb0f17c7eb6c69644b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Jul 2021 17:32:56 +0900 Subject: [PATCH 0576/2442] Rename property, reword xmldoc and improve readability of update code --- .../Objects/CatchHitObject.cs | 2 +- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 2 +- .../Formats/LegacyBeatmapDecoderTest.cs | 24 +++++++++---------- .../TestSceneHitObjectAccentColour.cs | 2 +- osu.Game/Beatmaps/BeatmapProcessor.cs | 14 +++++------ .../Objects/Types/IHasComboInformation.cs | 6 ++--- osu.Game/Skinning/LegacyBeatmapSkin.cs | 2 +- 7 files changed, 25 insertions(+), 27 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index bad558fcad..33353ec423 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Catch.Objects set => ComboIndexBindable.Value = value; } - public int BeatmapSkinComboIndex { get; set; } + public int ComboIndexWithOffsets { get; set; } public Bindable LastInComboBindable { get; } = new Bindable(); diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index db8a02aa0f..7c60480b00 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Osu.Objects set => ComboIndexBindable.Value = value; } - public int BeatmapSkinComboIndex { get; set; } + public int ComboIndexWithOffsets { get; set; } public Bindable LastInComboBindable { get; } = new Bindable(); diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 42f89a758e..4fe1cf3790 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -323,12 +323,12 @@ namespace osu.Game.Tests.Beatmaps.Formats new OsuBeatmapProcessor(converted).PreProcess(); new OsuBeatmapProcessor(converted).PostProcess(); - Assert.AreEqual(4, ((IHasComboInformation)converted.HitObjects.ElementAt(0)).BeatmapSkinComboIndex); - Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(2)).BeatmapSkinComboIndex); - Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(4)).BeatmapSkinComboIndex); - Assert.AreEqual(6, ((IHasComboInformation)converted.HitObjects.ElementAt(6)).BeatmapSkinComboIndex); - Assert.AreEqual(11, ((IHasComboInformation)converted.HitObjects.ElementAt(8)).BeatmapSkinComboIndex); - Assert.AreEqual(14, ((IHasComboInformation)converted.HitObjects.ElementAt(11)).BeatmapSkinComboIndex); + Assert.AreEqual(4, ((IHasComboInformation)converted.HitObjects.ElementAt(0)).ComboIndexWithOffsets); + Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(2)).ComboIndexWithOffsets); + Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(4)).ComboIndexWithOffsets); + Assert.AreEqual(6, ((IHasComboInformation)converted.HitObjects.ElementAt(6)).ComboIndexWithOffsets); + Assert.AreEqual(11, ((IHasComboInformation)converted.HitObjects.ElementAt(8)).ComboIndexWithOffsets); + Assert.AreEqual(14, ((IHasComboInformation)converted.HitObjects.ElementAt(11)).ComboIndexWithOffsets); } } @@ -346,12 +346,12 @@ namespace osu.Game.Tests.Beatmaps.Formats new CatchBeatmapProcessor(converted).PreProcess(); new CatchBeatmapProcessor(converted).PostProcess(); - Assert.AreEqual(4, ((IHasComboInformation)converted.HitObjects.ElementAt(0)).BeatmapSkinComboIndex); - Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(2)).BeatmapSkinComboIndex); - Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(4)).BeatmapSkinComboIndex); - Assert.AreEqual(6, ((IHasComboInformation)converted.HitObjects.ElementAt(6)).BeatmapSkinComboIndex); - Assert.AreEqual(11, ((IHasComboInformation)converted.HitObjects.ElementAt(8)).BeatmapSkinComboIndex); - Assert.AreEqual(14, ((IHasComboInformation)converted.HitObjects.ElementAt(11)).BeatmapSkinComboIndex); + Assert.AreEqual(4, ((IHasComboInformation)converted.HitObjects.ElementAt(0)).ComboIndexWithOffsets); + Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(2)).ComboIndexWithOffsets); + Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(4)).ComboIndexWithOffsets); + Assert.AreEqual(6, ((IHasComboInformation)converted.HitObjects.ElementAt(6)).ComboIndexWithOffsets); + Assert.AreEqual(11, ((IHasComboInformation)converted.HitObjects.ElementAt(8)).ComboIndexWithOffsets); + Assert.AreEqual(14, ((IHasComboInformation)converted.HitObjects.ElementAt(11)).ComboIndexWithOffsets); } } diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs index f6f482e254..9d53a5ba63 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs @@ -100,7 +100,7 @@ namespace osu.Game.Tests.Gameplay set => ComboIndexBindable.Value = value; } - public int BeatmapSkinComboIndex { get; set; } + public int ComboIndexWithOffsets { get; set; } public Bindable LastInComboBindable { get; } = new Bindable(); diff --git a/osu.Game/Beatmaps/BeatmapProcessor.cs b/osu.Game/Beatmaps/BeatmapProcessor.cs index 9121222bd7..cdeaab06ed 100644 --- a/osu.Game/Beatmaps/BeatmapProcessor.cs +++ b/osu.Game/Beatmaps/BeatmapProcessor.cs @@ -34,21 +34,19 @@ namespace osu.Game.Beatmaps isFirst = false; } + obj.ComboIndex = lastObj?.ComboIndex ?? 0; + obj.ComboIndexWithOffsets = lastObj?.ComboIndexWithOffsets ?? 0; + obj.IndexInCurrentCombo = (lastObj?.IndexInCurrentCombo + 1) ?? 0; + if (obj.NewCombo) { obj.IndexInCurrentCombo = 0; - obj.ComboIndex = (lastObj?.ComboIndex ?? 0) + 1; - obj.BeatmapSkinComboIndex = (lastObj?.BeatmapSkinComboIndex ?? 0) + obj.ComboOffset + 1; + obj.ComboIndex++; + obj.ComboIndexWithOffsets += obj.ComboOffset + 1; if (lastObj != null) lastObj.LastInCombo = true; } - else if (lastObj != null) - { - obj.IndexInCurrentCombo = lastObj.IndexInCurrentCombo + 1; - obj.ComboIndex = lastObj.ComboIndex; - obj.BeatmapSkinComboIndex = lastObj.BeatmapSkinComboIndex; - } lastObj = obj; } diff --git a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs index 46d7bef498..f01181f436 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs @@ -27,10 +27,10 @@ namespace osu.Game.Rulesets.Objects.Types int ComboIndex { get; set; } /// - /// A with the of this and all previous hitobjects applied to it. - /// This is used primarily for beatmap skins during combo colour retrieval, rather than the regular . + /// The offset of this combo in relation to the beatmap, with all aggregate s applied. + /// This should be used instead of only when retrieving combo colours from the beatmap's skin. /// - int BeatmapSkinComboIndex { get; set; } + int ComboIndexWithOffsets { get; set; } /// /// Whether the HitObject starts a new combo. diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 5b46a21381..e6ddeba316 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -63,7 +63,7 @@ namespace osu.Game.Skinning } protected override IBindable GetComboColour(IHasComboColours source, int comboIndex, IHasComboInformation combo) - => base.GetComboColour(source, combo.BeatmapSkinComboIndex, combo); + => base.GetComboColour(source, combo.ComboIndexWithOffsets, combo); public override ISample GetSample(ISampleInfo sampleInfo) { From 0a950a5da74e94216440fb235e2019e20e278b33 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Jul 2021 17:59:50 +0900 Subject: [PATCH 0577/2442] Disable auto property get-only inspection --- osu.sln.DotSettings | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 7284ca1a9a..139ee02b96 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -19,8 +19,8 @@ HINT DO_NOT_SHOW WARNING - WARNING - WARNING + HINT + HINT WARNING WARNING WARNING From f85ff40a6b3eddcc4e9a0ae21853eed059759616 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Wed, 21 Jul 2021 11:47:21 +0200 Subject: [PATCH 0578/2442] Add back LeftMouse button check --- osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 06cbf7c2e0..a24084aa05 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -112,7 +112,7 @@ namespace osu.Game.Screens.Edit.Compose.Components // even if a selection didn't occur, a drag event may still move the selection. bool movementPossible = prepareSelectionMovement(); - return selectionPerformed || movementPossible; + return selectionPerformed || (e.Button == MouseButton.Left && movementPossible); } protected SelectionBlueprint ClickedBlueprint { get; private set; } From 9d43ca122fc52372d0c50ebe84789095def45d6b Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Wed, 21 Jul 2021 12:04:09 +0200 Subject: [PATCH 0579/2442] Allow context menus to be triggered as well --- .../Components/Timeline/TimelineBlueprintContainer.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index a5210132a3..354311b7ff 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -238,6 +238,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline internal class TimelineSelectionHandler : EditorSelectionHandler, IKeyBindingHandler { + [Resolved] + private Timeline timeline { get; set; } + + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => timeline.ScreenSpaceDrawQuad.Contains(screenSpacePos); + // for now we always allow movement. snapping is provided by the Timeline's "distance" snap implementation public override bool HandleMovement(MoveSelectionEvent moveEvent) => true; From 032ced5d168075c427c4adf7bc8559f45a0aec02 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 21 Jul 2021 13:05:01 +0200 Subject: [PATCH 0580/2442] Localise beatmap explicit content pill. --- osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs b/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs index 329f8ee0a2..ba78592ed2 100644 --- a/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs +++ b/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs @@ -2,11 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapSet { @@ -34,7 +36,7 @@ namespace osu.Game.Overlays.BeatmapSet new OsuSpriteText { Margin = new MarginPadding { Horizontal = 10f, Vertical = 2f }, - Text = "EXPLICIT", + Text = BeatmapsetsStrings.NsfwBadgeLabel.ToUpper(), Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), Colour = OverlayColourProvider.Orange.Colour2, } From ea4f9b2ac772c7fd26d9075210dd5ce0502a26be Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 21 Jul 2021 13:15:07 +0200 Subject: [PATCH 0581/2442] Localise beatmap online status pill. --- osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs | 41 +++++++++++++++++++ .../Drawables/BeatmapSetOnlineStatusPill.cs | 4 +- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs b/osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs index ae5a44cfcd..6003e23a84 100644 --- a/osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs +++ b/osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs @@ -1,8 +1,13 @@ // 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 osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; + namespace osu.Game.Beatmaps { + [LocalisableEnum(typeof(BeatmapSetOnlineStatusEnumLocalisationMapper))] public enum BeatmapSetOnlineStatus { None = -3, @@ -20,4 +25,40 @@ namespace osu.Game.Beatmaps public static bool GrantsPerformancePoints(this BeatmapSetOnlineStatus status) => status == BeatmapSetOnlineStatus.Ranked || status == BeatmapSetOnlineStatus.Approved; } + + public class BeatmapSetOnlineStatusEnumLocalisationMapper : EnumLocalisationMapper + { + public override LocalisableString Map(BeatmapSetOnlineStatus value) + { + switch (value) + { + case BeatmapSetOnlineStatus.None: + return string.Empty; + + case BeatmapSetOnlineStatus.Graveyard: + return BeatmapsetsStrings.ShowStatusGraveyard; + + case BeatmapSetOnlineStatus.WIP: + return BeatmapsetsStrings.ShowStatusWip; + + case BeatmapSetOnlineStatus.Pending: + return BeatmapsetsStrings.ShowStatusPending; + + case BeatmapSetOnlineStatus.Ranked: + return BeatmapsetsStrings.ShowStatusRanked; + + case BeatmapSetOnlineStatus.Approved: + return BeatmapsetsStrings.ShowStatusApproved; + + case BeatmapSetOnlineStatus.Qualified: + return BeatmapsetsStrings.ShowStatusQualified; + + case BeatmapSetOnlineStatus.Loved: + return BeatmapsetsStrings.ShowStatusLoved; + + default: + throw new ArgumentOutOfRangeException(nameof(value), value, null); + } + } + } } diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs index f6e03d40ff..ffc010b3a3 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.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 osu.Framework.Extensions; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -28,7 +30,7 @@ namespace osu.Game.Beatmaps.Drawables status = value; Alpha = value == BeatmapSetOnlineStatus.None ? 0 : 1; - statusText.Text = value.ToString().ToUpperInvariant(); + statusText.Text = value.GetLocalisableDescription().ToUpper(); } } From cd54653977e4486452f3cfbb6d81dc78828a7ad5 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Wed, 21 Jul 2021 22:00:13 +0900 Subject: [PATCH 0582/2442] Add 'Soft' HoverSampleSet variant --- osu.Game/Graphics/UserInterface/HoverSampleSet.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Graphics/UserInterface/HoverSampleSet.cs b/osu.Game/Graphics/UserInterface/HoverSampleSet.cs index b88f81a143..b4afb4831f 100644 --- a/osu.Game/Graphics/UserInterface/HoverSampleSet.cs +++ b/osu.Game/Graphics/UserInterface/HoverSampleSet.cs @@ -10,6 +10,9 @@ namespace osu.Game.Graphics.UserInterface [Description("default")] Default, + [Description("soft")] + Soft, + [Description("button")] Button, From e3d1868af5cf49d730a26eff7cd522179a733645 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Wed, 21 Jul 2021 22:01:00 +0900 Subject: [PATCH 0583/2442] Add hover/select sounds to directory/file selector components --- .../UserInterfaceV2/OsuDirectorySelectorDirectory.cs | 9 +++++++-- osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs index 8a420cdcfb..794c728e56 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Graphics.UserInterfaceV2 { @@ -25,9 +26,13 @@ namespace osu.Game.Graphics.UserInterfaceV2 Flow.AutoSizeAxes = Axes.X; Flow.Height = OsuDirectorySelector.ITEM_HEIGHT; - AddInternal(new Background + AddRangeInternal(new Drawable[] { - Depth = 1 + new Background + { + Depth = 1 + }, + new HoverClickSounds(HoverSampleSet.Soft) }); } diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs b/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs index b9fb642cbe..e4c78e723d 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Graphics.UserInterfaceV2 { @@ -50,9 +51,13 @@ namespace osu.Game.Graphics.UserInterfaceV2 Flow.AutoSizeAxes = Axes.X; Flow.Height = OsuDirectorySelector.ITEM_HEIGHT; - AddInternal(new OsuDirectorySelectorDirectory.Background + AddRangeInternal(new Drawable[] { - Depth = 1 + new OsuDirectorySelectorDirectory.Background + { + Depth = 1 + }, + new HoverClickSounds(HoverSampleSet.Soft) }); } From 507b53dc73b5f97976c9d2289d0b3a7b2148923e Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Wed, 21 Jul 2021 22:02:40 +0900 Subject: [PATCH 0584/2442] Use 'Soft' hover/select samples for EditorTable row selection --- osu.Game/Screens/Edit/EditorTable.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Edit/EditorTable.cs b/osu.Game/Screens/Edit/EditorTable.cs index 815f3ed0ea..9578b96897 100644 --- a/osu.Game/Screens/Edit/EditorTable.cs +++ b/osu.Game/Screens/Edit/EditorTable.cs @@ -9,6 +9,7 @@ using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osuTK.Graphics; @@ -64,6 +65,7 @@ namespace osu.Game.Screens.Edit private EditorClock clock { get; set; } public RowBackground(object item) + : base(HoverSampleSet.Soft) { Item = item; From 283d953c4fc5da054b875c4c958ce75c238740fd Mon Sep 17 00:00:00 2001 From: aitani9 <55509723+aitani9@users.noreply.github.com> Date: Wed, 21 Jul 2021 14:07:00 -0700 Subject: [PATCH 0585/2442] Fix blinds moving when barrel roll mod is active --- osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | 35 +++++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs index 636cd63c69..f37058564e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -32,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Mods public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { - drawableRuleset.Overlays.Add(blinds = new DrawableOsuBlinds(drawableRuleset.Playfield.HitObjectContainer, drawableRuleset.Beatmap)); + drawableRuleset.Overlays.Add(blinds = new DrawableOsuBlinds(drawableRuleset.Playfield, drawableRuleset.Beatmap, drawableRuleset.Mods)); } public void ApplyToHealthProcessor(HealthProcessor healthProcessor) @@ -67,6 +68,8 @@ namespace osu.Game.Rulesets.Osu.Mods private readonly CompositeDrawable restrictTo; + private readonly bool barrelRollActive; + /// /// /// Percentage of playfield to extend blinds over. Basically moves the origin points where the blinds start. @@ -80,13 +83,24 @@ namespace osu.Game.Rulesets.Osu.Mods /// private const float leniency = 0.1f; - public DrawableOsuBlinds(CompositeDrawable restrictTo, Beatmap beatmap) + public DrawableOsuBlinds(CompositeDrawable restrictTo, Beatmap beatmap, IEnumerable mods) { this.restrictTo = restrictTo; this.beatmap = beatmap; targetBreakMultiplier = 0; easing = 1; + + barrelRollActive = false; + + foreach (Mod mod in mods) + { + if (mod is OsuModBarrelRoll) + { + barrelRollActive = true; + break; + } + } } [BackgroundDependencyLoader] @@ -128,8 +142,21 @@ namespace osu.Game.Rulesets.Osu.Mods protected override void Update() { - float start = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopLeft).X; - float end = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopRight).X; + float start, end; + + if (barrelRollActive) + { + float origin = restrictTo.ToSpaceOfOtherDrawable(restrictTo.OriginPosition, Parent).X; + float halfDiagonal = MathF.Sqrt(MathF.Pow(restrictTo.DrawWidth / 2, 2) + MathF.Pow(restrictTo.DrawHeight / 2, 2)); + + start = origin - halfDiagonal; + end = origin + halfDiagonal; + } + else + { + start = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopLeft).X; + end = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopRight).X; + } float rawWidth = end - start; From d93bf5be8023db9d68b9dc3822ca93eb36dbcdad Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 22 Jul 2021 12:58:39 +0900 Subject: [PATCH 0586/2442] Don't handle mouse down at fixed vertices --- .../Blueprints/Components/SelectionEditablePath.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs index 6c08b59e09..8c7314d0b6 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs @@ -44,8 +44,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components protected override bool OnMouseDown(MouseDownEvent e) { int index = getMouseTargetVertex(e.ScreenSpaceMouseDownPosition); - - if (index == -1) + if (index == -1 || VertexStates[index].IsFixed) return false; if (e.Button == MouseButton.Left && e.ShiftPressed) @@ -65,7 +64,12 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components protected override bool OnDragStart(DragStartEvent e) { - if (e.Button != MouseButton.Left || getMouseTargetVertex(e.ScreenSpaceMouseDownPosition) == -1) return false; + int index = getMouseTargetVertex(e.ScreenSpaceMouseDownPosition); + if (index == -1 || VertexStates[index].IsFixed) + return false; + + if (e.Button != MouseButton.Left) + return false; dragStartPosition = ToRelativePosition(e.ScreenSpaceMouseDownPosition); From aab7678a69029b0fe9e97d28b9cee0e585010567 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 22 Jul 2021 12:18:37 +0800 Subject: [PATCH 0587/2442] Truncate beatmap text --- .../OnlinePlay/DrawableRoomPlaylistItem.cs | 134 ++++++++++-------- 1 file changed, 75 insertions(+), 59 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index a3a61ccc36..69eb857661 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -108,7 +108,11 @@ namespace osu.Game.Screens.OnlinePlay difficultyIconContainer.Child = new DifficultyIcon(beatmap.Value, ruleset.Value, requiredMods) { Size = new Vector2(32) }; beatmapText.Clear(); - beatmapText.AddLink(Item.Beatmap.Value.ToRomanisableString(), LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineBeatmapID.ToString()); + beatmapText.AddLink(Item.Beatmap.Value.ToRomanisableString(), LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineBeatmapID.ToString(), null, text => + { + text.Truncate = true; + text.RelativeSizeAxes = Axes.X; + }); authorText.Clear(); @@ -147,84 +151,96 @@ namespace osu.Game.Screens.OnlinePlay RelativeSizeAxes = Axes.Both, Beatmap = { BindTarget = beatmap } }, - new FillFlowContainer + new GridContainer { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = 8 }, - Spacing = new Vector2(8, 0), - Direction = FillDirection.Horizontal, - Children = new Drawable[] + ColumnDimensions = new[] { - difficultyIconContainer = new Container + new Dimension(GridSizeMode.AutoSize), + new Dimension(), + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new Drawable[] { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - }, - new FillFlowContainer - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new Drawable[] + difficultyIconContainer = new Container { - beatmapText = new LinkFlowContainer(fontParameters) { AutoSizeAxes = Axes.Both }, - new FillFlowContainer + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Left = 8, Right = 8, }, + }, + new FillFlowContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + Children = new Drawable[] { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(10f, 0), - Children = new Drawable[] + beatmapText = new LinkFlowContainer(fontParameters) { - new FillFlowContainer + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10f, 0), + Children = new Drawable[] { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(10f, 0), - Children = new Drawable[] + new FillFlowContainer { - authorText = new LinkFlowContainer(fontParameters) { AutoSizeAxes = Axes.Both }, - explicitContentPill = new ExplicitContentBeatmapPill + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10f, 0), + Children = new Drawable[] { - Alpha = 0f, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Margin = new MarginPadding { Top = 3f }, - } + authorText = new LinkFlowContainer(fontParameters) { AutoSizeAxes = Axes.Both }, + explicitContentPill = new ExplicitContentBeatmapPill + { + Alpha = 0f, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding { Top = 3f }, + } + }, }, - }, - new Container - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - Child = modDisplay = new ModDisplay + new Container { - Scale = new Vector2(0.4f), - ExpansionMode = ExpansionMode.AlwaysExpanded + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + Child = modDisplay = new ModDisplay + { + Scale = new Vector2(0.4f), + ExpansionMode = ExpansionMode.AlwaysExpanded + } } } } } + }, + new FillFlowContainer + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Direction = FillDirection.Horizontal, + Margin = new MarginPadding { Left = 8, Right = 10, }, + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(5), + ChildrenEnumerable = CreateButtons().Select(button => button.With(b => + { + b.Anchor = Anchor.Centre; + b.Origin = Anchor.Centre; + })) } } } }, - new FillFlowContainer - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Direction = FillDirection.Horizontal, - AutoSizeAxes = Axes.Both, - Spacing = new Vector2(5), - X = -10, - ChildrenEnumerable = CreateButtons().Select(button => button.With(b => - { - b.Anchor = Anchor.Centre; - b.Origin = Anchor.Centre; - })) - } } }; } From 7b6981c632d0f3b649c459f9cef67b7907863076 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 22 Jul 2021 13:06:48 +0900 Subject: [PATCH 0588/2442] Don't show the flip button when flipping is a no-op --- osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs | 3 ++- osu.Game.Rulesets.Catch/Edit/PositionRange.cs | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index 50290ae292..0262843c43 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -69,7 +69,8 @@ namespace osu.Game.Rulesets.Catch.Edit { base.OnSelectionChanged(); - SelectionBox.CanFlipX = true; + var selectionRange = CatchHitObjectUtils.GetPositionRange(EditorBeatmap.SelectedHitObjects); + SelectionBox.CanFlipX = selectionRange.Length > 0 && EditorBeatmap.SelectedHitObjects.Any(h => h is CatchHitObject && !(h is BananaShower)); } /// diff --git a/osu.Game.Rulesets.Catch/Edit/PositionRange.cs b/osu.Game.Rulesets.Catch/Edit/PositionRange.cs index 64e789483a..543e773582 100644 --- a/osu.Game.Rulesets.Catch/Edit/PositionRange.cs +++ b/osu.Game.Rulesets.Catch/Edit/PositionRange.cs @@ -15,6 +15,8 @@ namespace osu.Game.Rulesets.Catch.Edit public readonly float Min; public readonly float Max; + public float Length => Max - Min; + public PositionRange(float value) : this(value, value) { From 19657cd00e461e4758ee6127edc61e2fa6f51b1f Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 22 Jul 2021 13:28:40 +0900 Subject: [PATCH 0589/2442] Guard against empty range in `PositionRange` --- osu.Game.Rulesets.Catch/Edit/PositionRange.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/PositionRange.cs b/osu.Game.Rulesets.Catch/Edit/PositionRange.cs index 543e773582..e61603e5e6 100644 --- a/osu.Game.Rulesets.Catch/Edit/PositionRange.cs +++ b/osu.Game.Rulesets.Catch/Edit/PositionRange.cs @@ -8,14 +8,15 @@ using System; namespace osu.Game.Rulesets.Catch.Edit { /// - /// Represents a closed interval of horizontal positions in the playfield. + /// Represents either the empty range or a closed interval of horizontal positions in the playfield. + /// A represents a closed interval if it is <= , and represents the empty range otherwise. /// public readonly struct PositionRange { public readonly float Min; public readonly float Max; - public float Length => Max - Min; + public float Length => Math.Max(0, Max - Min); public PositionRange(float value) : this(value, value) @@ -30,7 +31,11 @@ namespace osu.Game.Rulesets.Catch.Edit public static PositionRange Union(PositionRange a, PositionRange b) => new PositionRange(Math.Min(a.Min, b.Min), Math.Max(a.Max, b.Max)); - public float GetFlippedPosition(float x) => Max - (x - Min); + /// + /// Get the given position flipped (mirrored) for the axis at the center of this range. + /// Returns the given position unchanged if the range was empty. + /// + public float GetFlippedPosition(float x) => Min <= Max ? Max - (x - Min) : x; public static readonly PositionRange EMPTY = new PositionRange(float.PositiveInfinity, float.NegativeInfinity); } From 2151c1863ee58a9832ab092bf2a2f14439ac9db3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Jul 2021 14:07:32 +0900 Subject: [PATCH 0590/2442] Rename variables for catch-specific casting to avoid any confusion --- osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index 0262843c43..8593c452cf 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -40,12 +40,12 @@ namespace osu.Game.Rulesets.Catch.Edit EditorBeatmap.PerformOnSelection(h => { - if (!(h is CatchHitObject hitObject)) return; + if (!(h is CatchHitObject catchObject)) return; - hitObject.OriginalX += deltaX; + catchObject.OriginalX += deltaX; // Move the nested hit objects to give an instant result before nested objects are recreated. - foreach (var nested in hitObject.NestedHitObjects.OfType()) + foreach (var nested in catchObject.NestedHitObjects.OfType()) nested.OriginalX += deltaX; }); @@ -59,8 +59,8 @@ namespace osu.Game.Rulesets.Catch.Edit bool changed = false; EditorBeatmap.PerformOnSelection(h => { - if (h is CatchHitObject hitObject) - changed |= handleFlip(selectionRange, hitObject); + if (h is CatchHitObject catchObject) + changed |= handleFlip(selectionRange, catchObject); }); return changed; } From 57e5f5575a13bef84fd6c7fa5ffe99bbaac4cd81 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Jul 2021 14:23:24 +0900 Subject: [PATCH 0591/2442] Fix derived API request types firing success when they shouldn't The usual case of `virtual`/`override` being dangerous when logic is added to the base implementation. As such, I've removed this completely. --- osu.Game/Online/API/APIDownloadRequest.cs | 11 +++++------ osu.Game/Online/API/APIRequest.cs | 13 ++++++------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/osu.Game/Online/API/APIDownloadRequest.cs b/osu.Game/Online/API/APIDownloadRequest.cs index 62e22d8f88..63bb3e2287 100644 --- a/osu.Game/Online/API/APIDownloadRequest.cs +++ b/osu.Game/Online/API/APIDownloadRequest.cs @@ -16,6 +16,11 @@ namespace osu.Game.Online.API /// protected virtual string FileExtension { get; } = @".tmp"; + protected APIDownloadRequest() + { + base.Success += () => Success?.Invoke(filename); + } + protected override WebRequest CreateWebRequest() { var file = Path.GetTempFileName(); @@ -39,12 +44,6 @@ namespace osu.Game.Online.API TriggerSuccess(); } - internal override void TriggerSuccess() - { - base.TriggerSuccess(); - Success?.Invoke(filename); - } - public event APIProgressHandler Progressed; public new event APISuccessHandler Success; diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index 8d816d3975..e6bfca166e 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -25,6 +25,11 @@ namespace osu.Game.Online.API /// public new event APISuccessHandler Success; + protected APIRequest() + { + base.Success += () => Success?.Invoke(Result); + } + protected override void PostProcess() { base.PostProcess(); @@ -40,12 +45,6 @@ namespace osu.Game.Online.API TriggerSuccess(); } - - internal override void TriggerSuccess() - { - base.TriggerSuccess(); - Success?.Invoke(Result); - } } /// @@ -132,7 +131,7 @@ namespace osu.Game.Online.API { } - internal virtual void TriggerSuccess() + internal void TriggerSuccess() { lock (completionStateLock) { From ec3ce57bb9278d162564db366a1b0619b4f78a4f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Jul 2021 14:33:05 +0900 Subject: [PATCH 0592/2442] Fix song select background not showing in multiplayer/playlists The screen was now being loaded against incorrect dependencies. I'm not sure why I thought it wasn't possible to just do the `Push` in `LoadComplete` as it seems to work without issue this time... Closes #13974. --- osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 25b02e5084..117ceab6f2 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -162,10 +162,6 @@ namespace osu.Game.Screens.OnlinePlay ongoingOperationTracker, } }; - - // a lot of the functionality in this class depends on loungeSubScreen being in a ready to go state. - // as such, we intentionally load this inline so it is ready alongside this screen. - LoadComponent(loungeSubScreen = CreateLounge()); } private void onlineStateChanged(ValueChangedEvent state) => Schedule(() => @@ -184,7 +180,7 @@ namespace osu.Game.Screens.OnlinePlay screenStack.ScreenPushed += screenPushed; screenStack.ScreenExited += screenExited; - screenStack.Push(loungeSubScreen); + screenStack.Push(loungeSubScreen = CreateLounge()); apiState.BindTo(API.State); apiState.BindValueChanged(onlineStateChanged, true); From db6f32326678f163cfd6d2dc3df96246ec22105d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Jul 2021 14:45:56 +0900 Subject: [PATCH 0593/2442] Output startup component load start/end times to non-debug logs Useful for diagnosing issues in cases like #13981. --- osu.Game/OsuGame.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 8119df43ac..a87361e33c 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -932,7 +932,7 @@ namespace osu.Game try { - Logger.Log($"Loading {component}...", level: LogLevel.Debug); + Logger.Log($"Loading {component}..."); // Since this is running in a separate thread, it is possible for OsuGame to be disposed after LoadComponentAsync has been called // throwing an exception. To avoid this, the call is scheduled on the update thread, which does not run if IsDisposed = true @@ -952,7 +952,7 @@ namespace osu.Game await task.ConfigureAwait(false); - Logger.Log($"Loaded {component}!", level: LogLevel.Debug); + Logger.Log($"Loaded {component}!"); } catch (OperationCanceledException) { From 0cfa8f0f5beb8c3efbf6c2722251c624c85f57f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Jul 2021 15:35:35 +0900 Subject: [PATCH 0594/2442] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index da7a8209a4..69a89c3cd0 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 0e6cee8f18..9825d29405 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index aa4bec06e3..3f81b36216 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From b5cc9010de97d5d81d608faf155d8fcd1a864b93 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Jul 2021 15:39:01 +0900 Subject: [PATCH 0595/2442] Move resolved property to top of class --- .../Components/Timeline/TimelineBlueprintContainer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 354311b7ff..184bec7d44 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -190,6 +190,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private class SelectableAreaBackground : CompositeDrawable { + [Resolved] + private OsuColour colours { get; set; } + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) { float localY = ToLocalSpace(screenSpacePos).Y; @@ -220,9 +223,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }); } - [Resolved] - private OsuColour colours { get; set; } - protected override bool OnHover(HoverEvent e) { this.FadeColour(colours.BlueLighter, 120, Easing.OutQuint); From 2beef89c23a779a12ded341bdb84e1f3094904e3 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 22 Jul 2021 15:20:33 +0900 Subject: [PATCH 0596/2442] Add empty juice stream placement blueprint (no implementation) --- .../JuiceStreamPlacementBlueprint.cs | 11 +++++++++ .../Edit/CatchHitObjectComposer.cs | 1 + .../Edit/JuiceStreamCompositionTool.cs | 24 +++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs create mode 100644 osu.Game.Rulesets.Catch/Edit/JuiceStreamCompositionTool.cs diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs new file mode 100644 index 0000000000..0fb8dc32a2 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs @@ -0,0 +1,11 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Catch.Objects; + +namespace osu.Game.Rulesets.Catch.Edit.Blueprints +{ + public class JuiceStreamPlacementBlueprint : CatchPlacementBlueprint + { + } +} diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index d360274aa6..050c2f625d 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -38,6 +38,7 @@ namespace osu.Game.Rulesets.Catch.Edit protected override IReadOnlyList CompositionTools => new HitObjectCompositionTool[] { new FruitCompositionTool(), + new JuiceStreamCompositionTool(), new BananaShowerCompositionTool() }; diff --git a/osu.Game.Rulesets.Catch/Edit/JuiceStreamCompositionTool.cs b/osu.Game.Rulesets.Catch/Edit/JuiceStreamCompositionTool.cs new file mode 100644 index 0000000000..cb66e2952e --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/JuiceStreamCompositionTool.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Edit.Blueprints; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Tools; + +namespace osu.Game.Rulesets.Catch.Edit +{ + public class JuiceStreamCompositionTool : HitObjectCompositionTool + { + public JuiceStreamCompositionTool() + : base(nameof(JuiceStream)) + { + } + + public override Drawable CreateIcon() => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders); + + public override PlacementBlueprint CreatePlacementBlueprint() => new JuiceStreamPlacementBlueprint(); + } +} From 64102d297237c203c86ae107ed75f73ef498f036 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 22 Jul 2021 15:41:01 +0900 Subject: [PATCH 0597/2442] Add initial implementation of juice stream placement --- .../Components/PlacementEditablePath.cs | 44 +++++++ .../JuiceStreamPlacementBlueprint.cs | 107 ++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs new file mode 100644 index 0000000000..13dfe9db54 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs @@ -0,0 +1,44 @@ +// 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 osu.Game.Rulesets.Catch.Objects; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components +{ + public class PlacementEditablePath : EditablePath + { + private JuiceStreamPathVertex originalNewVertex; + + public PlacementEditablePath(Func positionToDistance) + : base(positionToDistance) + { + } + + public void AddNewVertex() + { + var endVertex = Vertices[^1]; + int index = AddVertex(endVertex.Distance, endVertex.X); + + for (int i = 0; i < VertexCount; i++) + { + VertexStates[i].IsSelected = i == index; + VertexStates[i].VertexBeforeChange = Vertices[i]; + } + + originalNewVertex = Vertices[index]; + } + + /// + /// Move the vertex added by in the last time. + /// + public void MoveLastVertex(Vector2 screenSpacePosition) + { + Vector2 position = ToRelativePosition(screenSpacePosition); + double distanceDelta = PositionToDistance(position.Y) - originalNewVertex.Distance; + float xDelta = position.X - originalNewVertex.X; + MoveSelectedVertices(distanceDelta, xDelta); + } + } +} diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs index 0fb8dc32a2..32ab1f19c1 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs @@ -1,11 +1,118 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Graphics; +using osu.Framework.Input.Events; +using osu.Game.Rulesets.Catch.Edit.Blueprints.Components; using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Edit; +using osuTK; +using osuTK.Input; namespace osu.Game.Rulesets.Catch.Edit.Blueprints { public class JuiceStreamPlacementBlueprint : CatchPlacementBlueprint { + private readonly ScrollingPath scrollingPath; + + private readonly NestedOutlineContainer nestedOutlineContainer; + + private readonly PlacementEditablePath editablePath; + + private int lastEditablePathId = -1; + + public JuiceStreamPlacementBlueprint() + { + InternalChildren = new Drawable[] + { + scrollingPath = new ScrollingPath(), + nestedOutlineContainer = new NestedOutlineContainer(), + editablePath = new PlacementEditablePath(positionToDistance) + }; + } + + protected override void Update() + { + base.Update(); + + if (PlacementActive == PlacementState.Active) + editablePath.UpdateFrom(HitObjectContainer, HitObject); + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + switch (PlacementActive) + { + case PlacementState.Waiting: + if (e.Button != MouseButton.Left) break; + + editablePath.AddNewVertex(); + BeginPlacement(true); + return true; + + case PlacementState.Active: + switch (e.Button) + { + case MouseButton.Left: + editablePath.AddNewVertex(); + return true; + + case MouseButton.Right: + EndPlacement(HitObject.Duration > 0); + return true; + } + + break; + } + + return base.OnMouseDown(e); + } + + public override void UpdateTimeAndPosition(SnapResult result) + { + switch (PlacementActive) + { + case PlacementState.Waiting: + if (!(result.Time is double snappedTime)) return; + + HitObject.OriginalX = ToLocalSpace(result.ScreenSpacePosition).X; + HitObject.StartTime = snappedTime; + break; + + case PlacementState.Active: + Vector2 unsnappedPosition = GetContainingInputManager().CurrentState.Mouse.Position; + editablePath.MoveLastVertex(unsnappedPosition); + break; + + default: + return; + } + + // Make sure the up-to-date position is used for outlines. + Vector2 startPosition = CatchHitObjectUtils.GetStartPosition(HitObjectContainer, HitObject); + editablePath.Position = nestedOutlineContainer.Position = scrollingPath.Position = startPosition; + + updateHitObjectFromPath(); + } + + private void updateHitObjectFromPath() + { + if (lastEditablePathId == editablePath.PathId) + return; + + editablePath.UpdateHitObjectFromPath(HitObject); + ApplyDefaultsToHitObject(); + + scrollingPath.UpdatePathFrom(HitObjectContainer, HitObject); + nestedOutlineContainer.UpdateNestedObjectsFrom(HitObjectContainer, HitObject); + + lastEditablePathId = editablePath.PathId; + } + + private double positionToDistance(float relativeYPosition) + { + double time = HitObjectContainer.TimeAtPosition(relativeYPosition, HitObject.StartTime); + return (time - HitObject.StartTime) * HitObject.Velocity; + } } } From 4e9ac5dc7b108234460050c22e2c986d9e4638b8 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 22 Jul 2021 15:46:23 +0900 Subject: [PATCH 0598/2442] Add tests of juice stream placement blueprint --- .../TestSceneJuiceStreamPlacementBlueprint.cs | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs new file mode 100644 index 0000000000..02861b1adf --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs @@ -0,0 +1,146 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Extensions.ObjectExtensions; +using osu.Framework.Utils; +using osu.Game.Rulesets.Catch.Edit.Blueprints; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.Objects.Drawables; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; +using osuTK.Input; + +namespace osu.Game.Rulesets.Catch.Tests.Editor +{ + public class TestSceneJuiceStreamPlacementBlueprint : CatchPlacementBlueprintTestScene + { + private const double velocity = 0.5; + + private JuiceStream lastObject => LastObject?.HitObject as JuiceStream; + + [BackgroundDependencyLoader] + private void load() + { + Beatmap.Value.BeatmapInfo.BaseDifficulty.SliderTickRate = 5; + Beatmap.Value.BeatmapInfo.BaseDifficulty.SliderMultiplier = velocity * 10; + } + + [Test] + public void TestBasicPlacement() + { + double[] times = { 300, 800 }; + float[] positions = { 100, 200 }; + addPlacementSteps(times, positions); + + AddAssert("juice stream is placed", () => lastObject != null); + AddAssert("start time is correct", () => Precision.AlmostEquals(lastObject.StartTime, times[0])); + AddAssert("end time is correct", () => Precision.AlmostEquals(lastObject.EndTime, times[1])); + AddAssert("start position is correct", () => Precision.AlmostEquals(lastObject.OriginalX, positions[0])); + AddAssert("end position is correct", () => Precision.AlmostEquals(lastObject.EndX, positions[1])); + } + + [Test] + public void TestEmptyNotCommitted() + { + addMoveAndClickSteps(100, 100); + addMoveAndClickSteps(100, 100); + addMoveAndClickSteps(100, 100, true); + AddAssert("juice stream not placed", () => lastObject == null); + } + + [Test] + public void TestMultipleSegments() + { + double[] times = { 100, 300, 500, 700 }; + float[] positions = { 100, 150, 100, 100 }; + addPlacementSteps(times, positions); + + AddAssert("has 4 vertices", () => lastObject.Path.ControlPoints.Count == 4); + addPathCheckStep(times, positions); + } + + [Test] + public void TestVelocityLimit() + { + double[] times = { 100, 300, 500 }; + float[] positions = { 200, 300, 100 }; + addPlacementSteps(times, positions); + addPathCheckStep(times, new float[] { 200, 200, 100 }); + } + + [Test] + public void TestClampedPositionIsRestored() + { + double[] times = { 100, 300, 500 }; + float[] positions = { 200, 200, 0, 250 }; + + addMoveAndClickSteps(times[0], positions[0]); + addMoveAndClickSteps(times[1], positions[1]); + AddMoveStep(times[2], positions[2]); + addMoveAndClickSteps(times[2], positions[3], true); + + addPathCheckStep(times, new float[] { 200, 200, 250 }); + } + + [Test] + public void TestFirstVertexIsFixed() + { + double[] times = { 100, 200 }; + float[] positions = { 100, 300 }; + addPlacementSteps(times, positions); + addPathCheckStep(times, new float[] { 100, 150 }); + } + + [Test] + public void TestOutOfOrder() + { + double[] times = { 100, 700, 500, 300 }; + float[] positions = { 100, 200, 150, 50 }; + addPlacementSteps(times, positions); + addPathCheckStep(times, positions); + } + + [Test] + public void TestMoveBeforeFirstVertex() + { + double[] times = { 300, 500, 100 }; + float[] positions = { 100, 100, 100 }; + addPlacementSteps(times, positions); + AddAssert("start time is correct", () => Precision.AlmostEquals(lastObject.StartTime, times[0])); + AddAssert("end time is correct", () => Precision.AlmostEquals(lastObject.EndTime, times[1], 1e-3)); + } + + protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableJuiceStream((JuiceStream)hitObject); + + protected override PlacementBlueprint CreateBlueprint() => new JuiceStreamPlacementBlueprint(); + + private void addMoveAndClickSteps(double time, float position, bool end = false) + { + AddMoveStep(time, position); + AddClickStep(end ? MouseButton.Right : MouseButton.Left); + } + + private void addPlacementSteps(double[] times, float[] positions) + { + for (int i = 0; i < times.Length; i++) + addMoveAndClickSteps(times[i], positions[i], i == times.Length - 1); + } + + private void addPathCheckStep(double[] times, float[] positions) => AddStep("assert path is correct", () => + Assert.That(getPositions(times), Is.EqualTo(positions).Within(Precision.FLOAT_EPSILON))); + + private float[] getPositions(IEnumerable times) + { + JuiceStream hitObject = lastObject.AsNonNull(); + return times + .Select(time => (time - hitObject.StartTime) * hitObject.Velocity) + .Select(distance => hitObject.EffectiveX + hitObject.Path.PositionAt(distance / hitObject.Distance).X) + .ToArray(); + } + } +} From 957a0686ed7625779f4db162d043b2ebbb2e0218 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Jul 2021 15:48:07 +0900 Subject: [PATCH 0599/2442] Split out nested classes from `TimelineBlueprintContainer` They got too big. --- .../Timeline/TimelineBlueprintContainer.cs | 121 ------------------ .../Components/Timeline/TimelineDragBox.cs | 79 ++++++++++++ .../Timeline/TimelineSelectionHandler.cs | 65 ++++++++++ 3 files changed, 144 insertions(+), 121 deletions(-) create mode 100644 osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineDragBox.cs create mode 100644 osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 184bec7d44..73c38ba23f 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -13,11 +13,9 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Utils; using osu.Game.Graphics; -using osu.Game.Input.Bindings; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts; @@ -236,125 +234,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } } - internal class TimelineSelectionHandler : EditorSelectionHandler, IKeyBindingHandler - { - [Resolved] - private Timeline timeline { get; set; } - - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => timeline.ScreenSpaceDrawQuad.Contains(screenSpacePos); - - // for now we always allow movement. snapping is provided by the Timeline's "distance" snap implementation - public override bool HandleMovement(MoveSelectionEvent moveEvent) => true; - - public bool OnPressed(GlobalAction action) - { - switch (action) - { - case GlobalAction.EditorNudgeLeft: - nudgeSelection(-1); - return true; - - case GlobalAction.EditorNudgeRight: - nudgeSelection(1); - return true; - } - - return false; - } - - public void OnReleased(GlobalAction action) - { - } - - /// - /// Nudge the current selection by the specified multiple of beat divisor lengths, - /// based on the timing at the first object in the selection. - /// - /// The direction and count of beat divisor lengths to adjust. - private void nudgeSelection(int amount) - { - var selected = EditorBeatmap.SelectedHitObjects; - - if (selected.Count == 0) - return; - - var timingPoint = EditorBeatmap.ControlPointInfo.TimingPointAt(selected.First().StartTime); - double adjustment = timingPoint.BeatLength / EditorBeatmap.BeatDivisor * amount; - - EditorBeatmap.PerformOnSelection(h => - { - h.StartTime += adjustment; - EditorBeatmap.Update(h); - }); - } - } - - private class TimelineDragBox : DragBox - { - // the following values hold the start and end X positions of the drag box in the timeline's local space, - // but with zoom unapplied in order to be able to compensate for positional changes - // while the timeline is being zoomed in/out. - private float? selectionStart; - private float selectionEnd; - - [Resolved] - private Timeline timeline { get; set; } - - public TimelineDragBox(Action performSelect) - : base(performSelect) - { - } - - protected override Drawable CreateBox() => new Box - { - RelativeSizeAxes = Axes.Y, - Alpha = 0.3f - }; - - public override bool HandleDrag(MouseButtonEvent e) - { - // The dragbox should only be active if the mouseDownPosition.Y is within this drawable's bounds. - float localY = ToLocalSpace(e.ScreenSpaceMouseDownPosition).Y; - if (DrawRectangle.Top > localY || DrawRectangle.Bottom < localY) - return false; - - selectionStart ??= e.MouseDownPosition.X / timeline.CurrentZoom; - - // only calculate end when a transition is not in progress to avoid bouncing. - if (Precision.AlmostEquals(timeline.CurrentZoom, timeline.Zoom)) - selectionEnd = e.MousePosition.X / timeline.CurrentZoom; - - updateDragBoxPosition(); - return true; - } - - private void updateDragBoxPosition() - { - if (selectionStart == null) - return; - - float rescaledStart = selectionStart.Value * timeline.CurrentZoom; - float rescaledEnd = selectionEnd * timeline.CurrentZoom; - - Box.X = Math.Min(rescaledStart, rescaledEnd); - Box.Width = Math.Abs(rescaledStart - rescaledEnd); - - var boxScreenRect = Box.ScreenSpaceDrawQuad.AABBFloat; - - // we don't care about where the hitobjects are vertically. in cases like stacking display, they may be outside the box without this adjustment. - boxScreenRect.Y -= boxScreenRect.Height; - boxScreenRect.Height *= 2; - - PerformSelection?.Invoke(boxScreenRect); - } - - public override void Hide() - { - base.Hide(); - selectionStart = null; - } - } - protected class TimelineSelectionBlueprintContainer : Container> { protected override Container> Content { get; } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineDragBox.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineDragBox.cs new file mode 100644 index 0000000000..8aad8aa6dc --- /dev/null +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineDragBox.cs @@ -0,0 +1,79 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osu.Framework.Utils; + +namespace osu.Game.Screens.Edit.Compose.Components.Timeline +{ + public class TimelineDragBox : DragBox + { + // the following values hold the start and end X positions of the drag box in the timeline's local space, + // but with zoom unapplied in order to be able to compensate for positional changes + // while the timeline is being zoomed in/out. + private float? selectionStart; + private float selectionEnd; + + [Resolved] + private Timeline timeline { get; set; } + + public TimelineDragBox(Action performSelect) + : base(performSelect) + { + } + + protected override Drawable CreateBox() => new Box + { + RelativeSizeAxes = Axes.Y, + Alpha = 0.3f + }; + + public override bool HandleDrag(MouseButtonEvent e) + { + // The dragbox should only be active if the mouseDownPosition.Y is within this drawable's bounds. + float localY = ToLocalSpace(e.ScreenSpaceMouseDownPosition).Y; + if (DrawRectangle.Top > localY || DrawRectangle.Bottom < localY) + return false; + + selectionStart ??= e.MouseDownPosition.X / timeline.CurrentZoom; + + // only calculate end when a transition is not in progress to avoid bouncing. + if (Precision.AlmostEquals(timeline.CurrentZoom, timeline.Zoom)) + selectionEnd = e.MousePosition.X / timeline.CurrentZoom; + + updateDragBoxPosition(); + return true; + } + + private void updateDragBoxPosition() + { + if (selectionStart == null) + return; + + float rescaledStart = selectionStart.Value * timeline.CurrentZoom; + float rescaledEnd = selectionEnd * timeline.CurrentZoom; + + Box.X = Math.Min(rescaledStart, rescaledEnd); + Box.Width = Math.Abs(rescaledStart - rescaledEnd); + + var boxScreenRect = Box.ScreenSpaceDrawQuad.AABBFloat; + + // we don't care about where the hitobjects are vertically. in cases like stacking display, they may be outside the box without this adjustment. + boxScreenRect.Y -= boxScreenRect.Height; + boxScreenRect.Height *= 2; + + PerformSelection?.Invoke(boxScreenRect); + } + + public override void Hide() + { + base.Hide(); + selectionStart = null; + } + } +} diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs new file mode 100644 index 0000000000..354013a5fd --- /dev/null +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs @@ -0,0 +1,65 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Input.Bindings; +using osu.Game.Input.Bindings; +using osu.Game.Rulesets.Objects; +using osuTK; + +namespace osu.Game.Screens.Edit.Compose.Components.Timeline +{ + internal class TimelineSelectionHandler : EditorSelectionHandler, IKeyBindingHandler + { + [Resolved] + private Timeline timeline { get; set; } + + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => timeline.ScreenSpaceDrawQuad.Contains(screenSpacePos); + + // for now we always allow movement. snapping is provided by the Timeline's "distance" snap implementation + public override bool HandleMovement(MoveSelectionEvent moveEvent) => true; + + public bool OnPressed(GlobalAction action) + { + switch (action) + { + case GlobalAction.EditorNudgeLeft: + nudgeSelection(-1); + return true; + + case GlobalAction.EditorNudgeRight: + nudgeSelection(1); + return true; + } + + return false; + } + + public void OnReleased(GlobalAction action) + { + } + + /// + /// Nudge the current selection by the specified multiple of beat divisor lengths, + /// based on the timing at the first object in the selection. + /// + /// The direction and count of beat divisor lengths to adjust. + private void nudgeSelection(int amount) + { + var selected = EditorBeatmap.SelectedHitObjects; + + if (selected.Count == 0) + return; + + var timingPoint = EditorBeatmap.ControlPointInfo.TimingPointAt(selected.First().StartTime); + double adjustment = timingPoint.BeatLength / EditorBeatmap.BeatDivisor * amount; + + EditorBeatmap.PerformOnSelection(h => + { + h.StartTime += adjustment; + EditorBeatmap.Update(h); + }); + } + } +} From 3fd8de3b912c9b8db588ddeef75b1beccc485d8c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Jul 2021 15:57:47 +0900 Subject: [PATCH 0600/2442] Fix skin editor's fake overlay potentially getting into a bad state --- osu.Game/Skinning/Editor/SkinEditorOverlay.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs index 34fac9c9cf..1aedd90817 100644 --- a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs +++ b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs @@ -59,14 +59,13 @@ namespace osu.Game.Skinning.Editor public override void Hide() { - base.Hide(); + // base call intentionally omitted. skinEditor.Hide(); } public override void Show() { - base.Show(); - + // base call intentionally omitted. if (skinEditor == null) { LoadComponentAsync(skinEditor = new SkinEditor(target), AddInternal); From 21053381c7046d5de3572c837d42394df9bc5e95 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Jul 2021 15:59:00 +0900 Subject: [PATCH 0601/2442] Fix skin editor potentially eating `GlobalAction.Back` when not displayed --- osu.Game/Skinning/Editor/SkinEditorOverlay.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs index 1aedd90817..2562e9c57c 100644 --- a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs +++ b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs @@ -37,8 +37,10 @@ namespace osu.Game.Skinning.Editor switch (action) { case GlobalAction.Back: - if (skinEditor?.State.Value == Visibility.Visible) - Hide(); + if (skinEditor?.State.Value != Visibility.Visible) + break; + + Hide(); return true; case GlobalAction.ToggleSkinEditor: From cc01b9e639f3542fc5ed6633aa6f75d765c0e50e Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 22 Jul 2021 16:14:43 +0900 Subject: [PATCH 0602/2442] Extract `SliderPath` reverse logic to be used in other rulesets --- .../Edit/OsuSelectionHandler.cs | 28 +---------- .../Rulesets/Objects/SliderPathExtensions.cs | 47 +++++++++++++++++++ 2 files changed, 49 insertions(+), 26 deletions(-) create mode 100644 osu.Game/Rulesets/Objects/SliderPathExtensions.cs diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index c2c2226af0..358a44e0e6 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -76,32 +76,8 @@ namespace osu.Game.Rulesets.Osu.Edit if (h is Slider slider) { - var points = slider.Path.ControlPoints.ToArray(); - Vector2 endPos = points.Last().Position.Value; - - slider.Path.ControlPoints.Clear(); - - slider.Position += endPos; - - PathType? lastType = null; - - for (var i = 0; i < points.Length; i++) - { - var p = points[i]; - p.Position.Value -= endPos; - - // propagate types forwards to last null type - if (i == points.Length - 1) - p.Type.Value = lastType; - else if (p.Type.Value != null) - { - var newType = p.Type.Value; - p.Type.Value = lastType; - lastType = newType; - } - - slider.Path.ControlPoints.Insert(0, p); - } + slider.Path.Reverse(out Vector2 offset); + slider.Position += offset; } } diff --git a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs new file mode 100644 index 0000000000..1438c2f128 --- /dev/null +++ b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs @@ -0,0 +1,47 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Game.Rulesets.Objects.Types; +using osuTK; + +#nullable enable + +namespace osu.Game.Rulesets.Objects +{ + public static class SliderPathExtensions + { + /// + /// Reverse the direction of this path. + /// + /// The . + /// The positional offset of the resulting path. It should be added to the start position of this path. + public static void Reverse(this SliderPath sliderPath, out Vector2 positionalOffset) + { + var points = sliderPath.ControlPoints.ToArray(); + positionalOffset = points.Last().Position.Value; + + sliderPath.ControlPoints.Clear(); + + PathType? lastType = null; + + for (var i = 0; i < points.Length; i++) + { + var p = points[i]; + p.Position.Value -= positionalOffset; + + // propagate types forwards to last null type + if (i == points.Length - 1) + p.Type.Value = lastType; + else if (p.Type.Value != null) + { + var newType = p.Type.Value; + p.Type.Value = lastType; + lastType = newType; + } + + sliderPath.ControlPoints.Insert(0, p); + } + } + } +} From dc90e4d24c0bcf986b0e7246d2758e43c6d91049 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 22 Jul 2021 16:52:47 +0900 Subject: [PATCH 0603/2442] `EditorBeatmap.SelectedHitObjects` -> `SelectedItems` (same thing) --- osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index 8593c452cf..ddae539361 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Catch.Edit Vector2 targetPosition = HitObjectContainer.ToLocalSpace(blueprint.ScreenSpaceSelectionPoint + moveEvent.ScreenSpaceDelta); float deltaX = targetPosition.X - originalPosition.X; - deltaX = limitMovement(deltaX, EditorBeatmap.SelectedHitObjects); + deltaX = limitMovement(deltaX, SelectedItems); if (deltaX == 0) { @@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Catch.Edit public override bool HandleFlip(Direction direction) { - var selectionRange = CatchHitObjectUtils.GetPositionRange(EditorBeatmap.SelectedHitObjects); + var selectionRange = CatchHitObjectUtils.GetPositionRange(SelectedItems); bool changed = false; EditorBeatmap.PerformOnSelection(h => @@ -69,8 +69,8 @@ namespace osu.Game.Rulesets.Catch.Edit { base.OnSelectionChanged(); - var selectionRange = CatchHitObjectUtils.GetPositionRange(EditorBeatmap.SelectedHitObjects); - SelectionBox.CanFlipX = selectionRange.Length > 0 && EditorBeatmap.SelectedHitObjects.Any(h => h is CatchHitObject && !(h is BananaShower)); + var selectionRange = CatchHitObjectUtils.GetPositionRange(SelectedItems); + SelectionBox.CanFlipX = selectionRange.Length > 0 && SelectedItems.Any(h => h is CatchHitObject && !(h is BananaShower)); } /// From 9fff304554dcf26514a2ebdfdd47449005437fb3 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 22 Jul 2021 17:00:08 +0900 Subject: [PATCH 0604/2442] Implement reversing of selected pattern in catch editor --- .../Edit/CatchSelectionHandler.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index ddae539361..36072d7fcb 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -65,12 +65,33 @@ namespace osu.Game.Rulesets.Catch.Edit return changed; } + public override bool HandleReverse() + { + double selectionStartTime = SelectedItems.Min(h => h.StartTime); + double selectionEndTime = SelectedItems.Max(h => h.GetEndTime()); + + EditorBeatmap.PerformOnSelection(hitObject => + { + hitObject.StartTime = selectionEndTime - (hitObject.GetEndTime() - selectionStartTime); + + if (hitObject is JuiceStream juiceStream) + { + juiceStream.Path.Reverse(out Vector2 positionalOffset); + juiceStream.OriginalX += positionalOffset.X; + juiceStream.LegacyConvertedY += positionalOffset.Y; + EditorBeatmap.Update(juiceStream); + } + }); + return true; + } + protected override void OnSelectionChanged() { base.OnSelectionChanged(); var selectionRange = CatchHitObjectUtils.GetPositionRange(SelectedItems); SelectionBox.CanFlipX = selectionRange.Length > 0 && SelectedItems.Any(h => h is CatchHitObject && !(h is BananaShower)); + SelectionBox.CanReverse = SelectedItems.Count > 1 || SelectedItems.Any(h => h is JuiceStream); } /// From cd7b90363afe3b39d923a70c054472a502d3f76d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 22 Jul 2021 16:15:23 +0300 Subject: [PATCH 0605/2442] Check nested hitobjects while asserting accent colour --- .../TestSceneLegacyBeatmapSkin.cs | 46 ++++++++++++------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs index f2f68e805c..f6484886da 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs @@ -9,6 +9,8 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Skinning; using osu.Game.Tests.Beatmaps; @@ -113,31 +115,43 @@ namespace osu.Game.Rulesets.Osu.Tests { int index = 0; - foreach (var drawable in TestPlayer.DrawableRuleset.Playfield.AllHitObjects) + return TestPlayer.DrawableRuleset.Playfield.AllHitObjects.All(d => { - index = nextExpectedComboIndex(index, (OsuHitObject)drawable.HitObject); - - if (drawable.AccentColour.Value != expectedColours[index % expectedColours.Length]) - return false; - } - - return true; + index = nextExpectedComboIndex(index, (OsuHitObject)d.HitObject); + return checkComboColour(d, expectedColours[index % expectedColours.Length]); + }); }); + + static bool checkComboColour(DrawableHitObject drawableHitObject, Color4 expectedColour) + { + return drawableHitObject.AccentColour.Value == expectedColour && + drawableHitObject.NestedHitObjects.All(n => checkComboColour(n, expectedColour)); + } } private static IEnumerable getHitCirclesWithLegacyOffsets() { var hitObjects = new List(); - for (int i = 0; i < 5; i++) + for (int i = 0; i < 10; i++) { - hitObjects.Add(new HitCircle - { - StartTime = i, - Position = new Vector2(256, 192), - NewCombo = true, - ComboOffset = i, - }); + var hitObject = i % 2 == 0 + ? (OsuHitObject)new HitCircle() + : new Slider() + { + Path = new SliderPath(new[] + { + new PathControlPoint(new Vector2(0, 0)), + new PathControlPoint(new Vector2(100, 0)), + }) + }; + + hitObject.StartTime = i; + hitObject.Position = new Vector2(256, 192); + hitObject.NewCombo = true; + hitObject.ComboOffset = i; + + hitObjects.Add(hitObject); } return hitObjects; From 523c154f153f0bb2e692a7048d68e3977f8dfdfa Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 22 Jul 2021 16:22:42 +0300 Subject: [PATCH 0606/2442] Add `ComboIndexWithOffsetsBindable` and bind similar to `ComboIndexBindable` --- osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs | 8 +++++++- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 8 +++++++- .../Gameplay/TestSceneHitObjectAccentColour.cs | 8 +++++++- .../Rulesets/Objects/Drawables/DrawableHitObject.cs | 11 +++++++++++ osu.Game/Rulesets/Objects/HitObject.cs | 1 + .../Rulesets/Objects/Types/IHasComboInformation.cs | 2 ++ .../Components/Timeline/TimelineHitObjectBlueprint.cs | 6 ++++++ 7 files changed, 41 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index 33353ec423..d43e6f1c8b 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -95,7 +95,13 @@ namespace osu.Game.Rulesets.Catch.Objects set => ComboIndexBindable.Value = value; } - public int ComboIndexWithOffsets { get; set; } + public Bindable ComboIndexWithOffsetsBindable { get; } = new Bindable(); + + public int ComboIndexWithOffsets + { + get => ComboIndexWithOffsetsBindable.Value; + set => ComboIndexWithOffsetsBindable.Value = value; + } public Bindable LastInComboBindable { get; } = new Bindable(); diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index 7c60480b00..36629fa41e 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -97,7 +97,13 @@ namespace osu.Game.Rulesets.Osu.Objects set => ComboIndexBindable.Value = value; } - public int ComboIndexWithOffsets { get; set; } + public Bindable ComboIndexWithOffsetsBindable { get; } = new Bindable(); + + public int ComboIndexWithOffsets + { + get => ComboIndexWithOffsetsBindable.Value; + set => ComboIndexWithOffsetsBindable.Value = value; + } public Bindable LastInComboBindable { get; } = new Bindable(); diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs index 9d53a5ba63..34f70659e3 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs @@ -100,7 +100,13 @@ namespace osu.Game.Tests.Gameplay set => ComboIndexBindable.Value = value; } - public int ComboIndexWithOffsets { get; set; } + public Bindable ComboIndexWithOffsetsBindable { get; } = new Bindable(); + + public int ComboIndexWithOffsets + { + get => ComboIndexWithOffsetsBindable.Value; + set => ComboIndexWithOffsetsBindable.Value = value; + } public Bindable LastInComboBindable { get; } = new Bindable(); diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 69bdb5fd73..2b222371e2 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -124,7 +124,9 @@ namespace osu.Game.Rulesets.Objects.Drawables public readonly Bindable StartTimeBindable = new Bindable(); private readonly BindableList samplesBindable = new BindableList(); private readonly Bindable userPositionalHitSounds = new Bindable(); + private readonly Bindable comboIndexBindable = new Bindable(); + private readonly Bindable comboIndexWithOffsetsBindable = new Bindable(); protected override bool RequiresChildrenUpdate => true; @@ -186,6 +188,7 @@ namespace osu.Game.Rulesets.Objects.Drawables base.LoadComplete(); comboIndexBindable.BindValueChanged(_ => UpdateComboColour(), true); + comboIndexWithOffsetsBindable.BindValueChanged(_ => UpdateComboColour(), true); updateState(ArmedState.Idle, true); } @@ -250,7 +253,10 @@ namespace osu.Game.Rulesets.Objects.Drawables StartTimeBindable.BindValueChanged(onStartTimeChanged); if (HitObject is IHasComboInformation combo) + { comboIndexBindable.BindTo(combo.ComboIndexBindable); + comboIndexWithOffsetsBindable.BindTo(combo.ComboIndexWithOffsetsBindable); + } samplesBindable.BindTo(HitObject.SamplesBindable); samplesBindable.BindCollectionChanged(onSamplesChanged, true); @@ -275,8 +281,13 @@ namespace osu.Game.Rulesets.Objects.Drawables protected sealed override void OnFree(HitObjectLifetimeEntry entry) { StartTimeBindable.UnbindFrom(HitObject.StartTimeBindable); + if (HitObject is IHasComboInformation combo) + { comboIndexBindable.UnbindFrom(combo.ComboIndexBindable); + comboIndexWithOffsetsBindable.UnbindFrom(combo.ComboIndexWithOffsetsBindable); + } + samplesBindable.UnbindFrom(HitObject.SamplesBindable); // Changes in start time trigger state updates. When a new hitobject is applied, OnApply() automatically performs a state update anyway. diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index db02eafa92..422655502d 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -118,6 +118,7 @@ namespace osu.Game.Rulesets.Objects foreach (var n in NestedHitObjects.OfType()) { n.ComboIndexBindable.BindTo(hasCombo.ComboIndexBindable); + n.ComboIndexWithOffsetsBindable.BindTo(hasCombo.ComboIndexWithOffsetsBindable); n.IndexInCurrentComboBindable.BindTo(hasCombo.IndexInCurrentComboBindable); } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs index f01181f436..eddf764ae7 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs @@ -26,6 +26,8 @@ namespace osu.Game.Rulesets.Objects.Types /// int ComboIndex { get; set; } + Bindable ComboIndexWithOffsetsBindable { get; } + /// /// The offset of this combo in relation to the beatmap, with all aggregate s applied. /// This should be used instead of only when retrieving combo colours from the beatmap's skin. diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 7f8cc1c8fa..d115c10b5d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -37,7 +37,10 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private readonly Bindable startTime; private Bindable indexInCurrentComboBindable; + private Bindable comboIndexBindable; + private Bindable comboIndexWithOffsetsBindable; + private Bindable displayColourBindable; private readonly ExtendableCircle circle; @@ -122,6 +125,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline comboIndexBindable = comboInfo.ComboIndexBindable.GetBoundCopy(); comboIndexBindable.BindValueChanged(_ => updateColour(), true); + comboIndexWithOffsetsBindable = comboInfo.ComboIndexWithOffsetsBindable.GetBoundCopy(); + comboIndexWithOffsetsBindable.BindValueChanged(_ => updateColour(), true); + skin.SourceChanged += updateColour; break; } From 20adfabc975f39c7afbbd1ae622612a01395392b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 22 Jul 2021 16:42:00 +0300 Subject: [PATCH 0607/2442] Remove unnecessary parentheses I... forgot to amend that. --- osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs index f6484886da..8b51225e98 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs @@ -137,7 +137,7 @@ namespace osu.Game.Rulesets.Osu.Tests { var hitObject = i % 2 == 0 ? (OsuHitObject)new HitCircle() - : new Slider() + : new Slider { Path = new SliderPath(new[] { From 00ec229bdec5eba0791a634c540611313dba148f Mon Sep 17 00:00:00 2001 From: Lucas A Date: Thu, 22 Jul 2021 19:20:20 +0200 Subject: [PATCH 0608/2442] Localise stat values according to the current locale. --- .../Overlays/Profile/Header/TopHeaderContainer.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index b64dba62e3..438f52a2ce 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -181,19 +181,19 @@ namespace osu.Game.Overlays.Profile.Header if (user?.Statistics != null) { - userStats.Add(new UserStatsLine(UsersStrings.ShowStatsRankedScore, user.Statistics.RankedScore.ToString("#,##0"))); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsRankedScore, user.Statistics.RankedScore.ToLocalisableString("#,##0"))); userStats.Add(new UserStatsLine(UsersStrings.ShowStatsHitAccuracy, user.Statistics.DisplayAccuracy)); - userStats.Add(new UserStatsLine(UsersStrings.ShowStatsPlayCount, user.Statistics.PlayCount.ToString("#,##0"))); - userStats.Add(new UserStatsLine(UsersStrings.ShowStatsTotalScore, user.Statistics.TotalScore.ToString("#,##0"))); - userStats.Add(new UserStatsLine(UsersStrings.ShowStatsTotalHits, user.Statistics.TotalHits.ToString("#,##0"))); - userStats.Add(new UserStatsLine(UsersStrings.ShowStatsMaximumCombo, user.Statistics.MaxCombo.ToString("#,##0"))); - userStats.Add(new UserStatsLine(UsersStrings.ShowStatsReplaysWatchedByOthers, user.Statistics.ReplaysWatched.ToString("#,##0"))); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsPlayCount, user.Statistics.PlayCount.ToLocalisableString("#,##0"))); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsTotalScore, user.Statistics.TotalScore.ToLocalisableString("#,##0"))); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsTotalHits, user.Statistics.TotalHits.ToLocalisableString("#,##0"))); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsMaximumCombo, user.Statistics.MaxCombo.ToLocalisableString("#,##0"))); + userStats.Add(new UserStatsLine(UsersStrings.ShowStatsReplaysWatchedByOthers, user.Statistics.ReplaysWatched.ToLocalisableString("#,##0"))); } } private class UserStatsLine : Container { - public UserStatsLine(LocalisableString left, string right) + public UserStatsLine(LocalisableString left, LocalisableString right) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; From a5736085a9408fe7ca2bc9c927c989760b205a5d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 23 Jul 2021 02:23:37 +0900 Subject: [PATCH 0609/2442] Ensure externally run operations on `LoungeSubScreen` are run after load is completed --- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 27 +++++++++++-------- .../Multiplayer/MultiplayerLoungeSubScreen.cs | 6 ++--- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index f43109c4fa..68bd3cd613 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -177,7 +177,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge this.HidePopover(); } - public void Join(Room room, string password) + public void Join(Room room, string password) => Schedule(() => { if (joiningRoomOperation != null) return; @@ -194,25 +194,22 @@ namespace osu.Game.Screens.OnlinePlay.Lounge joiningRoomOperation?.Dispose(); joiningRoomOperation = null; }); - } - - private void updateLoadingLayer() - { - if (operationInProgress.Value || !initialRoomsReceived.Value) - loadingLayer.Show(); - else - loadingLayer.Hide(); - } + }); /// /// Push a room as a new subscreen. /// - public virtual void Open(Room room) + public void Open(Room room) => Schedule(() => { // Handles the case where a room is clicked 3 times in quick succession if (!this.IsCurrentScreen()) return; + OpenNewRoom(room); + }); + + protected virtual void OpenNewRoom(Room room) + { selectedRoom.Value = room; this.Push(CreateRoomSubScreen(room)); @@ -221,5 +218,13 @@ namespace osu.Game.Screens.OnlinePlay.Lounge protected abstract FilterControl CreateFilterControl(); protected abstract RoomSubScreen CreateRoomSubScreen(Room room); + + private void updateLoadingLayer() + { + if (operationInProgress.Value || !initialRoomsReceived.Value) + loadingLayer.Show(); + else + loadingLayer.Hide(); + } } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs index 4d20652465..7062994479 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs @@ -20,15 +20,15 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private MultiplayerClient client { get; set; } - public override void Open(Room room) + protected override void OpenNewRoom(Room room) { - if (!client.IsConnected.Value) + if (client?.IsConnected.Value != true) { Logger.Log("Not currently connected to the multiplayer server.", LoggingTarget.Runtime, LogLevel.Important); return; } - base.Open(room); + base.OpenNewRoom(room); } } } From b31fef7e001c22f5e55c01ff8c3fa4df9c090b01 Mon Sep 17 00:00:00 2001 From: 02Naitsirk <57512128+02Naitsirk@users.noreply.github.com> Date: Thu, 22 Jul 2021 13:49:47 -0400 Subject: [PATCH 0610/2442] Implement total SR formula that better correlates with pp --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index e47f82fb39..2f0b7da789 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -34,7 +34,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; - double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2; + + double baseAimPerformance = Math.Pow(5 * Math.Max(1, aimRating) - 4, 3) / 100000; + double baseSpeedPerformance = Math.Pow(5 * Math.Max(1, speedRating) - 4, 3) / 100000; + double basePerformance = Math.Pow(Math.Pow(baseAimPerformance, 1.1) + Math.Pow(baseSpeedPerformance, 1.1), 1 / 1.1); + double starRating = basePerformance > 0.00001 ? 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0; HitWindows hitWindows = new OsuHitWindows(); hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty); From 986910a7e4398fb469c643e8feb53526d26d1493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 22 Jul 2021 22:43:35 +0200 Subject: [PATCH 0611/2442] Annotate dependency as possibly-null --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index e89f3424d9..9f3543d059 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -59,7 +60,7 @@ namespace osu.Game.Overlays.Settings.Sections private IBindable> managerRemoved; [BackgroundDependencyLoader(permitNulls: true)] - private void load(OsuConfigManager config, SkinEditorOverlay skinEditor) + private void load(OsuConfigManager config, [CanBeNull] SkinEditorOverlay skinEditor) { FlowContent.Spacing = new Vector2(0, 5); From e6b28e1386a481ee8e06aef41ae30ef1fae61370 Mon Sep 17 00:00:00 2001 From: aitani9 <55509723+aitani9@users.noreply.github.com> Date: Thu, 22 Jul 2021 14:01:31 -0700 Subject: [PATCH 0612/2442] Rename `origin` to `center` for clarity --- osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs index f37058564e..c4872aaf64 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -146,11 +146,11 @@ namespace osu.Game.Rulesets.Osu.Mods if (barrelRollActive) { - float origin = restrictTo.ToSpaceOfOtherDrawable(restrictTo.OriginPosition, Parent).X; + float center = restrictTo.ToSpaceOfOtherDrawable(restrictTo.OriginPosition, Parent).X; float halfDiagonal = MathF.Sqrt(MathF.Pow(restrictTo.DrawWidth / 2, 2) + MathF.Pow(restrictTo.DrawHeight / 2, 2)); - start = origin - halfDiagonal; - end = origin + halfDiagonal; + start = center - halfDiagonal; + end = center + halfDiagonal; } else { From 80cb7c77b911f380a7fd558d375509bc3e729bf4 Mon Sep 17 00:00:00 2001 From: aitani9 <55509723+aitani9@users.noreply.github.com> Date: Thu, 22 Jul 2021 14:04:01 -0700 Subject: [PATCH 0613/2442] Calculate the diagonal length using `Vector2.LengthFast` instead of manually --- osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs index c4872aaf64..09d73566a1 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -147,7 +147,7 @@ namespace osu.Game.Rulesets.Osu.Mods if (barrelRollActive) { float center = restrictTo.ToSpaceOfOtherDrawable(restrictTo.OriginPosition, Parent).X; - float halfDiagonal = MathF.Sqrt(MathF.Pow(restrictTo.DrawWidth / 2, 2) + MathF.Pow(restrictTo.DrawHeight / 2, 2)); + float halfDiagonal = (restrictTo.DrawSize / 2).LengthFast; start = center - halfDiagonal; end = center + halfDiagonal; From 715f3e3f7c1240baed2b64c2c096923f9fcec180 Mon Sep 17 00:00:00 2001 From: aitani9 <55509723+aitani9@users.noreply.github.com> Date: Thu, 22 Jul 2021 14:07:41 -0700 Subject: [PATCH 0614/2442] Make blinds move correctly whenever the playfield is rotated --- osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | 31 +++++++--------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs index 09d73566a1..3102db270e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -2,13 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Objects; @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Mods public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { - drawableRuleset.Overlays.Add(blinds = new DrawableOsuBlinds(drawableRuleset.Playfield, drawableRuleset.Beatmap, drawableRuleset.Mods)); + drawableRuleset.Overlays.Add(blinds = new DrawableOsuBlinds(drawableRuleset.Playfield, drawableRuleset.Beatmap)); } public void ApplyToHealthProcessor(HealthProcessor healthProcessor) @@ -68,8 +68,6 @@ namespace osu.Game.Rulesets.Osu.Mods private readonly CompositeDrawable restrictTo; - private readonly bool barrelRollActive; - /// /// /// Percentage of playfield to extend blinds over. Basically moves the origin points where the blinds start. @@ -83,24 +81,13 @@ namespace osu.Game.Rulesets.Osu.Mods /// private const float leniency = 0.1f; - public DrawableOsuBlinds(CompositeDrawable restrictTo, Beatmap beatmap, IEnumerable mods) + public DrawableOsuBlinds(CompositeDrawable restrictTo, Beatmap beatmap) { this.restrictTo = restrictTo; this.beatmap = beatmap; targetBreakMultiplier = 0; easing = 1; - - barrelRollActive = false; - - foreach (Mod mod in mods) - { - if (mod is OsuModBarrelRoll) - { - barrelRollActive = true; - break; - } - } } [BackgroundDependencyLoader] @@ -144,7 +131,12 @@ namespace osu.Game.Rulesets.Osu.Mods { float start, end; - if (barrelRollActive) + if (Precision.AlmostEquals(restrictTo.Rotation, 0)) + { + start = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopLeft).X; + end = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopRight).X; + } + else { float center = restrictTo.ToSpaceOfOtherDrawable(restrictTo.OriginPosition, Parent).X; float halfDiagonal = (restrictTo.DrawSize / 2).LengthFast; @@ -152,11 +144,6 @@ namespace osu.Game.Rulesets.Osu.Mods start = center - halfDiagonal; end = center + halfDiagonal; } - else - { - start = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopLeft).X; - end = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopRight).X; - } float rawWidth = end - start; From 6dbdfcc70cd9f228707977e34bb56329e97b867f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 22 Jul 2021 23:11:58 +0200 Subject: [PATCH 0615/2442] Fix room password not being percent-encoded in join request --- osu.Game/Online/Rooms/JoinRoomRequest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Rooms/JoinRoomRequest.cs b/osu.Game/Online/Rooms/JoinRoomRequest.cs index b2d772cac7..d9d4f2eb5c 100644 --- a/osu.Game/Online/Rooms/JoinRoomRequest.cs +++ b/osu.Game/Online/Rooms/JoinRoomRequest.cs @@ -22,10 +22,10 @@ namespace osu.Game.Online.Rooms { var req = base.CreateWebRequest(); req.Method = HttpMethod.Put; + req.AddParameter(@"password", Password, RequestParameterType.Query); return req; } - // Todo: Password needs to be specified here rather than via AddParameter() because this is a PUT request. May be a framework bug. - protected override string Target => $"rooms/{Room.RoomID.Value}/users/{User.Id}?password={Password}"; + protected override string Target => $@"rooms/{Room.RoomID.Value}/users/{User.Id}"; } } From d49d303bae69ce6969932e91ef192a81336c588d Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 23 Jul 2021 10:10:55 +0900 Subject: [PATCH 0616/2442] Call `GetContainingInputManager` at `LoadComplete` --- .../Edit/Blueprints/JuiceStreamPlacementBlueprint.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs index 32ab1f19c1..cff5bc2417 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; +using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Game.Rulesets.Catch.Edit.Blueprints.Components; using osu.Game.Rulesets.Catch.Objects; @@ -21,6 +22,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints private int lastEditablePathId = -1; + private InputManager inputManager; + public JuiceStreamPlacementBlueprint() { InternalChildren = new Drawable[] @@ -39,6 +42,13 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints editablePath.UpdateFrom(HitObjectContainer, HitObject); } + protected override void LoadComplete() + { + base.LoadComplete(); + + inputManager = GetContainingInputManager(); + } + protected override bool OnMouseDown(MouseDownEvent e) { switch (PlacementActive) @@ -80,7 +90,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints break; case PlacementState.Active: - Vector2 unsnappedPosition = GetContainingInputManager().CurrentState.Mouse.Position; + Vector2 unsnappedPosition = inputManager.CurrentState.Mouse.Position; editablePath.MoveLastVertex(unsnappedPosition); break; From 4509c8bcfbcf9fc2a9d6010d707868312fc13cea Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 23 Jul 2021 10:13:55 +0900 Subject: [PATCH 0617/2442] Use the more consistent `lastVertex`, with a comment --- .../Blueprints/Components/PlacementEditablePath.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs index 13dfe9db54..fedda20b9d 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs @@ -9,7 +9,11 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components { public class PlacementEditablePath : EditablePath { - private JuiceStreamPathVertex originalNewVertex; + /// + /// The original position of the last added vertex. + /// This is not same as the last vertex of the current path because the vertex ordering can change. + /// + private JuiceStreamPathVertex lastVertex; public PlacementEditablePath(Func positionToDistance) : base(positionToDistance) @@ -27,7 +31,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components VertexStates[i].VertexBeforeChange = Vertices[i]; } - originalNewVertex = Vertices[index]; + lastVertex = Vertices[index]; } /// @@ -36,8 +40,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components public void MoveLastVertex(Vector2 screenSpacePosition) { Vector2 position = ToRelativePosition(screenSpacePosition); - double distanceDelta = PositionToDistance(position.Y) - originalNewVertex.Distance; - float xDelta = position.X - originalNewVertex.X; + double distanceDelta = PositionToDistance(position.Y) - lastVertex.Distance; + float xDelta = position.X - lastVertex.X; MoveSelectedVertices(distanceDelta, xDelta); } } From bd3386e770d86da2aee1c2cecf539b30dd6bf30f Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 23 Jul 2021 10:17:42 +0900 Subject: [PATCH 0618/2442] Fix previously placed vertices in juice stream placement A different UX than not fixing vertices. --- .../Edit/Blueprints/Components/PlacementEditablePath.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs index fedda20b9d..158872fbab 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/PlacementEditablePath.cs @@ -28,6 +28,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components for (int i = 0; i < VertexCount; i++) { VertexStates[i].IsSelected = i == index; + VertexStates[i].IsFixed = i != index; VertexStates[i].VertexBeforeChange = Vertices[i]; } From b1fd6c0ded7b4a55ecf2489013fcac27ce9d8c3a Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 23 Jul 2021 10:45:33 +0900 Subject: [PATCH 0619/2442] Apply changes to tests of juice stream placement --- .../TestSceneJuiceStreamPlacementBlueprint.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs index 02861b1adf..cd1fa31b61 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs @@ -67,10 +67,19 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor [Test] public void TestVelocityLimit() { - double[] times = { 100, 300, 500 }; - float[] positions = { 200, 300, 100 }; + double[] times = { 100, 300 }; + float[] positions = { 200, 500 }; addPlacementSteps(times, positions); - addPathCheckStep(times, new float[] { 200, 200, 100 }); + addPathCheckStep(times, new float[] { 200, 300 }); + } + + [Test] + public void TestPreviousVerticesAreFixed() + { + double[] times = { 100, 300, 500, 700 }; + float[] positions = { 200, 400, 100, 500 }; + addPlacementSteps(times, positions); + addPathCheckStep(times, new float[] { 200, 300, 200, 300 }); } [Test] From 88d9e2ec06aa450de5bb28fd95807ae40f0fac5c Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Fri, 23 Jul 2021 10:23:31 +0800 Subject: [PATCH 0620/2442] Guard against IndexOutOfRange when parsing launch args --- osu.Desktop/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index cbee1694ba..dc712f2593 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -32,7 +32,7 @@ namespace osu.Desktop var split = arg.Split('='); var key = split[0]; - var val = split[1]; + var val = split.Length > 1 ? split[1] : string.Empty; switch (key) { From ee3791ccf2cea6710d68dab0412f15b620a38f86 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 23 Jul 2021 06:24:58 +0300 Subject: [PATCH 0621/2442] Update colours once on `TimelineHitObjectBlueprint` --- .../Components/Timeline/TimelineHitObjectBlueprint.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index d115c10b5d..6b75696d23 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -123,12 +123,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline indexInCurrentComboBindable.BindValueChanged(_ => updateComboIndex(), true); comboIndexBindable = comboInfo.ComboIndexBindable.GetBoundCopy(); - comboIndexBindable.BindValueChanged(_ => updateColour(), true); - comboIndexWithOffsetsBindable = comboInfo.ComboIndexWithOffsetsBindable.GetBoundCopy(); - comboIndexWithOffsetsBindable.BindValueChanged(_ => updateColour(), true); + comboIndexBindable.ValueChanged += _ => updateColour(); + comboIndexWithOffsetsBindable.ValueChanged += _ => updateColour(); skin.SourceChanged += updateColour; + updateColour(); break; } } From 8600a3bf5b3fc057ce0d342e9c4c1df88501b946 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 23 Jul 2021 07:15:36 +0300 Subject: [PATCH 0622/2442] Replace "offset" term in combo index documentations with "index" instead --- osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs index eddf764ae7..29a56fc625 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs @@ -15,21 +15,21 @@ namespace osu.Game.Rulesets.Objects.Types Bindable IndexInCurrentComboBindable { get; } /// - /// The offset of this hitobject in the current combo. + /// The index of this hitobject in the current combo. /// int IndexInCurrentCombo { get; set; } Bindable ComboIndexBindable { get; } /// - /// The offset of this combo in relation to the beatmap. + /// The index of this combo in relation to the beatmap. /// int ComboIndex { get; set; } Bindable ComboIndexWithOffsetsBindable { get; } /// - /// The offset of this combo in relation to the beatmap, with all aggregate s applied. + /// The index of this combo in relation to the beatmap, with all aggregate s applied. /// This should be used instead of only when retrieving combo colours from the beatmap's skin. /// int ComboIndexWithOffsets { get; set; } From 0b3b9e35baa308c5d7778a41c149534e025fc4b2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 23 Jul 2021 07:32:56 +0300 Subject: [PATCH 0623/2442] Also update colours once on `DrawableHitObject` --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 2b222371e2..25f3b8931a 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -187,7 +187,7 @@ namespace osu.Game.Rulesets.Objects.Drawables { base.LoadComplete(); - comboIndexBindable.BindValueChanged(_ => UpdateComboColour(), true); + comboIndexBindable.BindValueChanged(_ => UpdateComboColour()); comboIndexWithOffsetsBindable.BindValueChanged(_ => UpdateComboColour(), true); updateState(ArmedState.Idle, true); From 7bc30b46ff311c3d1fc1d585dc26ddd00c4098e1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 23 Jul 2021 07:51:58 +0300 Subject: [PATCH 0624/2442] Use `BindValueChanged` with last running immediately instead --- .../Components/Timeline/TimelineHitObjectBlueprint.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 6b75696d23..6e57b8e88c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -125,10 +125,10 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline comboIndexBindable = comboInfo.ComboIndexBindable.GetBoundCopy(); comboIndexWithOffsetsBindable = comboInfo.ComboIndexWithOffsetsBindable.GetBoundCopy(); - comboIndexBindable.ValueChanged += _ => updateColour(); - comboIndexWithOffsetsBindable.ValueChanged += _ => updateColour(); + comboIndexBindable.BindValueChanged(_ => updateColour()); + comboIndexWithOffsetsBindable.BindValueChanged(_ => updateColour(), true); + skin.SourceChanged += updateColour; - updateColour(); break; } } From b6c1cf4956f0f28b20735d1c27b8e640714753cf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 23 Jul 2021 13:59:51 +0900 Subject: [PATCH 0625/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 69a89c3cd0..3a9fdfebab 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9825d29405..8ccc3dfbe2 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 3f81b36216..66cae06713 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 17168b8137bc3c68e4f35df75af2f51d2d73cc1f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 23 Jul 2021 18:58:22 +0900 Subject: [PATCH 0626/2442] Fix authentication loss not handled correctly This handles the case where on initial API connection, the server responds with an `Unauthorized` response. It doesn't perform this same checking/handling on every API request, which is probably what we want eventually. Opting to not address the full issue because I know this is going to be a long one (see https://github.com/ppy/osu/blob/05c50c0f6cfafab359963586ec265ad4f143a46c/osu.Game/Online/API/APIAccess.cs#L233). --- osu.Game/Online/API/APIAccess.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 1686595512..eb3abff2b7 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -148,6 +148,16 @@ namespace osu.Game.Online.API var userReq = new GetUserRequest(); + userReq.Failure += ex => + { + if (ex.InnerException is WebException webException && webException.Message == @"Unauthorized") + { + log.Add(@"Login no longer valid"); + Logout(); + } + else + failConnectionProcess(); + }; userReq.Success += u => { localUser.Value = u; @@ -167,6 +177,7 @@ namespace osu.Game.Online.API // getting user's friends is considered part of the connection process. var friendsReq = new GetFriendsRequest(); + friendsReq.Failure += _ => failConnectionProcess(); friendsReq.Success += res => { friends.AddRange(res); From ff3d38de6f49455d7697f91f0ec108665c7f9dcb Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 23 Jul 2021 22:37:08 +0200 Subject: [PATCH 0627/2442] Localise accuracy display. --- osu.Game/Graphics/UserInterface/PercentageCounter.cs | 3 ++- osu.Game/Graphics/UserInterface/RollingCounter.cs | 5 +++-- osu.Game/Graphics/UserInterface/ScoreCounter.cs | 3 ++- osu.Game/Online/Leaderboards/LeaderboardScore.cs | 4 ++-- .../BeatmapSet/Scores/TopScoreStatisticsSection.cs | 3 ++- osu.Game/Scoring/ScoreInfo.cs | 3 ++- osu.Game/Screens/Play/Break/BreakInfoLine.cs | 5 +++-- osu.Game/Screens/Play/HUD/DefaultComboCounter.cs | 3 ++- .../Ranking/Expanded/Statistics/AccuracyStatistic.cs | 3 ++- osu.Game/Screens/Ranking/Expanded/TotalScoreCounter.cs | 3 ++- osu.Game/Users/UserStatistics.cs | 3 ++- osu.Game/Utils/FormatUtils.cs | 7 +++---- 12 files changed, 27 insertions(+), 18 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/PercentageCounter.cs b/osu.Game/Graphics/UserInterface/PercentageCounter.cs index 2d53ec066b..0ebf2849fe 100644 --- a/osu.Game/Graphics/UserInterface/PercentageCounter.cs +++ b/osu.Game/Graphics/UserInterface/PercentageCounter.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Graphics.Sprites; using osu.Game.Utils; @@ -27,7 +28,7 @@ namespace osu.Game.Graphics.UserInterface Current.Value = DisplayedCount = 1.0f; } - protected override string FormatCount(double count) => count.FormatAccuracy(); + protected override LocalisableString FormatCount(double count) => count.FormatAccuracy(); protected override double GetProportionalDuration(double currentValue, double newValue) { diff --git a/osu.Game/Graphics/UserInterface/RollingCounter.cs b/osu.Game/Graphics/UserInterface/RollingCounter.cs index b96181416d..244658b75e 100644 --- a/osu.Game/Graphics/UserInterface/RollingCounter.cs +++ b/osu.Game/Graphics/UserInterface/RollingCounter.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Localisation; namespace osu.Game.Graphics.UserInterface { @@ -137,8 +138,8 @@ namespace osu.Game.Graphics.UserInterface /// Used to format counts. /// /// Count to format. - /// Count formatted as a string. - protected virtual string FormatCount(T count) + /// Count formatted as a localisable string. + protected virtual LocalisableString FormatCount(T count) { return count.ToString(); } diff --git a/osu.Game/Graphics/UserInterface/ScoreCounter.cs b/osu.Game/Graphics/UserInterface/ScoreCounter.cs index 5747c846eb..7ebf3819e4 100644 --- a/osu.Game/Graphics/UserInterface/ScoreCounter.cs +++ b/osu.Game/Graphics/UserInterface/ScoreCounter.cs @@ -3,6 +3,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Graphics.Sprites; namespace osu.Game.Graphics.UserInterface @@ -37,7 +38,7 @@ namespace osu.Game.Graphics.UserInterface return currentValue > newValue ? currentValue - newValue : newValue - currentValue; } - protected override string FormatCount(double count) + protected override LocalisableString FormatCount(double count) { string format = new string('0', RequiredDisplayDigits.Value); diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index 7108a23e44..934b905a1a 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -372,10 +372,10 @@ namespace osu.Game.Online.Leaderboards public class LeaderboardScoreStatistic { public IconUsage Icon; - public string Value; + public LocalisableString Value; public string Name; - public LeaderboardScoreStatistic(IconUsage icon, string name, string value) + public LeaderboardScoreStatistic(IconUsage icon, string name, LocalisableString value) { Icon = icon; Name = name; diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs index 262f321598..3d5f3f595c 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -204,7 +205,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores this.text = text; } - public string Text + public LocalisableString Text { set => text.Text = value; } diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index a0c4d5a026..890ead40e3 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -7,6 +7,7 @@ using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Converters; +using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Online.API; @@ -34,7 +35,7 @@ namespace osu.Game.Scoring public double Accuracy { get; set; } [JsonIgnore] - public string DisplayAccuracy => Accuracy.FormatAccuracy(); + public LocalisableString DisplayAccuracy => Accuracy.FormatAccuracy(); [JsonProperty(@"pp")] public double? PP { get; set; } diff --git a/osu.Game/Screens/Play/Break/BreakInfoLine.cs b/osu.Game/Screens/Play/Break/BreakInfoLine.cs index 18aab394f8..0bc79d6e77 100644 --- a/osu.Game/Screens/Play/Break/BreakInfoLine.cs +++ b/osu.Game/Screens/Play/Break/BreakInfoLine.cs @@ -7,6 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Utils; @@ -63,7 +64,7 @@ namespace osu.Game.Screens.Play.Break valueText.Text = newText; } - protected virtual string Format(T count) + protected virtual LocalisableString Format(T count) { if (count is Enum countEnum) return countEnum.GetDescription(); @@ -86,6 +87,6 @@ namespace osu.Game.Screens.Play.Break { } - protected override string Format(double count) => count.FormatAccuracy(); + protected override LocalisableString Format(double count) => count.FormatAccuracy(); } } diff --git a/osu.Game/Screens/Play/HUD/DefaultComboCounter.cs b/osu.Game/Screens/Play/HUD/DefaultComboCounter.cs index 718ae24cf1..6d87211ddc 100644 --- a/osu.Game/Screens/Play/HUD/DefaultComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/DefaultComboCounter.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -31,7 +32,7 @@ namespace osu.Game.Screens.Play.HUD Current.BindTo(scoreProcessor.Combo); } - protected override string FormatCount(int count) + protected override LocalisableString FormatCount(int count) { return $@"{count}x"; } diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/AccuracyStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/AccuracyStatistic.cs index 288a107874..476c9fb42f 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/AccuracyStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/AccuracyStatistic.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -44,7 +45,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics protected override Easing RollingEasing => AccuracyCircle.ACCURACY_TRANSFORM_EASING; - protected override string FormatCount(double count) => count.FormatAccuracy(); + protected override LocalisableString FormatCount(double count) => count.FormatAccuracy(); protected override OsuSpriteText CreateSpriteText() => base.CreateSpriteText().With(s => { diff --git a/osu.Game/Screens/Ranking/Expanded/TotalScoreCounter.cs b/osu.Game/Screens/Ranking/Expanded/TotalScoreCounter.cs index 65082d3fae..c54bca9e3a 100644 --- a/osu.Game/Screens/Ranking/Expanded/TotalScoreCounter.cs +++ b/osu.Game/Screens/Ranking/Expanded/TotalScoreCounter.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -26,7 +27,7 @@ namespace osu.Game.Screens.Ranking.Expanded RelativeSizeAxes = Axes.X; } - protected override string FormatCount(long count) => count.ToString("N0"); + protected override LocalisableString FormatCount(long count) => count.ToString("N0"); protected override OsuSpriteText CreateSpriteText() => base.CreateSpriteText().With(s => { diff --git a/osu.Game/Users/UserStatistics.cs b/osu.Game/Users/UserStatistics.cs index 5ddcd86d28..449b0aa212 100644 --- a/osu.Game/Users/UserStatistics.cs +++ b/osu.Game/Users/UserStatistics.cs @@ -3,6 +3,7 @@ using System; using Newtonsoft.Json; +using osu.Framework.Localisation; using osu.Game.Scoring; using osu.Game.Utils; using static osu.Game.Users.User; @@ -45,7 +46,7 @@ namespace osu.Game.Users public double Accuracy; [JsonIgnore] - public string DisplayAccuracy => (Accuracy / 100).FormatAccuracy(); + public LocalisableString DisplayAccuracy => (Accuracy / 100).FormatAccuracy(); [JsonProperty(@"play_count")] public int PlayCount; diff --git a/osu.Game/Utils/FormatUtils.cs b/osu.Game/Utils/FormatUtils.cs index df1b6cf00d..e763558647 100644 --- a/osu.Game/Utils/FormatUtils.cs +++ b/osu.Game/Utils/FormatUtils.cs @@ -2,8 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Globalization; using Humanizer; +using osu.Framework.Localisation; namespace osu.Game.Utils { @@ -13,9 +13,8 @@ namespace osu.Game.Utils /// Turns the provided accuracy into a percentage with 2 decimal places. /// /// The accuracy to be formatted. - /// An optional format provider. /// formatted accuracy in percentage - public static string FormatAccuracy(this double accuracy, IFormatProvider formatProvider = null) + public static LocalisableString FormatAccuracy(this double accuracy) { // for the sake of display purposes, we don't want to show a user a "rounded up" percentage to the next whole number. // ie. a score which gets 89.99999% shouldn't ever show as 90%. @@ -23,7 +22,7 @@ namespace osu.Game.Utils // percentile with a non-matching grade is confusing. accuracy = Math.Floor(accuracy * 10000) / 10000; - return accuracy.ToString("0.00%", formatProvider ?? CultureInfo.CurrentCulture); + return accuracy.ToLocalisableString("0.00%"); } /// From a3f9d96a8ee1e656a1b07c3318a0a247a1351ee5 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 23 Jul 2021 23:12:22 +0200 Subject: [PATCH 0628/2442] Localise collapsed header container. --- osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs | 5 +++-- .../Profile/Header/Components/OverlinedInfoContainer.cs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs index 4195b0b2f1..e29f40f1a5 100644 --- a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; +using osu.Framework.Localisation; using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Resources.Localisation.Web; using osu.Game.Users; @@ -145,8 +146,8 @@ namespace osu.Game.Overlays.Profile.Header private void updateDisplay(User user) { - hiddenDetailGlobal.Content = user?.Statistics?.GlobalRank?.ToString("\\##,##0") ?? "-"; - hiddenDetailCountry.Content = user?.Statistics?.CountryRank?.ToString("\\##,##0") ?? "-"; + hiddenDetailGlobal.Content = user?.Statistics?.GlobalRank != null ? (LocalisableString)user?.Statistics?.GlobalRank.ToLocalisableString("\\##,##0") : "-"; + hiddenDetailCountry.Content = user?.Statistics?.CountryRank != null ? (LocalisableString)user?.Statistics?.CountryRank.ToLocalisableString("\\##,##0") : "-"; } } } diff --git a/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs b/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs index 8f1bbc4097..5ef8482b47 100644 --- a/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs +++ b/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Profile.Header.Components set => title.Text = value; } - public string Content + public LocalisableString Content { set => content.Text = value; } From 48c21674ed97612bfb71d4fdef38ffb853faba8e Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 23 Jul 2021 23:19:51 +0200 Subject: [PATCH 0629/2442] Localise expanded header container. --- .../Overlays/Profile/Header/DetailHeaderContainer.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs index 6214e504b0..a81396d3b8 100644 --- a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs @@ -7,6 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.Leaderboards; @@ -172,13 +173,13 @@ namespace osu.Game.Overlays.Profile.Header private void updateDisplay(User user) { medalInfo.Content = user?.Achievements?.Length.ToString() ?? "0"; - ppInfo.Content = user?.Statistics?.PP?.ToString("#,##0") ?? "0"; + ppInfo.Content = user?.Statistics?.PP != null ? (LocalisableString)user?.Statistics?.PP?.ToLocalisableString("#,##0") : "0"; foreach (var scoreRankInfo in scoreRankInfos) scoreRankInfo.Value.RankCount = user?.Statistics?.GradesCount[scoreRankInfo.Key] ?? 0; - detailGlobalRank.Content = user?.Statistics?.GlobalRank?.ToString("\\##,##0") ?? "-"; - detailCountryRank.Content = user?.Statistics?.CountryRank?.ToString("\\##,##0") ?? "-"; + detailGlobalRank.Content = user?.Statistics?.GlobalRank != null ? (LocalisableString)user?.Statistics?.GlobalRank?.ToLocalisableString("\\##,##0") : "-"; + detailCountryRank.Content = user?.Statistics?.CountryRank != null ? (LocalisableString)user?.Statistics?.CountryRank?.ToLocalisableString("\\##,##0") : "-"; rankGraph.Statistics.Value = user?.Statistics; } @@ -189,7 +190,7 @@ namespace osu.Game.Overlays.Profile.Header public int RankCount { - set => rankCount.Text = value.ToString("#,##0"); + set => rankCount.Text = value.ToLocalisableString("#,##0"); } public ScoreRankInfo(ScoreRank rank) From 011fad167d696b6affac29fa3dce839e03ab1a87 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 23 Jul 2021 23:38:31 +0200 Subject: [PATCH 0630/2442] Localise rank graph tooltip. --- osu.Game/Overlays/Profile/Header/Components/RankGraph.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs index 6bf356c0ff..74a25591b4 100644 --- a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs @@ -7,6 +7,7 @@ using System.Linq; using Humanizer; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Resources.Localisation.Web; @@ -65,7 +66,7 @@ namespace osu.Game.Overlays.Profile.Header.Components return new TooltipDisplayContent { - Rank = $"#{rank:N0}", + Rank = rank.ToLocalisableString("\\##,##0"), Time = days == 0 ? "now" : $"{"day".ToQuantity(days)} ago" }; } @@ -92,7 +93,7 @@ namespace osu.Game.Overlays.Profile.Header.Components private class TooltipDisplayContent { - public string Rank; + public LocalisableString Rank; public string Time; } } From dce47917fdb5a6b3605ef264cf181acc111c2beb Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Fri, 23 Jul 2021 19:04:12 -0700 Subject: [PATCH 0631/2442] Fix ruleset icons overflowing from settings footer --- osu.Game/Overlays/Settings/SettingsFooter.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsFooter.cs b/osu.Game/Overlays/Settings/SettingsFooter.cs index a815480094..ed49ce2b63 100644 --- a/osu.Game/Overlays/Settings/SettingsFooter.cs +++ b/osu.Game/Overlays/Settings/SettingsFooter.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Settings RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Direction = FillDirection.Vertical; - Padding = new MarginPadding { Top = 20, Bottom = 30 }; + Padding = new MarginPadding { Top = 20, Bottom = 30, Horizontal = SettingsPanel.CONTENT_MARGINS }; var modes = new List(); @@ -32,6 +32,8 @@ namespace osu.Game.Overlays.Settings { var icon = new ConstrainedIconContainer { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, Icon = ruleset.CreateInstance().CreateIcon(), Colour = Color4.Gray, Size = new Vector2(20), @@ -47,7 +49,8 @@ namespace osu.Game.Overlays.Settings Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Direction = FillDirection.Full, - AutoSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Children = modes, Spacing = new Vector2(5), Padding = new MarginPadding { Bottom = 10 }, From d23e47c253ecdd684702d8ee5b0ada0b4f12ec05 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 24 Jul 2021 10:03:13 +0200 Subject: [PATCH 0632/2442] Localise level progess bar stats. --- osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs index ed89d78a10..877637be22 100644 --- a/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs +++ b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs @@ -61,7 +61,7 @@ namespace osu.Game.Overlays.Profile.Header.Components private void updateProgress(User user) { levelProgressBar.Length = user?.Statistics?.Level.Progress / 100f ?? 0; - levelProgressText.Text = user?.Statistics?.Level.Progress.ToString("0'%'"); + levelProgressText.Text = user?.Statistics?.Level.Progress.ToLocalisableString("0'%'"); } } } From c92f69467aebbe47f572fff3a2896c1a21177a19 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 24 Jul 2021 10:06:31 +0200 Subject: [PATCH 0633/2442] Localise counter pills. --- osu.Game/Overlays/Profile/Sections/CounterPill.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/CounterPill.cs b/osu.Game/Overlays/Profile/Sections/CounterPill.cs index ca8abcfe5a..34211b40b7 100644 --- a/osu.Game/Overlays/Profile/Sections/CounterPill.cs +++ b/osu.Game/Overlays/Profile/Sections/CounterPill.cs @@ -8,6 +8,7 @@ using osu.Game.Graphics; using osu.Framework.Bindables; using osu.Game.Graphics.Sprites; using osu.Framework.Allocation; +using osu.Framework.Localisation; namespace osu.Game.Overlays.Profile.Sections { @@ -48,7 +49,7 @@ namespace osu.Game.Overlays.Profile.Sections private void onCurrentChanged(ValueChangedEvent value) { - counter.Text = value.NewValue.ToString("N0"); + counter.Text = value.NewValue.ToLocalisableString("N0"); } } } From 6095aa279176b7e56f2d7bc0d15170e5c579858a Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 24 Jul 2021 10:08:47 +0200 Subject: [PATCH 0634/2442] Localise profile line chart. --- .../Overlays/Profile/Sections/Historical/ProfileLineChart.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index e6fd09301e..449b1da35d 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -170,7 +170,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical Origin = Anchor.CentreRight, RelativePositionAxes = Axes.Y, Margin = new MarginPadding { Right = 3 }, - Text = value.ToString("N0"), + Text = value.ToLocalisableString("N0"), Font = OsuFont.GetFont(size: 12), Y = y }); @@ -193,7 +193,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { Origin = Anchor.CentreLeft, RelativePositionAxes = Axes.X, - Text = value.ToString("MMM yyyy"), + Text = value.ToLocalisableString("MMM yyyy"), Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), Rotation = 45, X = x From be26414fe39cc6d05eed2fce4ce26ab66afd1d49 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 24 Jul 2021 10:13:20 +0200 Subject: [PATCH 0635/2442] Localise user history graph. --- .../Profile/Sections/Historical/UserHistoryGraph.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs b/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs index d626c63fed..ac94f0fc87 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs @@ -34,8 +34,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical return new TooltipDisplayContent { Name = tooltipCounterName, - Count = playCount.ToString("N0"), - Date = date.ToString("MMMM yyyy") + Count = playCount.ToLocalisableString("N0"), + Date = date.ToLocalisableString("MMMM yyyy") }; } @@ -63,8 +63,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical private class TooltipDisplayContent { public LocalisableString Name; - public string Count; - public string Date; + public LocalisableString Count; + public LocalisableString Date; } } } From 5b55366178da454fd9f1f007a706150b3874e4af Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 24 Jul 2021 10:14:13 +0200 Subject: [PATCH 0636/2442] Localise profile header stat buttons. --- .../Profile/Header/Components/ProfileHeaderStatisticsButton.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs index b65d5e2329..1235836aac 100644 --- a/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs @@ -4,6 +4,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK; @@ -46,6 +47,6 @@ namespace osu.Game.Overlays.Profile.Header.Components protected abstract IconUsage Icon { get; } - protected void SetValue(int value) => drawableText.Text = value.ToString("#,##0"); + protected void SetValue(int value) => drawableText.Text = value.ToLocalisableString("#,##0"); } } From eba78317d55e4562c46a95205637c973ada48145 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 24 Jul 2021 10:16:47 +0200 Subject: [PATCH 0637/2442] Localise kudosu info stats. --- osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs index 37de669b3b..eb55a0a78d 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs @@ -55,7 +55,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu public new int Count { - set => valueText.Text = value.ToString("N0"); + set => valueText.Text = value.ToLocalisableString("N0"); } public CountSection(LocalisableString header) From 48120faeb2f669ab97e6fba4dc9e68f023d9d876 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 24 Jul 2021 19:21:16 +0900 Subject: [PATCH 0638/2442] Fix inability to join a multiplayer room which has no password --- osu.Game/Online/Rooms/JoinRoomRequest.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Rooms/JoinRoomRequest.cs b/osu.Game/Online/Rooms/JoinRoomRequest.cs index d9d4f2eb5c..2a3480c992 100644 --- a/osu.Game/Online/Rooms/JoinRoomRequest.cs +++ b/osu.Game/Online/Rooms/JoinRoomRequest.cs @@ -22,7 +22,8 @@ namespace osu.Game.Online.Rooms { var req = base.CreateWebRequest(); req.Method = HttpMethod.Put; - req.AddParameter(@"password", Password, RequestParameterType.Query); + if (!string.IsNullOrEmpty(Password)) + req.AddParameter(@"password", Password, RequestParameterType.Query); return req; } From e301a996077609f92e1fa6244c4570e96b93ccd0 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 24 Jul 2021 12:39:24 +0200 Subject: [PATCH 0639/2442] Fix accuracy format unit tests. --- osu.Game.Tests/NonVisual/FormatUtilsTest.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Tests/NonVisual/FormatUtilsTest.cs b/osu.Game.Tests/NonVisual/FormatUtilsTest.cs index df095ddee3..d69822cdc5 100644 --- a/osu.Game.Tests/NonVisual/FormatUtilsTest.cs +++ b/osu.Game.Tests/NonVisual/FormatUtilsTest.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.Globalization; using NUnit.Framework; using osu.Game.Utils; @@ -20,7 +19,7 @@ namespace osu.Game.Tests.NonVisual [TestCase(1, "100.00%")] public void TestAccuracyFormatting(double input, string expectedOutput) { - Assert.AreEqual(expectedOutput, input.FormatAccuracy(CultureInfo.InvariantCulture)); + Assert.AreEqual(expectedOutput, input.FormatAccuracy().ToString()); } } } From fa68caa892517f08e9eb4243d7570eb3b3ddceb5 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 24 Jul 2021 19:34:12 +0200 Subject: [PATCH 0640/2442] Fix CI inspections. --- osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs | 4 ++-- osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs index e29f40f1a5..f8cf07c69c 100644 --- a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs @@ -146,8 +146,8 @@ namespace osu.Game.Overlays.Profile.Header private void updateDisplay(User user) { - hiddenDetailGlobal.Content = user?.Statistics?.GlobalRank != null ? (LocalisableString)user?.Statistics?.GlobalRank.ToLocalisableString("\\##,##0") : "-"; - hiddenDetailCountry.Content = user?.Statistics?.CountryRank != null ? (LocalisableString)user?.Statistics?.CountryRank.ToLocalisableString("\\##,##0") : "-"; + hiddenDetailGlobal.Content = user?.Statistics?.GlobalRank != null ? (LocalisableString)user.Statistics.GlobalRank.ToLocalisableString("\\##,##0") : "-"; + hiddenDetailCountry.Content = user?.Statistics?.CountryRank != null ? (LocalisableString)user.Statistics.CountryRank.ToLocalisableString("\\##,##0") : "-"; } } } diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs index a81396d3b8..1ddfac3609 100644 --- a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs @@ -178,8 +178,8 @@ namespace osu.Game.Overlays.Profile.Header foreach (var scoreRankInfo in scoreRankInfos) scoreRankInfo.Value.RankCount = user?.Statistics?.GradesCount[scoreRankInfo.Key] ?? 0; - detailGlobalRank.Content = user?.Statistics?.GlobalRank != null ? (LocalisableString)user?.Statistics?.GlobalRank?.ToLocalisableString("\\##,##0") : "-"; - detailCountryRank.Content = user?.Statistics?.CountryRank != null ? (LocalisableString)user?.Statistics?.CountryRank?.ToLocalisableString("\\##,##0") : "-"; + detailGlobalRank.Content = user?.Statistics?.GlobalRank != null ? (LocalisableString)user.Statistics.GlobalRank.ToLocalisableString("\\##,##0") : "-"; + detailCountryRank.Content = user?.Statistics?.CountryRank != null ? (LocalisableString)user.Statistics.CountryRank.ToLocalisableString("\\##,##0") : "-"; rankGraph.Statistics.Value = user?.Statistics; } From b0b46eed40ba77d013fb1a20c8e5d84ad9c40fad Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 24 Jul 2021 21:32:26 +0200 Subject: [PATCH 0641/2442] Apply review suggestions. Co-authored-by: Salman Ahmed --- osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs | 4 ++-- osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs index f8cf07c69c..f15fa2705a 100644 --- a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs @@ -146,8 +146,8 @@ namespace osu.Game.Overlays.Profile.Header private void updateDisplay(User user) { - hiddenDetailGlobal.Content = user?.Statistics?.GlobalRank != null ? (LocalisableString)user.Statistics.GlobalRank.ToLocalisableString("\\##,##0") : "-"; - hiddenDetailCountry.Content = user?.Statistics?.CountryRank != null ? (LocalisableString)user.Statistics.CountryRank.ToLocalisableString("\\##,##0") : "-"; + hiddenDetailGlobal.Content = user?.Statistics?.GlobalRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-"; + hiddenDetailCountry.Content = user?.Statistics?.CountryRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-"; } } } diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs index 1ddfac3609..9e52751904 100644 --- a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs @@ -173,13 +173,13 @@ namespace osu.Game.Overlays.Profile.Header private void updateDisplay(User user) { medalInfo.Content = user?.Achievements?.Length.ToString() ?? "0"; - ppInfo.Content = user?.Statistics?.PP != null ? (LocalisableString)user?.Statistics?.PP?.ToLocalisableString("#,##0") : "0"; + ppInfo.Content = user?.Statistics?.PP?.ToLocalisableString("#,##0") ?? (LocalisableString)"0"; foreach (var scoreRankInfo in scoreRankInfos) scoreRankInfo.Value.RankCount = user?.Statistics?.GradesCount[scoreRankInfo.Key] ?? 0; - detailGlobalRank.Content = user?.Statistics?.GlobalRank != null ? (LocalisableString)user.Statistics.GlobalRank.ToLocalisableString("\\##,##0") : "-"; - detailCountryRank.Content = user?.Statistics?.CountryRank != null ? (LocalisableString)user.Statistics.CountryRank.ToLocalisableString("\\##,##0") : "-"; + detailGlobalRank.Content = user?.Statistics?.GlobalRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-"; + detailCountryRank.Content = user?.Statistics?.CountryRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-"; rankGraph.Statistics.Value = user?.Statistics; } From f6d4ead32ac0a0fcf2a8234ebe1f16e0ca6fc54e Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sat, 24 Jul 2021 14:44:22 -0700 Subject: [PATCH 0642/2442] Fix mod selector overflowing from beatmap info overlay --- .../Overlays/BeatmapSet/LeaderboardModSelector.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index 98662e5dea..5b903372fd 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -26,12 +26,14 @@ namespace osu.Game.Overlays.BeatmapSet public LeaderboardModSelector() { - AutoSizeAxes = Axes.Both; + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; InternalChild = modsContainer = new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Direction = FillDirection.Full, Spacing = new Vector2(4), }; @@ -54,7 +56,12 @@ namespace osu.Game.Overlays.BeatmapSet modsContainer.Add(new ModButton(new ModNoMod())); modsContainer.AddRange(ruleset.NewValue.CreateInstance().GetAllMods().Where(m => m.UserPlayable).Select(m => new ModButton(m))); - modsContainer.ForEach(button => button.OnSelectionChanged = selectionChanged); + modsContainer.ForEach(button => + { + button.Anchor = Anchor.TopCentre; + button.Origin = Anchor.TopCentre; + button.OnSelectionChanged = selectionChanged; + }); } protected override bool OnHover(HoverEvent e) From afaf44d522b51cbc1a84adca3355f3d2605b9eed Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 25 Jul 2021 15:07:16 +0900 Subject: [PATCH 0643/2442] Update `LocalisationAnalyser` and other packages --- osu.Game/osu.Game.csproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8ccc3dfbe2..4baaf89c67 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -22,23 +22,23 @@ - - - - + + + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + From 37cb67d9e09d45be3d623a2e0f709ee3054eae26 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 25 Jul 2021 15:24:49 +0900 Subject: [PATCH 0644/2442] Also update `dotnet-tools` file --- .config/dotnet-tools.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 97fcb52ab1..007e4341b8 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -27,10 +27,10 @@ ] }, "ppy.localisationanalyser.tools": { - "version": "2021.705.0", + "version": "2021.725.0", "commands": [ "localisation" ] } } -} \ No newline at end of file +} From bb3747ffc91604bc5cab8749d9ac973c02b330c7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 25 Jul 2021 17:06:39 +0900 Subject: [PATCH 0645/2442] Fix beatmap search requests double-escaping Closes #14008. --- osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index f1cb02fb10..8ce495e274 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -59,7 +59,7 @@ namespace osu.Game.Online.API.Requests SearchPlayed played = SearchPlayed.Any, SearchExplicit explicitContent = SearchExplicit.Hide) { - this.query = string.IsNullOrEmpty(query) ? string.Empty : System.Uri.EscapeDataString(query); + this.query = query; this.ruleset = ruleset; this.cursor = cursor; @@ -78,7 +78,9 @@ namespace osu.Game.Online.API.Requests protected override WebRequest CreateWebRequest() { var req = base.CreateWebRequest(); - req.AddParameter("q", query); + + if (query != null) + req.AddParameter("q", query); if (General != null && General.Any()) req.AddParameter("c", string.Join('.', General.Select(e => e.ToString().ToLowerInvariant()))); From eb585a612039f6d6a8da0b738e5a55f13e427d18 Mon Sep 17 00:00:00 2001 From: Gabe Livengood <47010459+ggliv@users.noreply.github.com> Date: Sun, 25 Jul 2021 20:40:50 -0400 Subject: [PATCH 0646/2442] Add "Mirror" mod --- osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs | 35 ++++++++++++++++++++++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 1 + osu.Game/Rulesets/Mods/ModMirror.cs | 14 +++++++++ 3 files changed, 50 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs create mode 100644 osu.Game/Rulesets/Mods/ModMirror.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs new file mode 100644 index 0000000000..5eff999343 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs @@ -0,0 +1,35 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.UI; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModMirror : ModMirror, IApplicableToHitObject + { + public void ApplyToHitObject(HitObject hitObject) + { + var osuObject = (OsuHitObject)hitObject; + + osuObject.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - osuObject.X, osuObject.Position.Y); + + if (!(hitObject is Slider slider)) + return; + + slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y)); + slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y)); + + var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); + foreach (var point in controlPoints) + point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y); + + slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value); + } + } +} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 5f37b0d040..27794d6010 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -166,6 +166,7 @@ namespace osu.Game.Rulesets.Osu new OsuModDifficultyAdjust(), new OsuModClassic(), new OsuModRandom(), + new OsuModMirror(), }; case ModType.Automation: diff --git a/osu.Game/Rulesets/Mods/ModMirror.cs b/osu.Game/Rulesets/Mods/ModMirror.cs new file mode 100644 index 0000000000..c4a7738303 --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModMirror.cs @@ -0,0 +1,14 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModMirror : Mod + { + public override string Name => "Mirror"; + public override string Acronym => "MR"; + public override ModType Type => ModType.Conversion; + public override string Description => "Flips the beatmap horizontally."; + public override double ScoreMultiplier => 1; + } +} From 2e1cd4a389e8dce86a3f865b99dbed5c0b4b438d Mon Sep 17 00:00:00 2001 From: Gabe Livengood <47010459+ggliv@users.noreply.github.com> Date: Sun, 25 Jul 2021 21:26:21 -0400 Subject: [PATCH 0647/2442] remove accidental tab characters --- osu.Game/Rulesets/Mods/ModMirror.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModMirror.cs b/osu.Game/Rulesets/Mods/ModMirror.cs index c4a7738303..4b03d6795d 100644 --- a/osu.Game/Rulesets/Mods/ModMirror.cs +++ b/osu.Game/Rulesets/Mods/ModMirror.cs @@ -9,6 +9,6 @@ namespace osu.Game.Rulesets.Mods public override string Acronym => "MR"; public override ModType Type => ModType.Conversion; public override string Description => "Flips the beatmap horizontally."; - public override double ScoreMultiplier => 1; + public override double ScoreMultiplier => 1; } } From 5141bf66eb56000e9585325c001e00841400348c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 26 Jul 2021 04:28:25 +0300 Subject: [PATCH 0648/2442] Add failing test case --- .../TestScenePlaylistsLoungeSubScreen.cs | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs index 7bf161d1d0..79ba6d9660 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs @@ -10,6 +10,7 @@ using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Playlists; using osu.Game.Tests.Visual.OnlinePlay; +using osuTK.Input; namespace osu.Game.Tests.Visual.Playlists { @@ -30,17 +31,35 @@ namespace osu.Game.Tests.Visual.Playlists private RoomsContainer roomsContainer => loungeScreen.ChildrenOfType().First(); + [Test] + public void TestScrollByDraggingRooms() + { + AddStep("reset mouse", () => InputManager.ReleaseButton(MouseButton.Left)); + + AddStep("add rooms", () => RoomManager.AddRooms(30)); + + AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms[0])); + + AddStep("move mouse to third room", () => InputManager.MoveMouseTo(roomsContainer.Rooms[2])); + AddStep("hold down", () => InputManager.PressButton(MouseButton.Left)); + AddStep("drag to top", () => InputManager.MoveMouseTo(roomsContainer.Rooms[0])); + + AddAssert("first and second room masked", () + => !checkRoomVisible(roomsContainer.Rooms[0]) && + !checkRoomVisible(roomsContainer.Rooms[1])); + } + [Test] public void TestScrollSelectedIntoView() { AddStep("add rooms", () => RoomManager.AddRooms(30)); - AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms.First())); + AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms[0])); - AddStep("select last room", () => roomsContainer.Rooms.Last().Click()); + AddStep("select last room", () => roomsContainer.Rooms[^1].Click()); - AddUntilStep("first room is masked", () => !checkRoomVisible(roomsContainer.Rooms.First())); - AddUntilStep("last room is not masked", () => checkRoomVisible(roomsContainer.Rooms.Last())); + AddUntilStep("first room is masked", () => !checkRoomVisible(roomsContainer.Rooms[0])); + AddUntilStep("last room is not masked", () => checkRoomVisible(roomsContainer.Rooms[^1])); } private bool checkRoomVisible(DrawableRoom room) => From 749d7a7b24e329fa68a2a706e721bb729bac9338 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 26 Jul 2021 04:10:49 +0300 Subject: [PATCH 0649/2442] Fix `DrawableRoom` swallowing mouse down events before reaching its container --- .../Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 940ae873ec..adff1cc33e 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -275,14 +275,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components protected override bool ShouldBeConsideredForInput(Drawable child) => state == SelectionState.Selected; - protected override bool OnMouseDown(MouseDownEvent e) - { - if (selectedRoom.Value != Room) - return true; - - return base.OnMouseDown(e); - } - protected override bool OnClick(ClickEvent e) { if (Room != selectedRoom.Value) From 971a67c669e8970c8810f24d17ec88cf26b26494 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Jul 2021 13:31:01 +0900 Subject: [PATCH 0650/2442] Add failing test coverage for misordered rooms --- .../TestSceneLoungeRoomsContainer.cs | 30 +++++++++++++++++++ .../Visual/OnlinePlay/BasicTestRoomManager.cs | 22 ++++++++++---- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs index 4d5bf8f225..0a23550f5d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs @@ -4,6 +4,8 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; using osu.Game.Online.Rooms; using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Osu; @@ -62,6 +64,31 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("last room selected", () => checkRoomSelected(RoomManager.Rooms.Last())); } + [Test] + public void TestKeyboardNavigationAfterOrderChange() + { + AddStep("add rooms", () => RoomManager.AddRooms(3)); + + AddStep("reorder rooms", () => + { + var room = RoomManager.Rooms[1]; + + RoomManager.RemoveRoom(room); + RoomManager.AddRoom(room); + }); + + AddAssert("no selection", () => checkRoomSelected(null)); + + press(Key.Down); + AddAssert("first room selected", () => checkRoomSelected(getRoomInFlow(0))); + + press(Key.Down); + AddAssert("second room selected", () => checkRoomSelected(getRoomInFlow(1))); + + press(Key.Down); + AddAssert("third room selected", () => checkRoomSelected(getRoomInFlow(2))); + } + [Test] public void TestClickDeselection() { @@ -121,5 +148,8 @@ namespace osu.Game.Tests.Visual.Multiplayer } private bool checkRoomSelected(Room room) => SelectedRoom.Value == room; + + private Room getRoomInFlow(int index) => + (container.ChildrenOfType>().First().FlowingChildren.ElementAt(index) as DrawableRoom)?.Room; } } diff --git a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs index 82c7266598..d37a64fa4b 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs @@ -18,11 +18,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay /// public class BasicTestRoomManager : IRoomManager { - public event Action RoomsUpdated - { - add { } - remove { } - } + public event Action RoomsUpdated; public readonly BindableList Rooms = new BindableList(); @@ -35,8 +31,21 @@ namespace osu.Game.Tests.Visual.OnlinePlay public void CreateRoom(Room room, Action onSuccess = null, Action onError = null) { room.RoomID.Value ??= Rooms.Select(r => r.RoomID.Value).Where(id => id != null).Select(id => id.Value).DefaultIfEmpty().Max() + 1; - Rooms.Add(room); onSuccess?.Invoke(room); + + AddRoom(room); + } + + public void AddRoom(Room room) + { + Rooms.Add(room); + RoomsUpdated?.Invoke(); + } + + public void RemoveRoom(Room room) + { + Rooms.Remove(room); + RoomsUpdated?.Invoke(); } public void JoinRoom(Room room, string password, Action onSuccess = null, Action onError = null) @@ -56,6 +65,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay var room = new Room { RoomID = { Value = i }, + Position = { Value = i }, Name = { Value = $"Room {i}" }, Host = { Value = new User { Username = "Host" } }, EndDate = { Value = DateTimeOffset.Now + TimeSpan.FromSeconds(10) }, From 3770193edee3bf4ca6449dccc6d4c3b8e8c08b49 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Jul 2021 13:22:11 +0900 Subject: [PATCH 0651/2442] Fix keyboard navigation at multiplayer lounge not iterating in correct order --- .../Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index 07e412ee75..d2253b2d2c 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -27,7 +27,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private readonly IBindableList rooms = new BindableList(); private readonly FillFlowContainer roomFlow; - public IReadOnlyList Rooms => roomFlow; + + public IReadOnlyList Rooms => roomFlow.FlowingChildren.Cast().ToArray(); [Resolved(CanBeNull = true)] private Bindable filter { get; set; } From 32e29d042894e36ea86654dc855b8544a3d2721d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Jul 2021 14:29:16 +0900 Subject: [PATCH 0652/2442] Ensure lounge is loaded before continuing with tests --- osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 36dd9c2de3..e18fa96218 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -79,6 +79,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("load multiplayer", () => LoadScreen(multiplayerScreen)); AddUntilStep("wait for multiplayer to load", () => multiplayerScreen.IsLoaded); + AddUntilStep("wait for lounge to load", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); } [Test] From 888954747cfd8dcc9d4561d5d97cad9e57ed78a6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Jul 2021 15:47:13 +0900 Subject: [PATCH 0653/2442] Rename class, add commenting and avoid firing requests to create rooms for testing purposes --- .../Multiplayer/TestSceneMultiplayer.cs | 12 ++++---- .../Navigation/TestSceneScreenNavigation.cs | 4 +-- .../IMultiplayerTestSceneDependencies.cs | 2 +- .../Multiplayer/MultiplayerTestScene.cs | 2 +- .../MultiplayerTestSceneDependencies.cs | 4 +-- .../Multiplayer/TestMultiplayerClient.cs | 4 +-- ...tRequestHandlingMultiplayerRoomManager.cs} | 29 ++++++++++++++----- 7 files changed, 36 insertions(+), 21 deletions(-) rename osu.Game/Tests/Visual/Multiplayer/{TestMultiplayerRoomManager.cs => TestRequestHandlingMultiplayerRoomManager.cs} (85%) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index e18fa96218..7c151ffac3 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -140,7 +140,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("create room", () => { - API.Queue(new CreateRoomRequest(new Room + multiplayerScreen.RoomManager.AddRoom(new Room { Name = { Value = "Test Room" }, Playlist = @@ -151,7 +151,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Ruleset = { Value = new OsuRuleset().RulesetInfo }, } } - })); + }); }); AddStep("refresh rooms", () => multiplayerScreen.RoomManager.Filter.Value = new FilterCriteria()); @@ -187,7 +187,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("create room", () => { - API.Queue(new CreateRoomRequest(new Room + multiplayerScreen.RoomManager.AddRoom(new Room { Name = { Value = "Test Room" }, Password = { Value = "password" }, @@ -199,7 +199,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Ruleset = { Value = new OsuRuleset().RulesetInfo }, } } - })); + }); }); AddStep("refresh rooms", () => multiplayerScreen.RoomManager.Filter.Value = new FilterCriteria()); @@ -433,9 +433,9 @@ namespace osu.Game.Tests.Visual.Multiplayer private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer { - public new TestMultiplayerRoomManager RoomManager { get; private set; } + public new TestRequestHandlingMultiplayerRoomManager RoomManager { get; private set; } - protected override RoomManager CreateRoomManager() => RoomManager = new TestMultiplayerRoomManager(); + protected override RoomManager CreateRoomManager() => RoomManager = new TestRequestHandlingMultiplayerRoomManager(); } } } diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 52401d32e5..7188a4e57f 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -353,10 +353,10 @@ namespace osu.Game.Tests.Visual.Navigation public TestMultiplayer() { - Client = new TestMultiplayerClient((TestMultiplayerRoomManager)RoomManager); + Client = new TestMultiplayerClient((TestRequestHandlingMultiplayerRoomManager)RoomManager); } - protected override RoomManager CreateRoomManager() => new TestMultiplayerRoomManager(); + protected override RoomManager CreateRoomManager() => new TestRequestHandlingMultiplayerRoomManager(); } } } diff --git a/osu.Game/Tests/Visual/Multiplayer/IMultiplayerTestSceneDependencies.cs b/osu.Game/Tests/Visual/Multiplayer/IMultiplayerTestSceneDependencies.cs index 204c189591..3362ebbbd6 100644 --- a/osu.Game/Tests/Visual/Multiplayer/IMultiplayerTestSceneDependencies.cs +++ b/osu.Game/Tests/Visual/Multiplayer/IMultiplayerTestSceneDependencies.cs @@ -22,7 +22,7 @@ namespace osu.Game.Tests.Visual.Multiplayer /// /// The cached . /// - new TestMultiplayerRoomManager RoomManager { get; } + new TestRequestHandlingMultiplayerRoomManager RoomManager { get; } /// /// The cached . diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs index b7d3793ab1..42345b7266 100644 --- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs +++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs @@ -18,7 +18,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public const int PLAYER_2_ID = 56; public TestMultiplayerClient Client => OnlinePlayDependencies.Client; - public new TestMultiplayerRoomManager RoomManager => OnlinePlayDependencies.RoomManager; + public new TestRequestHandlingMultiplayerRoomManager RoomManager => OnlinePlayDependencies.RoomManager; public TestUserLookupCache LookupCache => OnlinePlayDependencies?.LookupCache; public TestSpectatorClient SpectatorClient => OnlinePlayDependencies?.SpectatorClient; diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestSceneDependencies.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestSceneDependencies.cs index a2b0b066a7..2e13fb6a56 100644 --- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestSceneDependencies.cs +++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestSceneDependencies.cs @@ -18,7 +18,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public TestMultiplayerClient Client { get; } public TestUserLookupCache LookupCache { get; } public TestSpectatorClient SpectatorClient { get; } - public new TestMultiplayerRoomManager RoomManager => (TestMultiplayerRoomManager)base.RoomManager; + public new TestRequestHandlingMultiplayerRoomManager RoomManager => (TestRequestHandlingMultiplayerRoomManager)base.RoomManager; public MultiplayerTestSceneDependencies() { @@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.Multiplayer CacheAs(SpectatorClient); } - protected override IRoomManager CreateRoomManager() => new TestMultiplayerRoomManager(); + protected override IRoomManager CreateRoomManager() => new TestRequestHandlingMultiplayerRoomManager(); protected virtual TestSpectatorClient CreateSpectatorClient() => new TestSpectatorClient(); } diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 1528ed0bc8..3349d670c8 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -38,9 +38,9 @@ namespace osu.Game.Tests.Visual.Multiplayer [Resolved] private BeatmapManager beatmaps { get; set; } = null!; - private readonly TestMultiplayerRoomManager roomManager; + private readonly TestRequestHandlingMultiplayerRoomManager roomManager; - public TestMultiplayerClient(TestMultiplayerRoomManager roomManager) + public TestMultiplayerClient(TestRequestHandlingMultiplayerRoomManager roomManager) { this.roomManager = roomManager; } diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs b/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs similarity index 85% rename from osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs rename to osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs index 59679f3d66..2e56c8a094 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs @@ -20,7 +20,11 @@ namespace osu.Game.Tests.Visual.Multiplayer /// /// A for use in multiplayer test scenes. Should generally not be used by itself outside of a . /// - public class TestMultiplayerRoomManager : MultiplayerRoomManager + /// + /// This implementation will pretend to be a server, handling room retrieval and manipulation requests + /// and returning a roughly expected state, without the need for a server to be running. + /// + public class TestRequestHandlingMultiplayerRoomManager : MultiplayerRoomManager { [Resolved] private IAPIProvider api { get; set; } @@ -33,13 +37,16 @@ namespace osu.Game.Tests.Visual.Multiplayer public new readonly List Rooms = new List(); + private int currentRoomId; + private int currentPlaylistItemId; + [BackgroundDependencyLoader] private void load() { int currentScoreId = 0; - int currentRoomId = 0; - int currentPlaylistItemId = 0; + // Handling here is pretending to be a server, while also updating the local state to match + // how the server would eventually respond and update the RoomManager. ((DummyAPIAccess)api).HandleRequest = req => { switch (req) @@ -48,19 +55,16 @@ namespace osu.Game.Tests.Visual.Multiplayer var apiRoom = new Room(); apiRoom.CopyFrom(createRoomRequest.Room); - apiRoom.RoomID.Value ??= currentRoomId++; // Passwords are explicitly not copied between rooms. apiRoom.HasPassword.Value = !string.IsNullOrEmpty(createRoomRequest.Room.Password.Value); apiRoom.Password.Value = createRoomRequest.Room.Password.Value; - for (int i = 0; i < apiRoom.Playlist.Count; i++) - apiRoom.Playlist[i].ID = currentPlaylistItemId++; + AddRoom(apiRoom); var responseRoom = new APICreatedRoom(); responseRoom.CopyFrom(createResponseRoom(apiRoom, false)); - Rooms.Add(apiRoom); createRoomRequest.TriggerSuccess(responseRoom); return true; @@ -128,6 +132,17 @@ namespace osu.Game.Tests.Visual.Multiplayer }; } + public void AddRoom(Room room) + { + room.RoomID.Value ??= currentRoomId++; + for (int i = 0; i < room.Playlist.Count; i++) + room.Playlist[i].ID = currentPlaylistItemId++; + + Rooms.Add(room); + } + + public new void RemoveRoom(Room room) => base.RemoveRoom(room); + private Room createResponseRoom(Room room, bool withParticipants) { var responseRoom = new Room(); From 04c8ea2813659627ad7fac3a7ac05c86533d00cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Jul 2021 16:33:56 +0900 Subject: [PATCH 0654/2442] Add failing test for the global ruleset being set to an invalid value --- osu.Game.Tests/Visual/TestSceneOsuGame.cs | 44 +++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/TestSceneOsuGame.cs index 4e5e8517a4..c52d846a68 100644 --- a/osu.Game.Tests/Visual/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/TestSceneOsuGame.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; using osu.Framework.Platform; +using osu.Framework.Testing; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Configuration; @@ -32,6 +33,7 @@ using osuTK.Graphics; namespace osu.Game.Tests.Visual { [TestFixture] + [HeadlessTest] public class TestSceneOsuGame : OsuTestScene { private IReadOnlyList requiredGameDependencies => new[] @@ -83,10 +85,15 @@ namespace osu.Game.Tests.Visual typeof(PreviewTrackManager), }; + private OsuGame game; + + [Resolved] + private OsuGameBase gameBase { get; set; } + [BackgroundDependencyLoader] - private void load(GameHost host, OsuGameBase gameBase) + private void load(GameHost host) { - OsuGame game = new OsuGame(); + game = new OsuGame(); game.SetHost(host); Children = new Drawable[] @@ -100,7 +107,39 @@ namespace osu.Game.Tests.Visual }; AddUntilStep("wait for load", () => game.IsLoaded); + } + [Test] + public void TestNullRulesetHandled() + { + RulesetInfo ruleset = null; + + AddStep("store current ruleset", () => ruleset = Ruleset.Value); + AddStep("set global ruleset to null value", () => Ruleset.Value = null); + + AddAssert("ruleset still valid", () => Ruleset.Value.Available); + AddAssert("ruleset unchanged", () => ReferenceEquals(Ruleset.Value, ruleset)); + } + + [Test] + public void TestUnavailableRulesetHandled() + { + RulesetInfo ruleset = null; + + AddStep("store current ruleset", () => ruleset = Ruleset.Value); + AddStep("set global ruleset to invalid value", () => Ruleset.Value = new RulesetInfo + { + Name = "unavailable", + Available = false, + }); + + AddAssert("ruleset still valid", () => Ruleset.Value.Available); + AddAssert("ruleset unchanged", () => ReferenceEquals(Ruleset.Value, ruleset)); + } + + [Test] + public void TestAvailableDependencies() + { AddAssert("check OsuGame DI members", () => { foreach (var type in requiredGameDependencies) @@ -111,6 +150,7 @@ namespace osu.Game.Tests.Visual return true; }); + AddAssert("check OsuGameBase DI members", () => { foreach (var type in requiredGameBaseDependencies) From 046f30a268bfa27ed72bd2ebec4b71b87fd1d597 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Jul 2021 16:34:38 +0900 Subject: [PATCH 0655/2442] Reject invalid global ruleset values --- osu.Game/OsuGameBase.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 4b5fa4f62e..a7fac24351 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -476,13 +476,17 @@ namespace osu.Game private void onRulesetChanged(ValueChangedEvent r) { + if (r.NewValue?.Available != true) + { + // reject the change if the ruleset is not available. + Ruleset.Value = r.OldValue; + return; + } + var dict = new Dictionary>(); - if (r.NewValue?.Available == true) - { - foreach (ModType type in Enum.GetValues(typeof(ModType))) - dict[type] = r.NewValue.CreateInstance().GetModsFor(type).ToList(); - } + foreach (ModType type in Enum.GetValues(typeof(ModType))) + dict[type] = r.NewValue.CreateInstance().GetModsFor(type).ToList(); if (!SelectedMods.Disabled) SelectedMods.Value = Array.Empty(); From af9f910a127da8d7763279f1d4494ce2e83f5e4d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Jul 2021 16:59:27 +0900 Subject: [PATCH 0656/2442] Change `WarningText` to accept `LocalisableString` Can't work just yet, but best to have the flow in place to maintain 100% localisation on classes which were already localised. --- osu.Game/Overlays/Settings/SettingsItem.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index c60ad020f0..feba3ccd46 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -61,12 +61,17 @@ namespace osu.Game.Overlays.Settings /// Text to be displayed at the bottom of this . /// Generally used to recommend the user change their setting as the current one is considered sub-optimal. /// - public string WarningText + public LocalisableString? WarningText { set { + bool hasValue = string.IsNullOrWhiteSpace(value.ToString()); + if (warningText == null) { + if (!hasValue) + return; + // construct lazily for cases where the label is not needed (may be provided by the Control). FlowContent.Add(warningText = new OsuTextFlowContainer { @@ -77,8 +82,8 @@ namespace osu.Game.Overlays.Settings }); } - warningText.Alpha = string.IsNullOrWhiteSpace(value) ? 0 : 1; - warningText.Text = value; + warningText.Alpha = hasValue ? 0 : 1; + warningText.Text = value.ToString(); // TODO: Add localisation support after https://github.com/ppy/osu-framework/pull/4603 is merged. } } From b70bd7689e6a2d6202e91070200af1d78c3a3bc3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Jul 2021 16:59:59 +0900 Subject: [PATCH 0657/2442] Add warning about using high precision mouse on macOS --- osu.Game/Localisation/MouseSettingsStrings.cs | 5 +++++ .../Settings/Sections/Input/MouseSettings.cs | 16 +++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/osu.Game/Localisation/MouseSettingsStrings.cs b/osu.Game/Localisation/MouseSettingsStrings.cs index 9b1f7fe4c5..fbc48e7d74 100644 --- a/osu.Game/Localisation/MouseSettingsStrings.cs +++ b/osu.Game/Localisation/MouseSettingsStrings.cs @@ -54,6 +54,11 @@ namespace osu.Game.Localisation /// public static LocalisableString CursorSensitivity => new TranslatableString(getKey(@"cursor_sensitivity"), @"Cursor sensitivity"); + /// + /// "This setting currently has issues on macOS. It is recommended to adjust sensitivity externally and keep this disabled for now." + /// + public static LocalisableString HighPrecisionMacOSWarning => new TranslatableString(getKey(@"high_precision_macos_warning"), @"This setting currently has issues on macOS. It is recommended to adjust sensitivity externally and keep this disabled for now."); + private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index 753096a207..7047412c9b 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Configuration; @@ -28,6 +29,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input private SettingsEnumDropdown confineMouseModeSetting; private Bindable relativeMode; + private SettingsCheckbox highPrecisionMouse; + public MouseSettings(MouseHandler mouseHandler) { this.mouseHandler = mouseHandler; @@ -45,7 +48,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input Children = new Drawable[] { - new SettingsCheckbox + highPrecisionMouse = new SettingsCheckbox { LabelText = MouseSettingsStrings.HighPrecisionMouse, TooltipText = MouseSettingsStrings.HighPrecisionMouseTooltip, @@ -107,6 +110,17 @@ namespace osu.Game.Overlays.Settings.Sections.Input confineMouseModeSetting.TooltipText = string.Empty; } }, true); + + highPrecisionMouse.Current.BindValueChanged(highPrecision => + { + if (RuntimeInfo.OS == RuntimeInfo.Platform.macOS) + { + if (highPrecision.NewValue) + highPrecisionMouse.WarningText = MouseSettingsStrings.HighPrecisionMacOSWarning; + else + highPrecisionMouse.WarningText = null; + } + }, true); } private class SensitivitySetting : SettingsSlider From 075507648a946da9ac0acabb7dca749d2b800a52 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Jul 2021 17:24:43 +0900 Subject: [PATCH 0658/2442] Show warning for linux as well --- osu.Game/Localisation/MouseSettingsStrings.cs | 4 ++-- osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Localisation/MouseSettingsStrings.cs b/osu.Game/Localisation/MouseSettingsStrings.cs index fbc48e7d74..1dd4d8e343 100644 --- a/osu.Game/Localisation/MouseSettingsStrings.cs +++ b/osu.Game/Localisation/MouseSettingsStrings.cs @@ -55,9 +55,9 @@ namespace osu.Game.Localisation public static LocalisableString CursorSensitivity => new TranslatableString(getKey(@"cursor_sensitivity"), @"Cursor sensitivity"); /// - /// "This setting currently has issues on macOS. It is recommended to adjust sensitivity externally and keep this disabled for now." + /// "This setting currently has issues on your platform. It is recommended to adjust sensitivity externally and keep this disabled for now." /// - public static LocalisableString HighPrecisionMacOSWarning => new TranslatableString(getKey(@"high_precision_macos_warning"), @"This setting currently has issues on macOS. It is recommended to adjust sensitivity externally and keep this disabled for now."); + public static LocalisableString HighPrecisionPlatformWarning => new TranslatableString(getKey(@"high_precision_platform_warning"), @"This setting has known issues on your platform. If you encounter problems, it is recommended to adjust sensitivity externally and keep this disabled for now."); private static string getKey(string key) => $@"{prefix}:{key}"; } diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index 7047412c9b..a4da17c5cd 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -113,10 +113,10 @@ namespace osu.Game.Overlays.Settings.Sections.Input highPrecisionMouse.Current.BindValueChanged(highPrecision => { - if (RuntimeInfo.OS == RuntimeInfo.Platform.macOS) + if (RuntimeInfo.OS != RuntimeInfo.Platform.Windows) { if (highPrecision.NewValue) - highPrecisionMouse.WarningText = MouseSettingsStrings.HighPrecisionMacOSWarning; + highPrecisionMouse.WarningText = MouseSettingsStrings.HighPrecisionPlatformWarning; else highPrecisionMouse.WarningText = null; } From c8944b62ec2a9c4a4c89e6761a6902e9f78b7f54 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Jul 2021 17:27:39 +0900 Subject: [PATCH 0659/2442] Update incorrect linked comment --- osu.Game/Overlays/Settings/SettingsItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index feba3ccd46..bd17c02af9 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -83,7 +83,7 @@ namespace osu.Game.Overlays.Settings } warningText.Alpha = hasValue ? 0 : 1; - warningText.Text = value.ToString(); // TODO: Add localisation support after https://github.com/ppy/osu-framework/pull/4603 is merged. + warningText.Text = value.ToString(); // TODO: Remove ToString() call after TextFlowContainer supports localisation (see https://github.com/ppy/osu-framework/issues/4636). } } From 5984699842064be759742e718a772df232a4ee02 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Jul 2021 17:40:07 +0900 Subject: [PATCH 0660/2442] Update comment to match updated string Co-authored-by: Dan Balasescu --- osu.Game/Localisation/MouseSettingsStrings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Localisation/MouseSettingsStrings.cs b/osu.Game/Localisation/MouseSettingsStrings.cs index 1dd4d8e343..5e894c4e0b 100644 --- a/osu.Game/Localisation/MouseSettingsStrings.cs +++ b/osu.Game/Localisation/MouseSettingsStrings.cs @@ -55,7 +55,7 @@ namespace osu.Game.Localisation public static LocalisableString CursorSensitivity => new TranslatableString(getKey(@"cursor_sensitivity"), @"Cursor sensitivity"); /// - /// "This setting currently has issues on your platform. It is recommended to adjust sensitivity externally and keep this disabled for now." + /// "This setting has known issues on your platform. If you encounter problems, it is recommended to adjust sensitivity externally and keep this disabled for now." /// public static LocalisableString HighPrecisionPlatformWarning => new TranslatableString(getKey(@"high_precision_platform_warning"), @"This setting has known issues on your platform. If you encounter problems, it is recommended to adjust sensitivity externally and keep this disabled for now."); From bb046fa3b8694a68e694e1a02aa363a44f3b78aa Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 26 Jul 2021 17:46:56 +0900 Subject: [PATCH 0661/2442] Move catcher trail generation logic to `Catcher` It resolves mutual dependency of `Catcher` and `CatcherTrailDisplay`. Trail generation logic is moved to `Catcher`. The generation logic no longer uses delayed scheduling because the hidden state is hard to manage. Instead, the last time a trail is generated is calculated and used. The new logic has a different behavior when the dash key is pressed in succession under 50ms, but it is not noticeable for normal plays. --- .../TestSceneCatchSkinConfiguration.cs | 2 +- .../TestSceneCatcher.cs | 12 ++-- .../TestSceneCatcherArea.cs | 6 +- .../TestSceneHyperDashColouring.cs | 12 ++-- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 7 +-- osu.Game.Rulesets.Catch/UI/Catcher.cs | 51 ++++++---------- .../UI/CatcherTrailDisplay.cs | 59 +++++++------------ 7 files changed, 56 insertions(+), 93 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs index 83f28086e6..58ff97d563 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Catch.Tests var skin = new TestSkin { FlipCatcherPlate = flip }; container.Child = new SkinProvidingContainer(skin) { - Child = catcher = new Catcher(new Container(), new DroppedObjectContainer()) + Child = catcher = new Catcher(new CatcherTrailDisplay(), new DroppedObjectContainer()) { Anchor = Anchor.Centre } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index b4282e6784..1e582bd9e8 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Catch.Tests [Resolved] private OsuConfigManager config { get; set; } - private Container trailContainer; + private CatcherTrailDisplay trailDisplay; private DroppedObjectContainer droppedObjectContainer; @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Catch.Tests CircleSize = 0, }; - trailContainer = new Container(); + trailDisplay = new CatcherTrailDisplay(); droppedObjectContainer = new DroppedObjectContainer(); Child = new Container @@ -54,8 +54,8 @@ namespace osu.Game.Rulesets.Catch.Tests Children = new Drawable[] { droppedObjectContainer, - catcher = new TestCatcher(trailContainer, droppedObjectContainer, difficulty), - trailContainer, + catcher = new TestCatcher(trailDisplay, droppedObjectContainer, difficulty), + trailDisplay, } }; }); @@ -294,8 +294,8 @@ namespace osu.Game.Rulesets.Catch.Tests { public IEnumerable CaughtObjects => this.ChildrenOfType(); - public TestCatcher(Container trailsTarget, DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty) - : base(trailsTarget, droppedObjectTarget, difficulty) + public TestCatcher(CatcherTrailDisplay trails, DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty) + : base(trails, droppedObjectTarget, difficulty) { } } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs index 6a518cf0ef..c97e6bf9ee 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs @@ -121,11 +121,13 @@ namespace osu.Game.Rulesets.Catch.Tests { public TestCatcherArea(BeatmapDifficulty beatmapDifficulty) { - var droppedObjectContainer = new DroppedObjectContainer(); + var trailDisplay = new CatcherTrailDisplay { Depth = -1 }; + Add(trailDisplay); + var droppedObjectContainer = new DroppedObjectContainer(); Add(droppedObjectContainer); - Catcher = new Catcher(this, droppedObjectContainer, beatmapDifficulty) + Catcher = new Catcher(trailDisplay, droppedObjectContainer, beatmapDifficulty) { X = CatchPlayfield.CENTER_X }; diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index 73797d0a6a..88ddc7e8a8 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -113,30 +113,28 @@ namespace osu.Game.Rulesets.Catch.Tests private void checkHyperDashCatcherColour(ISkin skin, Color4 expectedCatcherColour, Color4? expectedEndGlowColour = null) { - Container trailsContainer = null; - Catcher catcher = null; CatcherTrailDisplay trails = null; + Catcher catcher = null; AddStep("create hyper-dashing catcher", () => { - trailsContainer = new Container(); + trails = new CatcherTrailDisplay(); Child = setupSkinHierarchy(new Container { Anchor = Anchor.Centre, Children = new Drawable[] { - catcher = new Catcher(trailsContainer, new DroppedObjectContainer()) + catcher = new Catcher(trails, new DroppedObjectContainer()) { Scale = new Vector2(4) }, - trailsContainer + trails } }, skin); }); - AddStep("get trails container", () => + AddStep("start hyper-dash", () => { - trails = trailsContainer.OfType().Single(); catcher.SetHyperDashState(2); }); diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index b43815a8bd..bcbe7c776e 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -3,7 +3,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawables; @@ -45,14 +44,14 @@ namespace osu.Game.Rulesets.Catch.UI [BackgroundDependencyLoader] private void load() { - var trailContainer = new Container + var trailDisplay = new CatcherTrailDisplay { Anchor = Anchor.BottomLeft, Origin = Anchor.TopLeft }; var droppedObjectContainer = new DroppedObjectContainer(); - Catcher = new Catcher(trailContainer, droppedObjectContainer, difficulty) + Catcher = new Catcher(trailDisplay, droppedObjectContainer, difficulty) { X = CENTER_X }; @@ -70,7 +69,7 @@ namespace osu.Game.Rulesets.Catch.UI Origin = Anchor.TopLeft, Catcher = Catcher, }, - trailContainer, + trailDisplay, HitObjectContainer, }); diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 49508b1caf..f8b0dabeb9 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -71,10 +71,10 @@ namespace osu.Game.Rulesets.Catch.UI /// private const float caught_fruit_scale_adjust = 0.5f; - [NotNull] - private readonly Container trailsTarget; - - private CatcherTrailDisplay trails; + /// + /// Contains trails and afterimages (also called "end glow" in code) of the catcher. + /// + private readonly CatcherTrailDisplay trails; /// /// Contains caught objects on the plate. @@ -92,20 +92,7 @@ namespace osu.Game.Rulesets.Catch.UI private set => Body.AnimationState.Value = value; } - private bool dashing; - - public bool Dashing - { - get => dashing; - set - { - if (value == dashing) return; - - dashing = value; - - updateTrailVisibility(); - } - } + public bool Dashing { get; set; } /// /// The currently facing direction. @@ -138,9 +125,9 @@ namespace osu.Game.Rulesets.Catch.UI private readonly DrawablePool caughtBananaPool; private readonly DrawablePool caughtDropletPool; - public Catcher([NotNull] Container trailsTarget, [NotNull] DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty = null) + public Catcher([NotNull] CatcherTrailDisplay trails, [NotNull] DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty = null) { - this.trailsTarget = trailsTarget; + this.trails = trails; this.droppedObjectTarget = droppedObjectTarget; Origin = Anchor.TopCentre; @@ -177,15 +164,6 @@ namespace osu.Game.Rulesets.Catch.UI private void load(OsuConfigManager config) { hitLighting = config.GetBindable(OsuSetting.HitLighting); - trails = new CatcherTrailDisplay(this); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - // don't add in above load as we may potentially modify a parent in an unsafe manner. - trailsTarget.Add(trails); } /// @@ -313,7 +291,7 @@ namespace osu.Game.Rulesets.Catch.UI if (!wasHyperDashing) { - trails.DisplayEndGlow(); + trails.DisplayEndGlow(CurrentState, X, Scale * Body.Scale); runHyperDashStateTransition(true); } } @@ -331,13 +309,9 @@ namespace osu.Game.Rulesets.Catch.UI private void runHyperDashStateTransition(bool hyperDashing) { - updateTrailVisibility(); - this.FadeColour(hyperDashing ? hyperDashColour : Color4.White, HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint); } - private void updateTrailVisibility() => trails.DisplayTrail = Dashing || HyperDashing; - protected override void SkinChanged(ISkinSource skin) { base.SkinChanged(skin); @@ -373,6 +347,15 @@ namespace osu.Game.Rulesets.Catch.UI X = hyperDashTargetPosition; SetHyperDashState(); } + + if (Dashing || HyperDashing) + { + double lastTrailTime = trails.LastDashTrail?.LifetimeStart ?? double.NegativeInfinity; + double generationInterval = HyperDashing ? 25 : 50; + + if (Time.Current - lastTrailTime >= generationInterval) + trails.DisplayDashTrail(CurrentState, X, Scale * Body.Scale, HyperDashing); + } } private void placeCaughtObject(DrawablePalpableCatchHitObject drawableObject, Vector2 position) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs index b59fabcb70..347df5f114 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs @@ -1,7 +1,7 @@ // 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.Linq; using JetBrains.Annotations; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -17,7 +17,10 @@ namespace osu.Game.Rulesets.Catch.UI /// public class CatcherTrailDisplay : CompositeDrawable { - private readonly Catcher catcher; + [CanBeNull] + public CatcherTrail LastDashTrail => dashTrails.Concat(hyperDashTrails) + .OrderByDescending(trail => trail.LifetimeStart) + .FirstOrDefault(); private readonly DrawablePool trailPool; @@ -55,30 +58,8 @@ namespace osu.Game.Rulesets.Catch.UI } } - private bool trail; - - /// - /// Whether to start displaying trails following the catcher. - /// - public bool DisplayTrail + public CatcherTrailDisplay() { - get => trail; - set - { - if (trail == value) - return; - - trail = value; - - if (trail) - displayTrail(); - } - } - - public CatcherTrailDisplay([NotNull] Catcher catcher) - { - this.catcher = catcher ?? throw new ArgumentNullException(nameof(catcher)); - RelativeSizeAxes = Axes.Both; InternalChildren = new Drawable[] @@ -93,9 +74,11 @@ namespace osu.Game.Rulesets.Catch.UI /// /// Displays a single end-glow catcher sprite. /// - public void DisplayEndGlow() + public void DisplayEndGlow(CatcherAnimationState animationState, float x, Vector2 scale) { - var endGlow = createTrailSprite(endGlowSprites); + var endGlow = createTrail(animationState, x, scale); + + endGlowSprites.Add(endGlow); endGlow.MoveToOffset(new Vector2(0, -10), 1200, Easing.In); endGlow.ScaleTo(endGlow.Scale * 0.95f).ScaleTo(endGlow.Scale * 1.2f, 1200, Easing.In); @@ -103,28 +86,26 @@ namespace osu.Game.Rulesets.Catch.UI endGlow.Expire(true); } - private void displayTrail() + public void DisplayDashTrail(CatcherAnimationState animationState, float x, Vector2 scale, bool hyperDashing) { - if (!DisplayTrail) - return; + var sprite = createTrail(animationState, x, scale); - var sprite = createTrailSprite(catcher.HyperDashing ? hyperDashTrails : dashTrails); + if (hyperDashing) + hyperDashTrails.Add(sprite); + else + dashTrails.Add(sprite); sprite.FadeTo(0.4f).FadeOut(800, Easing.OutQuint); sprite.Expire(true); - - Scheduler.AddDelayed(displayTrail, catcher.HyperDashing ? 25 : 50); } - private CatcherTrail createTrailSprite(Container target) + private CatcherTrail createTrail(CatcherAnimationState animationState, float x, Vector2 scale) { CatcherTrail sprite = trailPool.Get(); - sprite.AnimationState = catcher.CurrentState; - sprite.Scale = catcher.Scale * catcher.Body.Scale; - sprite.Position = catcher.Position; - - target.Add(sprite); + sprite.AnimationState = animationState; + sprite.Scale = scale; + sprite.Position = new Vector2(x, 0); return sprite; } From c08130398cd604aa6a2235879ffc2601e8aa5c8e Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 26 Jul 2021 17:50:10 +0900 Subject: [PATCH 0662/2442] Add some comments --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 3 +++ osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index f8b0dabeb9..fa444fdad7 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -92,6 +92,9 @@ namespace osu.Game.Rulesets.Catch.UI private set => Body.AnimationState.Value = value; } + /// + /// Whether the catcher is currently dashing. + /// public bool Dashing { get; set; } /// diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs index 347df5f114..00f2a76bde 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs @@ -17,6 +17,10 @@ namespace osu.Game.Rulesets.Catch.UI /// public class CatcherTrailDisplay : CompositeDrawable { + /// + /// The most recent dash trail added in this container. + /// Only alive (not faded out) trails are considered. + /// [CanBeNull] public CatcherTrail LastDashTrail => dashTrails.Concat(hyperDashTrails) .OrderByDescending(trail => trail.LifetimeStart) From 428244227818b5267021734ab907074e49c919cb Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 26 Jul 2021 17:50:52 +0900 Subject: [PATCH 0663/2442] Make `Catcher.body` private as it is no longer needed by `CatcherTrailDisplay` --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index fa444fdad7..e7c26ee44f 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -88,8 +88,8 @@ namespace osu.Game.Rulesets.Catch.UI public CatcherAnimationState CurrentState { - get => Body.AnimationState.Value; - private set => Body.AnimationState.Value = value; + get => body.AnimationState.Value; + private set => body.AnimationState.Value = value; } /// @@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Catch.UI /// private readonly float catchWidth; - internal readonly SkinnableCatcher Body; + private readonly SkinnableCatcher body; private Color4 hyperDashColour = DEFAULT_HYPER_DASH_COLOUR; private Color4 hyperDashEndGlowColour = DEFAULT_HYPER_DASH_COLOUR; @@ -154,7 +154,7 @@ namespace osu.Game.Rulesets.Catch.UI // offset fruit vertically to better place "above" the plate. Y = -5 }, - Body = new SkinnableCatcher(), + body = new SkinnableCatcher(), hitExplosionContainer = new HitExplosionContainer { Anchor = Anchor.TopCentre, @@ -294,7 +294,7 @@ namespace osu.Game.Rulesets.Catch.UI if (!wasHyperDashing) { - trails.DisplayEndGlow(CurrentState, X, Scale * Body.Scale); + trails.DisplayEndGlow(CurrentState, X, Scale * body.Scale); runHyperDashStateTransition(true); } } @@ -340,7 +340,7 @@ namespace osu.Game.Rulesets.Catch.UI base.Update(); var scaleFromDirection = new Vector2((int)VisualDirection, 1); - Body.Scale = scaleFromDirection; + body.Scale = scaleFromDirection; caughtObjectContainer.Scale = hitExplosionContainer.Scale = flipCatcherPlate ? scaleFromDirection : Vector2.One; // Correct overshooting. @@ -357,7 +357,7 @@ namespace osu.Game.Rulesets.Catch.UI double generationInterval = HyperDashing ? 25 : 50; if (Time.Current - lastTrailTime >= generationInterval) - trails.DisplayDashTrail(CurrentState, X, Scale * Body.Scale, HyperDashing); + trails.DisplayDashTrail(CurrentState, X, Scale * body.Scale, HyperDashing); } } From 0fbe950a3c452f645f811147d624836c4ca9c996 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 14 Jun 2021 16:26:39 +0900 Subject: [PATCH 0664/2442] Modify catcher autoplay test pattern to see more variety movement --- osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs index f552c3c27b..0ca0ddfcb1 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs @@ -27,9 +27,9 @@ namespace osu.Game.Rulesets.Catch.Tests } }; - for (int i = 0; i < 100; i++) + for (int i = 0; i < 12; i++) { - float width = (i % 10 + 1) / 20f * CatchPlayfield.WIDTH; + float width = (i + 1) / 20f * CatchPlayfield.WIDTH; beatmap.HitObjects.Add(new JuiceStream { @@ -39,8 +39,8 @@ namespace osu.Game.Rulesets.Catch.Tests Vector2.Zero, new Vector2(width, 0) }), - StartTime = i * 2000, - NewCombo = i % 8 == 0, + StartTime = i * 1000, + NewCombo = i % 5 == 0, Samples = new List(new[] { new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "normal", volume: 100) From 8045534fa511d93b9e9be5c5b98ff2e2369c9bcb Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 26 Jul 2021 18:18:24 +0900 Subject: [PATCH 0665/2442] Remove outdated comment and simplify code --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 49508b1caf..b724b84a56 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -218,14 +218,9 @@ namespace osu.Game.Rulesets.Catch.UI if (!(hitObject is PalpableCatchHitObject fruit)) return false; - var halfCatchWidth = catchWidth * 0.5f; - - // this stuff wil disappear once we move fruit to non-relative coordinate space in the future. - var catchObjectPosition = fruit.EffectiveX; - var catcherPosition = Position.X; - - return catchObjectPosition >= catcherPosition - halfCatchWidth && - catchObjectPosition <= catcherPosition + halfCatchWidth; + float halfCatchWidth = catchWidth * 0.5f; + return fruit.EffectiveX >= X - halfCatchWidth && + fruit.EffectiveX <= X + halfCatchWidth; } public void OnNewResult(DrawableCatchHitObject drawableObject, JudgementResult result) From 55e8a44db605f24af80cc71c53c372b5289bc989 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Mon, 26 Jul 2021 18:15:59 +0700 Subject: [PATCH 0666/2442] add test for DrawableComment Can reproduce the issue at https://github.com/ppy/osu/issues/13993 --- .../Visual/Online/TestSceneDrawableComment.cs | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs b/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs new file mode 100644 index 0000000000..26580d02d8 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs @@ -0,0 +1,75 @@ +// 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 NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; +using osu.Game.Overlays.Comments; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneDrawableComment : OsuTestScene + { + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + + private Container container; + + [SetUp] + public void SetUp() => Schedule(() => + { + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background4, + }, + container = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }, + }; + }); + + [TestCaseSource(nameof(comments))] + public void TestComment(string description, string text) + { + AddStep(description, () => + { + comment.Message = text; + container.Add(new DrawableComment(comment)); + }); + } + + private static readonly Comment comment = new Comment + { + Id = 1, + LegacyName = "Test User", + CreatedAt = DateTimeOffset.Now, + VotesCount = 0, + }; + + private static object[] comments = + { + new[] { "Plain", "This is plain comment" }, + // Taken from https://github.com/ppy/osu/issues/13993#issuecomment-885994077 + new[] + { + "Problematic", @"My tablet doesn't work :( +It's a Huion 420 and it's apparently incompatible with OpenTablet Driver. The warning I get is: ""DeviceInUseException: Device is currently in use by another kernel module. To fix this issue, please follow the instructions from https://github.com/OpenTabletDriver/OpenTabletDriver/wiki/Linux-FAQ#arg umentoutofrangeexception-value-0-15"" and it repeats 4 times on the notification before logging subsequent warnings. +Checking the logs, it looks for other Huion tablets before sending the notification (e.g. + ""2021-07-23 03:52:33 [verbose]: Detect: Searching for tablet 'Huion WH1409 V2' + 20 2021-07-23 03:52:33 [error]: DeviceInUseException: Device is currently in use by another kernel module. To fix this issue, please follow the instructions from https://github.com/OpenTabletDriver/OpenTabletDriver/wiki/Linux-FAQ#arg umentoutofrangeexception-value-0-15"") +I use an Arch based installation of Linux and the tablet runs perfectly with Digimend kernel driver, with area configuration, pen pressure, etc. On osu!lazer the cursor disappears until I set it to ""Borderless"" instead of ""Fullscreen"" and even after it shows up, it goes to the bottom left corner as soon as a map starts. +I have honestly 0 idea of whats going on at this point." + } + }; + } +} From 43100c5288099d7c7eadb5306d61d23080cf2008 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Mon, 26 Jul 2021 18:18:33 +0700 Subject: [PATCH 0667/2442] initial CommentMarkdownContainer --- .../Comments/CommentMarkdownContainer.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 osu.Game/Overlays/Comments/CommentMarkdownContainer.cs diff --git a/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs b/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs new file mode 100644 index 0000000000..0b0d4d9a58 --- /dev/null +++ b/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Markdig.Syntax.Inlines; +using osu.Framework.Graphics.Containers.Markdown; +using osu.Game.Graphics.Containers.Markdown; + +namespace osu.Game.Overlays.Comments +{ + public class CommentMarkdownContainer : OsuMarkdownContainer + { + public override MarkdownTextFlowContainer CreateTextFlow() => new CommentMarkdownTextFlowContainer(); + + private class CommentMarkdownTextFlowContainer : OsuMarkdownTextFlowContainer + { + // Don't render image in comment for now + protected override void AddImage(LinkInline linkInline) { } + } + } +} From 2a6aeb5310809d58bcf969958ab105d47a749265 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Mon, 26 Jul 2021 18:18:55 +0700 Subject: [PATCH 0668/2442] use CommentMarkdownContainer in DrawableContainer --- osu.Game/Overlays/Comments/DrawableComment.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Comments/DrawableComment.cs b/osu.Game/Overlays/Comments/DrawableComment.cs index d94f8c4b8b..3520b15b1e 100644 --- a/osu.Game/Overlays/Comments/DrawableComment.cs +++ b/osu.Game/Overlays/Comments/DrawableComment.cs @@ -13,7 +13,6 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Bindables; using System.Linq; using osu.Game.Graphics.Sprites; -using osu.Game.Online.Chat; using osu.Framework.Allocation; using System.Collections.Generic; using System; @@ -61,7 +60,7 @@ namespace osu.Game.Overlays.Comments { LinkFlowContainer username; FillFlowContainer info; - LinkFlowContainer message; + CommentMarkdownContainer message; GridContainer content; VotePill votePill; @@ -153,10 +152,12 @@ namespace osu.Game.Overlays.Comments } } }, - message = new LinkFlowContainer(s => s.Font = OsuFont.GetFont(size: 14)) + message = new CommentMarkdownContainer { RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y + AutoSizeAxes = Axes.Y, + DocumentMargin = new MarginPadding(0), + DocumentPadding = new MarginPadding(0), }, info = new FillFlowContainer { @@ -275,10 +276,7 @@ namespace osu.Game.Overlays.Comments } if (Comment.HasMessage) - { - var formattedSource = MessageFormatter.FormatText(Comment.Message); - message.AddLinks(formattedSource.Text, formattedSource.Links); - } + message.Text = Comment.Message; if (Comment.IsDeleted) { From f80c46e2a01d94e6dd5939da4ed7eab5e1863d80 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Mon, 26 Jul 2021 21:02:57 +0700 Subject: [PATCH 0669/2442] add heading test --- .../Visual/Online/TestSceneDrawableComment.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs b/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs index 26580d02d8..b93e7a1739 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs @@ -59,6 +59,17 @@ namespace osu.Game.Tests.Visual.Online private static object[] comments = { new[] { "Plain", "This is plain comment" }, + + new[] + { + "Heading", @"# Heading 1 +## Heading 2 +### Heading 3 +#### Heading 4 +##### Heading 5 +###### Heading 6" + }, + // Taken from https://github.com/ppy/osu/issues/13993#issuecomment-885994077 new[] { From 6631f0de1958755724c8f5c375b544e79def2ec6 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Mon, 26 Jul 2021 21:07:35 +0700 Subject: [PATCH 0670/2442] add CommentMarkdownHeading --- .../Comments/CommentMarkdownContainer.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs b/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs index 0b0d4d9a58..aeab292b0d 100644 --- a/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs +++ b/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using Markdig.Syntax; using Markdig.Syntax.Inlines; using osu.Framework.Graphics.Containers.Markdown; using osu.Game.Graphics.Containers.Markdown; @@ -11,10 +12,37 @@ namespace osu.Game.Overlays.Comments { public override MarkdownTextFlowContainer CreateTextFlow() => new CommentMarkdownTextFlowContainer(); + protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) => new CommentMarkdownHeading(headingBlock); + private class CommentMarkdownTextFlowContainer : OsuMarkdownTextFlowContainer { // Don't render image in comment for now protected override void AddImage(LinkInline linkInline) { } } + + private class CommentMarkdownHeading : OsuMarkdownHeading + { + public CommentMarkdownHeading(HeadingBlock headingBlock) + : base(headingBlock) + { + } + + protected override float GetFontSizeByLevel(int level) + { + var defaultFontSize = base.GetFontSizeByLevel(6); + + switch (level) + { + case 1: + return 1.2f * defaultFontSize; + + case 2: + return 1.1f * defaultFontSize; + + default: + return defaultFontSize; + } + } + } } } From dc864abbd896662ee4d2c7f4f6aeff1e08cd500f Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Mon, 26 Jul 2021 21:08:57 +0700 Subject: [PATCH 0671/2442] add link test --- osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs b/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs index b93e7a1739..7b741accbb 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs @@ -59,6 +59,7 @@ namespace osu.Game.Tests.Visual.Online private static object[] comments = { new[] { "Plain", "This is plain comment" }, + new[] { "Link", "Please visit https://osu.ppy.sh" }, new[] { From 49160e4482a0cf54f1ccd783b773b1a35ad1d1da Mon Sep 17 00:00:00 2001 From: Gabe Livengood <47010459+ggliv@users.noreply.github.com> Date: Mon, 26 Jul 2021 10:46:41 -0400 Subject: [PATCH 0672/2442] review modifications: maniamodmirror inheritance, reflection utilities, vertical flip option --- .../Mods/ManiaModMirror.cs | 6 +-- osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs | 15 +----- osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs | 30 ++++++----- .../Utils/OsuHitObjectGenerationUtils.cs | 52 +++++++++++++++++++ osu.Game/Rulesets/Mods/ModMirror.cs | 1 - 5 files changed, 71 insertions(+), 33 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs index cf404cc98e..f01884c97f 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs @@ -10,13 +10,9 @@ using osu.Game.Rulesets.Mania.Beatmaps; namespace osu.Game.Rulesets.Mania.Mods { - public class ManiaModMirror : Mod, IApplicableToBeatmap + public class ManiaModMirror : ModMirror, IApplicableToBeatmap { - public override string Name => "Mirror"; - public override string Acronym => "MR"; - public override ModType Type => ModType.Conversion; public override string Description => "Notes are flipped horizontally."; - public override double ScoreMultiplier => 1; public void ApplyToBeatmap(IBeatmap beatmap) { diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs index 16c166257a..a693e50016 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs @@ -7,6 +7,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.Osu.Utils; using osuTK; namespace osu.Game.Rulesets.Osu.Mods @@ -19,19 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods { var osuObject = (OsuHitObject)hitObject; - osuObject.Position = new Vector2(osuObject.Position.X, OsuPlayfield.BASE_SIZE.Y - osuObject.Y); - - if (!(hitObject is Slider slider)) - return; - - slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); - slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); - - var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); - foreach (var point in controlPoints) - point.Position.Value = new Vector2(point.Position.Value.X, -point.Position.Value.Y); - - slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value); + OsuHitObjectGenerationUtils.ReflectOsuHitObjectVertically(osuObject); } } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs index 5eff999343..a8cf72a5c7 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs @@ -2,34 +2,36 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; +using osu.Framework.Bindables; +using osu.Game.Configuration; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.Osu.Utils; using osuTK; namespace osu.Game.Rulesets.Osu.Mods { public class OsuModMirror : ModMirror, IApplicableToHitObject { + public override string Description => "Reflect the playfield."; + + [SettingSource("Reflect Horizontally", "Reflect the playfield horizontally.")] + public Bindable ReflectY { get; } = new BindableBool(true); + [SettingSource("Reflect Vertically", "Reflect the playfield vertically.")] + public Bindable ReflectX { get; } = new BindableBool(false); + public void ApplyToHitObject(HitObject hitObject) { + if (!(ReflectY.Value || ReflectX.Value)) + return; // TODO deselect the mod if possible so replays and submissions don't have purposeless mods attached. var osuObject = (OsuHitObject)hitObject; - - osuObject.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - osuObject.X, osuObject.Position.Y); - - if (!(hitObject is Slider slider)) - return; - - slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y)); - slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y)); - - var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); - foreach (var point in controlPoints) - point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y); - - slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value); + if (ReflectY.Value) + OsuHitObjectGenerationUtils.ReflectOsuHitObjectHorizontally(osuObject); + if (ReflectX.Value) + OsuHitObjectGenerationUtils.ReflectOsuHitObjectVertically(osuObject); } } } diff --git a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs index 06b964a647..f4ab60367e 100644 --- a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs +++ b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs @@ -2,7 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Objects; using osuTK; namespace osu.Game.Rulesets.Osu.Utils @@ -100,5 +104,53 @@ namespace osu.Game.Rulesets.Osu.Utils initial.Length * MathF.Sin(finalAngleRad) ); } + + /// + /// Reflects an OsuHitObject's position horizontally (over the 'y axis'). + /// + /// The OsuHitObject to be reflected. + /// The reflected OsuHitObject. + public static OsuHitObject ReflectOsuHitObjectHorizontally(OsuHitObject osuObject) + { + osuObject.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - osuObject.X, osuObject.Position.Y); + + if (!(osuObject is Slider slider)) + return osuObject; + + slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y)); + slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y)); + + var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); + foreach (var point in controlPoints) + point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y); + + slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value); + + return osuObject; + } + + /// + /// Reflects an OsuHitObject's position vertically (over the 'x axis'). + /// + /// The OsuHitObject to be reflected. + /// The reflected OsuHitObject. + public static OsuHitObject ReflectOsuHitObjectVertically(OsuHitObject osuObject) + { + osuObject.Position = new Vector2(osuObject.Position.X, OsuPlayfield.BASE_SIZE.Y - osuObject.Y); + + if (!(osuObject is Slider slider)) + return osuObject; + + slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); + slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); + + var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); + foreach (var point in controlPoints) + point.Position.Value = new Vector2(point.Position.Value.X, -point.Position.Value.Y); + + slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value); + + return osuObject; + } } } diff --git a/osu.Game/Rulesets/Mods/ModMirror.cs b/osu.Game/Rulesets/Mods/ModMirror.cs index 4b03d6795d..3c4b7d0c60 100644 --- a/osu.Game/Rulesets/Mods/ModMirror.cs +++ b/osu.Game/Rulesets/Mods/ModMirror.cs @@ -8,7 +8,6 @@ namespace osu.Game.Rulesets.Mods public override string Name => "Mirror"; public override string Acronym => "MR"; public override ModType Type => ModType.Conversion; - public override string Description => "Flips the beatmap horizontally."; public override double ScoreMultiplier => 1; } } From 5b06a9d120e494fe5cabd55734cc9716ef399125 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 27 Jul 2021 03:55:49 +0900 Subject: [PATCH 0673/2442] Apply changes required for AudioMixer --- osu.Game/Audio/PreviewTrackManager.cs | 11 +++++++---- osu.Game/Tests/Visual/OsuTestScene.cs | 2 ++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game/Audio/PreviewTrackManager.cs b/osu.Game/Audio/PreviewTrackManager.cs index d88fd1e62b..e2c4657057 100644 --- a/osu.Game/Audio/PreviewTrackManager.cs +++ b/osu.Game/Audio/PreviewTrackManager.cs @@ -7,6 +7,7 @@ using System.IO; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Audio.Mixing; using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -30,11 +31,11 @@ namespace osu.Game.Audio private readonly BindableNumber globalTrackVolumeAdjust = new BindableNumber(OsuGameBase.GLOBAL_TRACK_VOLUME_ADJUST); [BackgroundDependencyLoader] - private void load() + private void load(AudioManager audioManager) { // this is a temporary solution to get around muting ourselves. // todo: update this once we have a BackgroundTrackManager or similar. - trackStore = new PreviewTrackStore(new OnlineStore()); + trackStore = new PreviewTrackStore(audioManager.Mixer, new OnlineStore()); audio.AddItem(trackStore); trackStore.AddAdjustment(AdjustableProperty.Volume, globalTrackVolumeAdjust); @@ -118,10 +119,12 @@ namespace osu.Game.Audio private class PreviewTrackStore : AudioCollectionManager, ITrackStore { + private readonly AudioMixer defaultMixer; private readonly IResourceStore store; - internal PreviewTrackStore(IResourceStore store) + internal PreviewTrackStore(AudioMixer defaultMixer, IResourceStore store) { + this.defaultMixer = defaultMixer; this.store = store; } @@ -145,7 +148,7 @@ namespace osu.Game.Audio if (dataStream == null) return null; - Track track = new TrackBass(dataStream); + Track track = new TrackBass(dataStream, (IBassAudioMixer)defaultMixer); AddItem(track); return track; } diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 57e400a77e..242c3298e8 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -10,6 +10,7 @@ using JetBrains.Annotations; using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Audio.Mixing; using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -290,6 +291,7 @@ namespace osu.Game.Tests.Visual private bool running; public TrackVirtualManual(IFrameBasedClock referenceClock) + : base(new NullAudioMixer()) { this.referenceClock = referenceClock; Length = double.PositiveInfinity; From c7c261ba037f02449d5f7dcabf9b1837839fa581 Mon Sep 17 00:00:00 2001 From: Gabe Livengood <47010459+ggliv@users.noreply.github.com> Date: Mon, 26 Jul 2021 17:48:03 -0400 Subject: [PATCH 0674/2442] review modifications: change xmldoc wording, configure with enum instead of bool, declare incompatibility with hr --- osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs | 3 ++ osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs | 34 +++++++++++++------ .../Utils/OsuHitObjectGenerationUtils.cs | 4 +-- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs index a693e50016..82b8959456 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs @@ -1,6 +1,7 @@ // 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.Linq; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Rulesets.Mods; @@ -16,6 +17,8 @@ namespace osu.Game.Rulesets.Osu.Mods { public override double ScoreMultiplier => 1.06; + public override Type[] IncompatibleMods => new[] { typeof(ModEasy), typeof(ModDifficultyAdjust), typeof(ModMirror) }; + public void ApplyToHitObject(HitObject hitObject) { var osuObject = (OsuHitObject)hitObject; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs index a8cf72a5c7..fd1d685fd1 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs @@ -1,6 +1,7 @@ // 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.Linq; using osu.Framework.Bindables; using osu.Game.Configuration; @@ -17,21 +18,34 @@ namespace osu.Game.Rulesets.Osu.Mods public class OsuModMirror : ModMirror, IApplicableToHitObject { public override string Description => "Reflect the playfield."; + public override Type[] IncompatibleMods => new[] { typeof(ModHardRock) }; - [SettingSource("Reflect Horizontally", "Reflect the playfield horizontally.")] - public Bindable ReflectY { get; } = new BindableBool(true); - [SettingSource("Reflect Vertically", "Reflect the playfield vertically.")] - public Bindable ReflectX { get; } = new BindableBool(false); + [SettingSource("Reflection", "Change the type of reflection.")] + public Bindable Reflection { get; } = new Bindable(); public void ApplyToHitObject(HitObject hitObject) { - if (!(ReflectY.Value || ReflectX.Value)) - return; // TODO deselect the mod if possible so replays and submissions don't have purposeless mods attached. var osuObject = (OsuHitObject)hitObject; - if (ReflectY.Value) - OsuHitObjectGenerationUtils.ReflectOsuHitObjectHorizontally(osuObject); - if (ReflectX.Value) - OsuHitObjectGenerationUtils.ReflectOsuHitObjectVertically(osuObject); + switch (Reflection.Value) + { + case MirrorType.Horizontal: + OsuHitObjectGenerationUtils.ReflectOsuHitObjectHorizontally(osuObject); + break; + case MirrorType.Vertical: + OsuHitObjectGenerationUtils.ReflectOsuHitObjectVertically(osuObject); + break; + case MirrorType.Both: + OsuHitObjectGenerationUtils.ReflectOsuHitObjectHorizontally(osuObject); + OsuHitObjectGenerationUtils.ReflectOsuHitObjectVertically(osuObject); + break; + } + } + + public enum MirrorType + { + Horizontal, + Vertical, + Both } } } diff --git a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs index f4ab60367e..bf55898901 100644 --- a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs +++ b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs @@ -106,7 +106,7 @@ namespace osu.Game.Rulesets.Osu.Utils } /// - /// Reflects an OsuHitObject's position horizontally (over the 'y axis'). + /// Reflects an OsuHitObject's position horizontally. /// /// The OsuHitObject to be reflected. /// The reflected OsuHitObject. @@ -130,7 +130,7 @@ namespace osu.Game.Rulesets.Osu.Utils } /// - /// Reflects an OsuHitObject's position vertically (over the 'x axis'). + /// Reflects an OsuHitObject's position vertically. /// /// The OsuHitObject to be reflected. /// The reflected OsuHitObject. From 34c671f712d71c8a3e3821d7dc45e46f055e8f1b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 27 Jul 2021 12:06:52 +0900 Subject: [PATCH 0675/2442] Temporary changes to compile with latest framework --- osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs | 4 +++- osu.Game/Overlays/Rankings/Tables/RankingsTable.cs | 3 ++- osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs | 5 +++-- osu.Game/Screens/Edit/EditorTable.cs | 4 +++- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index ddd1dfa6cd..4b44c34c6d 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -8,6 +8,8 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.Extensions.EnumExtensions; +using osu.Framework.Extensions.LocalisationExtensions; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -215,7 +217,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private class HeaderText : OsuSpriteText { - public HeaderText(string text) + public HeaderText(LocalisableString text) { Text = text.ToUpper(); Font = OsuFont.GetFont(size: 10, weight: FontWeight.Bold); diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 585b5c22aa..3f8779b97b 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -10,6 +10,7 @@ using osu.Framework.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Localisation; using osu.Game.Users; using osu.Game.Users.Drawables; using osuTK; @@ -109,7 +110,7 @@ namespace osu.Game.Overlays.Rankings.Tables { private readonly bool isHighlighted; - public HeaderText(string text, bool isHighlighted) + public HeaderText(LocalisableString text, bool isHighlighted) { this.isHighlighted = isHighlighted; diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs index a6969f483f..a77ce81c23 100644 --- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Users; @@ -19,7 +20,7 @@ namespace osu.Game.Overlays.Rankings.Tables { } - protected virtual IEnumerable GradeColumns => new List { "SS", "S", "A" }; + protected virtual IEnumerable GradeColumns => new List { "SS", "S", "A" }; protected override TableColumn[] CreateAdditionalHeaders() => new[] { @@ -66,7 +67,7 @@ namespace osu.Game.Overlays.Rankings.Tables private class UserTableHeaderText : HeaderText { - public UserTableHeaderText(string text, bool isHighlighted, bool isGrade) + public UserTableHeaderText(LocalisableString text, bool isHighlighted, bool isGrade) : base(text, isHighlighted) { Margin = new MarginPadding diff --git a/osu.Game/Screens/Edit/EditorTable.cs b/osu.Game/Screens/Edit/EditorTable.cs index 9578b96897..77c3a2f26c 100644 --- a/osu.Game/Screens/Edit/EditorTable.cs +++ b/osu.Game/Screens/Edit/EditorTable.cs @@ -2,10 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -46,7 +48,7 @@ namespace osu.Game.Screens.Edit private class HeaderText : OsuSpriteText { - public HeaderText(string text) + public HeaderText(LocalisableString text) { Text = text.ToUpper(); Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold); From d9d9db6f621ea2353e11906bf334a1e9d98d962c Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 27 Jul 2021 19:00:08 +0900 Subject: [PATCH 0676/2442] Revert "Modify catcher autoplay test pattern to see more variety movement" I found `TestSceneHyperDash` is useful for visual inspection of hyper dash trails. This reverts commit 0fbe950a --- osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs index 0ca0ddfcb1..f552c3c27b 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs @@ -27,9 +27,9 @@ namespace osu.Game.Rulesets.Catch.Tests } }; - for (int i = 0; i < 12; i++) + for (int i = 0; i < 100; i++) { - float width = (i + 1) / 20f * CatchPlayfield.WIDTH; + float width = (i % 10 + 1) / 20f * CatchPlayfield.WIDTH; beatmap.HitObjects.Add(new JuiceStream { @@ -39,8 +39,8 @@ namespace osu.Game.Rulesets.Catch.Tests Vector2.Zero, new Vector2(width, 0) }), - StartTime = i * 1000, - NewCombo = i % 5 == 0, + StartTime = i * 2000, + NewCombo = i % 8 == 0, Samples = new List(new[] { new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "normal", volume: 100) From de68fd12b3ef0f0e9a6df12a6fc05ff3eb0a53ce Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 27 Jul 2021 18:48:31 +0900 Subject: [PATCH 0677/2442] Move catcher trail colouring logic to `CatcherTrailDisplay` --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 8 ---- .../UI/CatcherTrailDisplay.cs | 46 ++++++------------- 2 files changed, 15 insertions(+), 39 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index e7c26ee44f..a5f51374c4 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -115,7 +115,6 @@ namespace osu.Game.Rulesets.Catch.UI private readonly SkinnableCatcher body; private Color4 hyperDashColour = DEFAULT_HYPER_DASH_COLOUR; - private Color4 hyperDashEndGlowColour = DEFAULT_HYPER_DASH_COLOUR; private double hyperDashModifier = 1; private int hyperDashDirection; @@ -323,13 +322,6 @@ namespace osu.Game.Rulesets.Catch.UI skin.GetConfig(CatchSkinColour.HyperDash)?.Value ?? DEFAULT_HYPER_DASH_COLOUR; - hyperDashEndGlowColour = - skin.GetConfig(CatchSkinColour.HyperDashAfterImage)?.Value ?? - hyperDashColour; - - trails.HyperDashTrailsColour = hyperDashColour; - trails.EndGlowSpritesColour = hyperDashEndGlowColour; - flipCatcherPlate = skin.GetConfig(CatchSkinConfiguration.FlipCatcherPlate)?.Value ?? true; runHyperDashStateTransition(HyperDashing); diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs index 00f2a76bde..9931ef8f64 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs @@ -6,6 +6,8 @@ using JetBrains.Annotations; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Pooling; +using osu.Game.Rulesets.Catch.Skinning; +using osu.Game.Skinning; using osuTK; using osuTK.Graphics; @@ -15,7 +17,7 @@ namespace osu.Game.Rulesets.Catch.UI /// Represents a component responsible for displaying /// the appropriate catcher trails when requested to. /// - public class CatcherTrailDisplay : CompositeDrawable + public class CatcherTrailDisplay : SkinReloadableDrawable { /// /// The most recent dash trail added in this container. @@ -26,42 +28,16 @@ namespace osu.Game.Rulesets.Catch.UI .OrderByDescending(trail => trail.LifetimeStart) .FirstOrDefault(); + public Color4 HyperDashTrailsColour => hyperDashTrails.Colour; + + public Color4 EndGlowSpritesColour => endGlowSprites.Colour; + private readonly DrawablePool trailPool; private readonly Container dashTrails; private readonly Container hyperDashTrails; private readonly Container endGlowSprites; - private Color4 hyperDashTrailsColour = Catcher.DEFAULT_HYPER_DASH_COLOUR; - - public Color4 HyperDashTrailsColour - { - get => hyperDashTrailsColour; - set - { - if (hyperDashTrailsColour == value) - return; - - hyperDashTrailsColour = value; - hyperDashTrails.Colour = hyperDashTrailsColour; - } - } - - private Color4 endGlowSpritesColour = Catcher.DEFAULT_HYPER_DASH_COLOUR; - - public Color4 EndGlowSpritesColour - { - get => endGlowSpritesColour; - set - { - if (endGlowSpritesColour == value) - return; - - endGlowSpritesColour = value; - endGlowSprites.Colour = endGlowSpritesColour; - } - } - public CatcherTrailDisplay() { RelativeSizeAxes = Axes.Both; @@ -75,6 +51,14 @@ namespace osu.Game.Rulesets.Catch.UI }; } + protected override void SkinChanged(ISkinSource skin) + { + base.SkinChanged(skin); + + hyperDashTrails.Colour = skin.GetConfig(CatchSkinColour.HyperDash)?.Value ?? Catcher.DEFAULT_HYPER_DASH_COLOUR; + endGlowSprites.Colour = skin.GetConfig(CatchSkinColour.HyperDashAfterImage)?.Value ?? hyperDashTrails.Colour; + } + /// /// Displays a single end-glow catcher sprite. /// From da69867fd4b452bc2765b43006ba3bbedc86cf6b Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 27 Jul 2021 18:59:55 +0900 Subject: [PATCH 0678/2442] Move catcher trail generation logic to `CatcherArea` --- .../TestSceneCatchSkinConfiguration.cs | 2 +- .../TestSceneCatcher.cs | 10 ++-- .../TestSceneCatcherArea.cs | 5 +- .../TestSceneHyperDashColouring.cs | 10 ++-- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 8 +--- osu.Game.Rulesets.Catch/UI/Catcher.cs | 22 ++------- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 48 +++++++++++++------ 7 files changed, 48 insertions(+), 57 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs index 58ff97d563..8ae2bcca0e 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchSkinConfiguration.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Catch.Tests var skin = new TestSkin { FlipCatcherPlate = flip }; container.Child = new SkinProvidingContainer(skin) { - Child = catcher = new Catcher(new CatcherTrailDisplay(), new DroppedObjectContainer()) + Child = catcher = new Catcher(new DroppedObjectContainer()) { Anchor = Anchor.Centre } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index 1e582bd9e8..540f02580f 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -31,8 +31,6 @@ namespace osu.Game.Rulesets.Catch.Tests [Resolved] private OsuConfigManager config { get; set; } - private CatcherTrailDisplay trailDisplay; - private DroppedObjectContainer droppedObjectContainer; private TestCatcher catcher; @@ -45,7 +43,6 @@ namespace osu.Game.Rulesets.Catch.Tests CircleSize = 0, }; - trailDisplay = new CatcherTrailDisplay(); droppedObjectContainer = new DroppedObjectContainer(); Child = new Container @@ -54,8 +51,7 @@ namespace osu.Game.Rulesets.Catch.Tests Children = new Drawable[] { droppedObjectContainer, - catcher = new TestCatcher(trailDisplay, droppedObjectContainer, difficulty), - trailDisplay, + catcher = new TestCatcher(droppedObjectContainer, difficulty), } }; }); @@ -294,8 +290,8 @@ namespace osu.Game.Rulesets.Catch.Tests { public IEnumerable CaughtObjects => this.ChildrenOfType(); - public TestCatcher(CatcherTrailDisplay trails, DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty) - : base(trails, droppedObjectTarget, difficulty) + public TestCatcher(DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty) + : base(droppedObjectTarget, difficulty) { } } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs index c97e6bf9ee..a3307c9224 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs @@ -121,13 +121,10 @@ namespace osu.Game.Rulesets.Catch.Tests { public TestCatcherArea(BeatmapDifficulty beatmapDifficulty) { - var trailDisplay = new CatcherTrailDisplay { Depth = -1 }; - Add(trailDisplay); - var droppedObjectContainer = new DroppedObjectContainer(); Add(droppedObjectContainer); - Catcher = new Catcher(trailDisplay, droppedObjectContainer, beatmapDifficulty) + Catcher = new Catcher(droppedObjectContainer, beatmapDifficulty) { X = CatchPlayfield.CENTER_X }; diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index 88ddc7e8a8..4524086b02 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -118,19 +118,19 @@ namespace osu.Game.Rulesets.Catch.Tests AddStep("create hyper-dashing catcher", () => { - trails = new CatcherTrailDisplay(); + CatcherArea catcherArea; Child = setupSkinHierarchy(new Container { Anchor = Anchor.Centre, - Children = new Drawable[] + Child = catcherArea = new CatcherArea { - catcher = new Catcher(trails, new DroppedObjectContainer()) + Catcher = catcher = new Catcher(new DroppedObjectContainer()) { Scale = new Vector2(4) - }, - trails + } } }, skin); + trails = catcherArea.CatcherTrails; }); AddStep("start hyper-dash", () => diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index bcbe7c776e..1e20643a08 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -44,14 +44,9 @@ namespace osu.Game.Rulesets.Catch.UI [BackgroundDependencyLoader] private void load() { - var trailDisplay = new CatcherTrailDisplay - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.TopLeft - }; var droppedObjectContainer = new DroppedObjectContainer(); - Catcher = new Catcher(trailDisplay, droppedObjectContainer, difficulty) + Catcher = new Catcher(droppedObjectContainer, difficulty) { X = CENTER_X }; @@ -69,7 +64,6 @@ namespace osu.Game.Rulesets.Catch.UI Origin = Anchor.TopLeft, Catcher = Catcher, }, - trailDisplay, HitObjectContainer, }); diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index a5f51374c4..dd6d8626d0 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -71,11 +71,6 @@ namespace osu.Game.Rulesets.Catch.UI /// private const float caught_fruit_scale_adjust = 0.5f; - /// - /// Contains trails and afterimages (also called "end glow" in code) of the catcher. - /// - private readonly CatcherTrailDisplay trails; - /// /// Contains caught objects on the plate. /// @@ -102,6 +97,8 @@ namespace osu.Game.Rulesets.Catch.UI /// public Direction VisualDirection { get; set; } = Direction.Right; + public Vector2 BodyScale => Scale * body.Scale; + /// /// Whether the contents of the catcher plate should be visually flipped when the catcher direction is changed. /// @@ -127,9 +124,8 @@ namespace osu.Game.Rulesets.Catch.UI private readonly DrawablePool caughtBananaPool; private readonly DrawablePool caughtDropletPool; - public Catcher([NotNull] CatcherTrailDisplay trails, [NotNull] DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty = null) + public Catcher([NotNull] DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty = null) { - this.trails = trails; this.droppedObjectTarget = droppedObjectTarget; Origin = Anchor.TopCentre; @@ -292,10 +288,7 @@ namespace osu.Game.Rulesets.Catch.UI hyperDashTargetPosition = targetPosition; if (!wasHyperDashing) - { - trails.DisplayEndGlow(CurrentState, X, Scale * body.Scale); runHyperDashStateTransition(true); - } } } @@ -342,15 +335,6 @@ namespace osu.Game.Rulesets.Catch.UI X = hyperDashTargetPosition; SetHyperDashState(); } - - if (Dashing || HyperDashing) - { - double lastTrailTime = trails.LastDashTrail?.LifetimeStart ?? double.NegativeInfinity; - double generationInterval = HyperDashing ? 25 : 50; - - if (Time.Current - lastTrailTime >= generationInterval) - trails.DisplayDashTrail(CurrentState, X, Scale * body.Scale, HyperDashing); - } } private void placeCaughtObject(DrawablePalpableCatchHitObject drawableObject, Vector2 position) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index de0ace9817..bcf4b36c9c 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -25,15 +25,13 @@ namespace osu.Game.Rulesets.Catch.UI public Catcher Catcher { get => catcher; - set - { - if (catcher != null) - Remove(catcher); - - Add(catcher = value); - } + set => catcherContainer.Child = catcher = value; } + internal CatcherTrailDisplay CatcherTrails { get; } + + private readonly Container catcherContainer; + private readonly CatchComboDisplay comboDisplay; private Catcher catcher; @@ -45,20 +43,28 @@ namespace osu.Game.Rulesets.Catch.UI /// private int currentDirection; + // TODO: support replay rewind + private bool lastHyperDashState; + /// /// must be set before loading. /// public CatcherArea() { Size = new Vector2(CatchPlayfield.WIDTH, Catcher.BASE_SIZE); - Child = comboDisplay = new CatchComboDisplay + Children = new Drawable[] { - RelativeSizeAxes = Axes.None, - AutoSizeAxes = Axes.Both, - Anchor = Anchor.TopLeft, - Origin = Anchor.Centre, - Margin = new MarginPadding { Bottom = 350f }, - X = CatchPlayfield.CENTER_X + catcherContainer = new Container { RelativeSizeAxes = Axes.Both }, + CatcherTrails = new CatcherTrailDisplay(), + comboDisplay = new CatchComboDisplay + { + RelativeSizeAxes = Axes.None, + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopLeft, + Origin = Anchor.Centre, + Margin = new MarginPadding { Bottom = 350f }, + X = CatchPlayfield.CENTER_X + } }; } @@ -102,6 +108,20 @@ namespace osu.Game.Rulesets.Catch.UI base.UpdateAfterChildren(); comboDisplay.X = Catcher.X; + + if (!lastHyperDashState && Catcher.HyperDashing && Time.Elapsed > 0) + CatcherTrails.DisplayEndGlow(Catcher.CurrentState, Catcher.X, Catcher.BodyScale); + + if (Catcher.Dashing || Catcher.HyperDashing) + { + double lastTrailTime = CatcherTrails.LastDashTrail?.LifetimeStart ?? double.NegativeInfinity; + double generationInterval = Catcher.HyperDashing ? 25 : 50; + + if (Time.Current - lastTrailTime >= generationInterval) + CatcherTrails.DisplayDashTrail(Catcher.CurrentState, Catcher.X, Catcher.BodyScale, Catcher.HyperDashing); + } + + lastHyperDashState = Catcher.HyperDashing; } public void SetCatcherPosition(float X) From 846f539428ac75afcc2fb8a1ca4f1ea933dc78f1 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 27 Jul 2021 19:11:08 +0900 Subject: [PATCH 0679/2442] Avoid usage of LINQ in last dash trail computation --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 3 +-- .../UI/CatcherTrailDisplay.cs | 24 +++++++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index bcf4b36c9c..9eb692c875 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -114,10 +114,9 @@ namespace osu.Game.Rulesets.Catch.UI if (Catcher.Dashing || Catcher.HyperDashing) { - double lastTrailTime = CatcherTrails.LastDashTrail?.LifetimeStart ?? double.NegativeInfinity; double generationInterval = Catcher.HyperDashing ? 25 : 50; - if (Time.Current - lastTrailTime >= generationInterval) + if (Time.Current - CatcherTrails.LastDashTrailTime >= generationInterval) CatcherTrails.DisplayDashTrail(Catcher.CurrentState, Catcher.X, Catcher.BodyScale, Catcher.HyperDashing); } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs index 9931ef8f64..31b7a6e0ed 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs @@ -1,8 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; -using JetBrains.Annotations; +using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Pooling; @@ -20,13 +19,11 @@ namespace osu.Game.Rulesets.Catch.UI public class CatcherTrailDisplay : SkinReloadableDrawable { /// - /// The most recent dash trail added in this container. + /// The most recent time a dash trail was added to this container. /// Only alive (not faded out) trails are considered. + /// Returns if no dash trail is alive. /// - [CanBeNull] - public CatcherTrail LastDashTrail => dashTrails.Concat(hyperDashTrails) - .OrderByDescending(trail => trail.LifetimeStart) - .FirstOrDefault(); + public double LastDashTrailTime => getLastDashTrailTime(); public Color4 HyperDashTrailsColour => hyperDashTrails.Colour; @@ -97,5 +94,18 @@ namespace osu.Game.Rulesets.Catch.UI return sprite; } + + private double getLastDashTrailTime() + { + double maxTime = double.NegativeInfinity; + + foreach (var trail in dashTrails) + maxTime = Math.Max(maxTime, trail.LifetimeStart); + + foreach (var trail in hyperDashTrails) + maxTime = Math.Max(maxTime, trail.LifetimeStart); + + return maxTime; + } } } From c741366c72a559663d0358a317f40c149e7d0e4c Mon Sep 17 00:00:00 2001 From: Gabe Livengood <47010459+ggliv@users.noreply.github.com> Date: Tue, 27 Jul 2021 09:01:01 -0400 Subject: [PATCH 0680/2442] review modifications: change xmldocs, change reflection method name, remove reflection method returns, simplify incompat. mod list --- osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs | 4 ++-- osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs | 10 +++++---- .../Utils/OsuHitObjectGenerationUtils.cs | 22 +++++++------------ 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs index 82b8959456..c94bafe6af 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs @@ -17,13 +17,13 @@ namespace osu.Game.Rulesets.Osu.Mods { public override double ScoreMultiplier => 1.06; - public override Type[] IncompatibleMods => new[] { typeof(ModEasy), typeof(ModDifficultyAdjust), typeof(ModMirror) }; + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModMirror)).ToArray(); public void ApplyToHitObject(HitObject hitObject) { var osuObject = (OsuHitObject)hitObject; - OsuHitObjectGenerationUtils.ReflectOsuHitObjectVertically(osuObject); + OsuHitObjectGenerationUtils.ReflectVertically(osuObject); } } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs index fd1d685fd1..9b4614c9b2 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs @@ -29,14 +29,16 @@ namespace osu.Game.Rulesets.Osu.Mods switch (Reflection.Value) { case MirrorType.Horizontal: - OsuHitObjectGenerationUtils.ReflectOsuHitObjectHorizontally(osuObject); + OsuHitObjectGenerationUtils.ReflectHorizontally(osuObject); break; + case MirrorType.Vertical: - OsuHitObjectGenerationUtils.ReflectOsuHitObjectVertically(osuObject); + OsuHitObjectGenerationUtils.ReflectVertically(osuObject); break; + case MirrorType.Both: - OsuHitObjectGenerationUtils.ReflectOsuHitObjectHorizontally(osuObject); - OsuHitObjectGenerationUtils.ReflectOsuHitObjectVertically(osuObject); + OsuHitObjectGenerationUtils.ReflectHorizontally(osuObject); + OsuHitObjectGenerationUtils.ReflectVertically(osuObject); break; } } diff --git a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs index bf55898901..fcd5fb4a31 100644 --- a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs +++ b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs @@ -106,16 +106,15 @@ namespace osu.Game.Rulesets.Osu.Utils } /// - /// Reflects an OsuHitObject's position horizontally. + /// Reflects the position of the in the playfield vertically. /// - /// The OsuHitObject to be reflected. - /// The reflected OsuHitObject. - public static OsuHitObject ReflectOsuHitObjectHorizontally(OsuHitObject osuObject) + /// The object to reflect. + public static void ReflectHorizontally(OsuHitObject osuObject) { osuObject.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - osuObject.X, osuObject.Position.Y); if (!(osuObject is Slider slider)) - return osuObject; + return; slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y)); slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y)); @@ -125,21 +124,18 @@ namespace osu.Game.Rulesets.Osu.Utils point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y); slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value); - - return osuObject; } /// - /// Reflects an OsuHitObject's position vertically. + /// Reflects the position of the in the playfield horizontally. /// - /// The OsuHitObject to be reflected. - /// The reflected OsuHitObject. - public static OsuHitObject ReflectOsuHitObjectVertically(OsuHitObject osuObject) + /// The object to reflect. + public static void ReflectVertically(OsuHitObject osuObject) { osuObject.Position = new Vector2(osuObject.Position.X, OsuPlayfield.BASE_SIZE.Y - osuObject.Y); if (!(osuObject is Slider slider)) - return osuObject; + return; slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); @@ -149,8 +145,6 @@ namespace osu.Game.Rulesets.Osu.Utils point.Position.Value = new Vector2(point.Position.Value.X, -point.Position.Value.Y); slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value); - - return osuObject; } } } From ed903c60eadb700b9a8005584ebb4ca8139f46be Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 27 Jul 2021 18:14:05 +0300 Subject: [PATCH 0681/2442] Fix code style issues and remove unused using directives --- osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs | 3 --- osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs | 5 +---- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs index c94bafe6af..007820b016 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs @@ -3,13 +3,10 @@ using System; using System.Linq; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.Utils; -using osuTK; namespace osu.Game.Rulesets.Osu.Mods { diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs index 9b4614c9b2..a59ae33523 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs @@ -2,16 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; using osu.Framework.Bindables; using osu.Game.Configuration; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.Utils; -using osuTK; namespace osu.Game.Rulesets.Osu.Mods { @@ -26,6 +22,7 @@ namespace osu.Game.Rulesets.Osu.Mods public void ApplyToHitObject(HitObject hitObject) { var osuObject = (OsuHitObject)hitObject; + switch (Reflection.Value) { case MirrorType.Horizontal: From 5cb02002d78a96879fb8e948c7a236a64ad07d74 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 27 Jul 2021 18:22:32 +0300 Subject: [PATCH 0682/2442] Fix flipped xmldoc --- osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs index fcd5fb4a31..57ec51cf64 100644 --- a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs +++ b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs @@ -106,7 +106,7 @@ namespace osu.Game.Rulesets.Osu.Utils } /// - /// Reflects the position of the in the playfield vertically. + /// Reflects the position of the in the playfield horizontally. /// /// The object to reflect. public static void ReflectHorizontally(OsuHitObject osuObject) @@ -127,7 +127,7 @@ namespace osu.Game.Rulesets.Osu.Utils } /// - /// Reflects the position of the in the playfield horizontally. + /// Reflects the position of the in the playfield vertically. /// /// The object to reflect. public static void ReflectVertically(OsuHitObject osuObject) From 94877117b9868e00ec8c95c9b482d92a25f7bc35 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 27 Jul 2021 18:22:47 +0200 Subject: [PATCH 0683/2442] Apply changes in-line with framework changes. --- osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs | 4 +++- osu.Game/Overlays/Rankings/Tables/RankingsTable.cs | 3 ++- osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs | 5 +++-- osu.Game/Screens/Edit/EditorTable.cs | 4 +++- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index ddd1dfa6cd..22033f14cd 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -18,6 +18,8 @@ using osu.Game.Scoring; using osu.Game.Users.Drawables; using osuTK; using osuTK.Graphics; +using osu.Framework.Localisation; +using osu.Framework.Extensions.LocalisationExtensions; namespace osu.Game.Overlays.BeatmapSet.Scores { @@ -215,7 +217,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private class HeaderText : OsuSpriteText { - public HeaderText(string text) + public HeaderText(LocalisableString text) { Text = text.ToUpper(); Font = OsuFont.GetFont(size: 10, weight: FontWeight.Bold); diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 585b5c22aa..0da48c6f88 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -13,6 +13,7 @@ using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Users; using osu.Game.Users.Drawables; using osuTK; +using osu.Framework.Localisation; namespace osu.Game.Overlays.Rankings.Tables { @@ -109,7 +110,7 @@ namespace osu.Game.Overlays.Rankings.Tables { private readonly bool isHighlighted; - public HeaderText(string text, bool isHighlighted) + public HeaderText(LocalisableString text, bool isHighlighted) { this.isHighlighted = isHighlighted; diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs index a6969f483f..e3081491f4 100644 --- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -9,6 +9,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Users; using osu.Game.Scoring; +using osu.Framework.Localisation; namespace osu.Game.Overlays.Rankings.Tables { @@ -32,7 +33,7 @@ namespace osu.Game.Overlays.Rankings.Tables protected override Drawable CreateHeader(int index, TableColumn column) { var title = column?.Header ?? string.Empty; - return new UserTableHeaderText(title, HighlightedColumn == title, GradeColumns.Contains(title)); + return new UserTableHeaderText(title, HighlightedColumn == title, GradeColumns.Contains(title.ToString())); } protected sealed override Country GetCountry(UserStatistics item) => item.User.Country; @@ -66,7 +67,7 @@ namespace osu.Game.Overlays.Rankings.Tables private class UserTableHeaderText : HeaderText { - public UserTableHeaderText(string text, bool isHighlighted, bool isGrade) + public UserTableHeaderText(LocalisableString text, bool isHighlighted, bool isGrade) : base(text, isHighlighted) { Margin = new MarginPadding diff --git a/osu.Game/Screens/Edit/EditorTable.cs b/osu.Game/Screens/Edit/EditorTable.cs index 9578b96897..77c3a2f26c 100644 --- a/osu.Game/Screens/Edit/EditorTable.cs +++ b/osu.Game/Screens/Edit/EditorTable.cs @@ -2,10 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -46,7 +48,7 @@ namespace osu.Game.Screens.Edit private class HeaderText : OsuSpriteText { - public HeaderText(string text) + public HeaderText(LocalisableString text) { Text = text.ToUpper(); Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold); From 239b38a0ab1776fa4d38bbe35dddb0ec9114f3d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 27 Jul 2021 21:42:34 +0200 Subject: [PATCH 0684/2442] Reduce implicit conversions by using `default` --- osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs | 2 +- osu.Game/Overlays/Rankings/Tables/RankingsTable.cs | 2 +- osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs | 2 +- osu.Game/Screens/Edit/EditorTable.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index 22033f14cd..fee0e62315 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -213,7 +213,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores return content.ToArray(); } - protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? string.Empty); + protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? default); private class HeaderText : OsuSpriteText { diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 0da48c6f88..f568aae59c 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -75,7 +75,7 @@ namespace osu.Game.Overlays.Rankings.Tables protected override Drawable CreateHeader(int index, TableColumn column) { - var title = column?.Header ?? string.Empty; + var title = column?.Header ?? default; return new HeaderText(title, title == HighlightedColumn); } diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs index e3081491f4..56814244c0 100644 --- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Rankings.Tables protected override Drawable CreateHeader(int index, TableColumn column) { - var title = column?.Header ?? string.Empty; + var title = column?.Header ?? default; return new UserTableHeaderText(title, HighlightedColumn == title, GradeColumns.Contains(title.ToString())); } diff --git a/osu.Game/Screens/Edit/EditorTable.cs b/osu.Game/Screens/Edit/EditorTable.cs index 77c3a2f26c..756339405c 100644 --- a/osu.Game/Screens/Edit/EditorTable.cs +++ b/osu.Game/Screens/Edit/EditorTable.cs @@ -44,7 +44,7 @@ namespace osu.Game.Screens.Edit }); } - protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? string.Empty); + protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? default); private class HeaderText : OsuSpriteText { From 712bc578dcc03a8be8649bba5fd56c483f41aa05 Mon Sep 17 00:00:00 2001 From: Gabe Livengood <47010459+ggliv@users.noreply.github.com> Date: Tue, 27 Jul 2021 17:45:52 -0400 Subject: [PATCH 0685/2442] update setting name and description --- osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs index a59ae33523..1770e79d3f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => "Reflect the playfield."; public override Type[] IncompatibleMods => new[] { typeof(ModHardRock) }; - [SettingSource("Reflection", "Change the type of reflection.")] + [SettingSource("Mirrored axes", "Choose which of the playfield's axes are mirrored.")] public Bindable Reflection { get; } = new Bindable(); public void ApplyToHitObject(HitObject hitObject) From 0bf04ece34698217162e22a40db73a603410cf27 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 28 Jul 2021 18:13:43 +0900 Subject: [PATCH 0686/2442] Avoid `internal` property by using `ChildrenOfType` --- .../TestSceneHyperDashColouring.cs | 2 +- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index 4524086b02..82b69716a8 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -130,7 +130,7 @@ namespace osu.Game.Rulesets.Catch.Tests } } }, skin); - trails = catcherArea.CatcherTrails; + trails = catcherArea.ChildrenOfType().Single(); }); AddStep("start hyper-dash", () => diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 9eb692c875..2d6d7fb8d8 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -28,12 +28,12 @@ namespace osu.Game.Rulesets.Catch.UI set => catcherContainer.Child = catcher = value; } - internal CatcherTrailDisplay CatcherTrails { get; } - private readonly Container catcherContainer; private readonly CatchComboDisplay comboDisplay; + private readonly CatcherTrailDisplay catcherTrails; + private Catcher catcher; /// @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Catch.UI Children = new Drawable[] { catcherContainer = new Container { RelativeSizeAxes = Axes.Both }, - CatcherTrails = new CatcherTrailDisplay(), + catcherTrails = new CatcherTrailDisplay(), comboDisplay = new CatchComboDisplay { RelativeSizeAxes = Axes.None, @@ -110,14 +110,14 @@ namespace osu.Game.Rulesets.Catch.UI comboDisplay.X = Catcher.X; if (!lastHyperDashState && Catcher.HyperDashing && Time.Elapsed > 0) - CatcherTrails.DisplayEndGlow(Catcher.CurrentState, Catcher.X, Catcher.BodyScale); + catcherTrails.DisplayEndGlow(Catcher.CurrentState, Catcher.X, Catcher.BodyScale); if (Catcher.Dashing || Catcher.HyperDashing) { double generationInterval = Catcher.HyperDashing ? 25 : 50; - if (Time.Current - CatcherTrails.LastDashTrailTime >= generationInterval) - CatcherTrails.DisplayDashTrail(Catcher.CurrentState, Catcher.X, Catcher.BodyScale, Catcher.HyperDashing); + if (Time.Current - catcherTrails.LastDashTrailTime >= generationInterval) + catcherTrails.DisplayDashTrail(Catcher.CurrentState, Catcher.X, Catcher.BodyScale, Catcher.HyperDashing); } lastHyperDashState = Catcher.HyperDashing; From a960a28d0679b74fd7c28b21e14a41941a1a5e1b Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 28 Jul 2021 19:02:24 +0900 Subject: [PATCH 0687/2442] Replace "end glow" terminology with "hyper-dash after-image" Because the is "end glow" is when a hyper-dash is *started*, the name was confusing. The "after-image" was already used in the code as a synonym of "end glow" inconsistently. --- .../TestSceneHyperDashColouring.cs | 8 +++---- osu.Game.Rulesets.Catch/UI/Catcher.cs | 3 +-- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 2 +- .../UI/CatcherTrailDisplay.cs | 24 +++++++++---------- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index 82b69716a8..70b2c8c82a 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Catch.Tests } [Test] - public void TestCustomEndGlowColour() + public void TestCustomAfterImageColour() { var skin = new TestSkin { @@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Catch.Tests } [Test] - public void TestCustomEndGlowColourPriority() + public void TestCustomAfterImageColourPriority() { var skin = new TestSkin { @@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Catch.Tests checkHyperDashFruitColour(skin, skin.HyperDashColour); } - private void checkHyperDashCatcherColour(ISkin skin, Color4 expectedCatcherColour, Color4? expectedEndGlowColour = null) + private void checkHyperDashCatcherColour(ISkin skin, Color4 expectedCatcherColour, Color4? expectedAfterImageColour = null) { CatcherTrailDisplay trails = null; Catcher catcher = null; @@ -141,7 +141,7 @@ namespace osu.Game.Rulesets.Catch.Tests AddUntilStep("catcher colour is correct", () => catcher.Colour == expectedCatcherColour); AddAssert("catcher trails colours are correct", () => trails.HyperDashTrailsColour == expectedCatcherColour); - AddAssert("catcher end-glow colours are correct", () => trails.EndGlowSpritesColour == (expectedEndGlowColour ?? expectedCatcherColour)); + AddAssert("catcher after-image colours are correct", () => trails.HyperDashAfterImageColour == (expectedAfterImageColour ?? expectedCatcherColour)); AddStep("finish hyper-dashing", () => { diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index b42e5352dd..9fd4610e6e 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -36,8 +36,7 @@ namespace osu.Game.Rulesets.Catch.UI public const float ALLOWED_CATCH_RANGE = 0.8f; /// - /// The default colour used to tint hyper-dash fruit, along with the moving catcher, its trail - /// and end glow/after-image during a hyper-dash. + /// The default colour used to tint hyper-dash fruit, along with the moving catcher, its trail and after-image during a hyper-dash. /// public static readonly Color4 DEFAULT_HYPER_DASH_COLOUR = Color4.Red; diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 2d6d7fb8d8..78f7e34649 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Catch.UI comboDisplay.X = Catcher.X; if (!lastHyperDashState && Catcher.HyperDashing && Time.Elapsed > 0) - catcherTrails.DisplayEndGlow(Catcher.CurrentState, Catcher.X, Catcher.BodyScale); + catcherTrails.DisplayHyperDashAfterImage(Catcher.CurrentState, Catcher.X, Catcher.BodyScale); if (Catcher.Dashing || Catcher.HyperDashing) { diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs index 31b7a6e0ed..c7e576d970 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs @@ -27,13 +27,13 @@ namespace osu.Game.Rulesets.Catch.UI public Color4 HyperDashTrailsColour => hyperDashTrails.Colour; - public Color4 EndGlowSpritesColour => endGlowSprites.Colour; + public Color4 HyperDashAfterImageColour => hyperDashAfterImages.Colour; private readonly DrawablePool trailPool; private readonly Container dashTrails; private readonly Container hyperDashTrails; - private readonly Container endGlowSprites; + private readonly Container hyperDashAfterImages; public CatcherTrailDisplay() { @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Catch.UI trailPool = new DrawablePool(30), dashTrails = new Container { RelativeSizeAxes = Axes.Both }, hyperDashTrails = new Container { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR }, - endGlowSprites = new Container { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR }, + hyperDashAfterImages = new Container { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR }, }; } @@ -53,22 +53,22 @@ namespace osu.Game.Rulesets.Catch.UI base.SkinChanged(skin); hyperDashTrails.Colour = skin.GetConfig(CatchSkinColour.HyperDash)?.Value ?? Catcher.DEFAULT_HYPER_DASH_COLOUR; - endGlowSprites.Colour = skin.GetConfig(CatchSkinColour.HyperDashAfterImage)?.Value ?? hyperDashTrails.Colour; + hyperDashAfterImages.Colour = skin.GetConfig(CatchSkinColour.HyperDashAfterImage)?.Value ?? hyperDashTrails.Colour; } /// - /// Displays a single end-glow catcher sprite. + /// Displays a hyper-dash after-image of the catcher. /// - public void DisplayEndGlow(CatcherAnimationState animationState, float x, Vector2 scale) + public void DisplayHyperDashAfterImage(CatcherAnimationState animationState, float x, Vector2 scale) { - var endGlow = createTrail(animationState, x, scale); + var trail = createTrail(animationState, x, scale); - endGlowSprites.Add(endGlow); + hyperDashAfterImages.Add(trail); - endGlow.MoveToOffset(new Vector2(0, -10), 1200, Easing.In); - endGlow.ScaleTo(endGlow.Scale * 0.95f).ScaleTo(endGlow.Scale * 1.2f, 1200, Easing.In); - endGlow.FadeOut(1200); - endGlow.Expire(true); + trail.MoveToOffset(new Vector2(0, -10), 1200, Easing.In); + trail.ScaleTo(trail.Scale * 0.95f).ScaleTo(trail.Scale * 1.2f, 1200, Easing.In); + trail.FadeOut(1200); + trail.Expire(true); } public void DisplayDashTrail(CatcherAnimationState animationState, float x, Vector2 scale, bool hyperDashing) From 90f3611ed059dc581229756885faf799a4bebc99 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 28 Jul 2021 19:05:48 +0900 Subject: [PATCH 0688/2442] Replace "sprite" variable names in `CatcherTrailDisplay` The `CatcherTrail` was originally named `CatcherTrailSprite`, but it is not a sprite anymore. --- .../UI/CatcherTrailDisplay.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs index c7e576d970..abc76fc925 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs @@ -73,26 +73,26 @@ namespace osu.Game.Rulesets.Catch.UI public void DisplayDashTrail(CatcherAnimationState animationState, float x, Vector2 scale, bool hyperDashing) { - var sprite = createTrail(animationState, x, scale); + var trail = createTrail(animationState, x, scale); if (hyperDashing) - hyperDashTrails.Add(sprite); + hyperDashTrails.Add(trail); else - dashTrails.Add(sprite); + dashTrails.Add(trail); - sprite.FadeTo(0.4f).FadeOut(800, Easing.OutQuint); - sprite.Expire(true); + trail.FadeTo(0.4f).FadeOut(800, Easing.OutQuint); + trail.Expire(true); } private CatcherTrail createTrail(CatcherAnimationState animationState, float x, Vector2 scale) { - CatcherTrail sprite = trailPool.Get(); + CatcherTrail trail = trailPool.Get(); - sprite.AnimationState = animationState; - sprite.Scale = scale; - sprite.Position = new Vector2(x, 0); + trail.AnimationState = animationState; + trail.Scale = scale; + trail.Position = new Vector2(x, 0); - return sprite; + return trail; } private double getLastDashTrailTime() From 4a4d9b0dc6ef79a48c5718ae6d3e211095f0aa02 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Jul 2021 19:19:28 +0900 Subject: [PATCH 0689/2442] Update description to match mania mirror implementation --- osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs index 1770e79d3f..3faca0b01f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs @@ -13,10 +13,10 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModMirror : ModMirror, IApplicableToHitObject { - public override string Description => "Reflect the playfield."; + public override string Description => "Flip objects on the chosen axes."; public override Type[] IncompatibleMods => new[] { typeof(ModHardRock) }; - [SettingSource("Mirrored axes", "Choose which of the playfield's axes are mirrored.")] + [SettingSource("Mirrored axes", "Choose which axes objects are mirrored over.")] public Bindable Reflection { get; } = new Bindable(); public void ApplyToHitObject(HitObject hitObject) From 58bbe9db7e3128b80a3fa694a5d6f27628e4c8d7 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Wed, 28 Jul 2021 18:21:08 +0800 Subject: [PATCH 0690/2442] Added muted mod --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 31 ++++--- osu.Game.Rulesets.Catch/Mods/CatchModMuted.cs | 11 +++ osu.Game.Rulesets.Mania/ManiaRuleset.cs | 21 +++-- osu.Game.Rulesets.Mania/Mods/ManiaModMuted.cs | 11 +++ osu.Game.Rulesets.Osu/Mods/OsuModMuted.cs | 12 +++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 37 ++++---- osu.Game.Rulesets.Taiko/Mods/TaikoModMuted.cs | 11 +++ osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 31 ++++--- osu.Game/Overlays/MusicController.cs | 11 ++- osu.Game/Rulesets/Mods/ModMuted.cs | 93 +++++++++++++++++++ 10 files changed, 208 insertions(+), 61 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Mods/CatchModMuted.cs create mode 100644 osu.Game.Rulesets.Mania/Mods/ManiaModMuted.cs create mode 100644 osu.Game.Rulesets.Osu/Mods/OsuModMuted.cs create mode 100644 osu.Game.Rulesets.Taiko/Mods/TaikoModMuted.cs create mode 100644 osu.Game/Rulesets/Mods/ModMuted.cs diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 76863acc78..c77caf0cef 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -1,30 +1,30 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Rulesets.Catch.Mods; -using osu.Game.Rulesets.Catch.UI; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.UI; +using System; using System.Collections.Generic; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; -using osu.Game.Rulesets.Catch.Replays; -using osu.Game.Rulesets.Replays.Types; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; +using osu.Game.Graphics; using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Catch.Difficulty; -using osu.Game.Rulesets.Catch.Scoring; -using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Scoring; -using osu.Game.Scoring; -using System; -using osu.Framework.Extensions.EnumExtensions; using osu.Game.Rulesets.Catch.Edit; +using osu.Game.Rulesets.Catch.Mods; +using osu.Game.Rulesets.Catch.Replays; +using osu.Game.Rulesets.Catch.Scoring; using osu.Game.Rulesets.Catch.Skinning.Legacy; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Replays.Types; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Scoring; using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch @@ -130,7 +130,8 @@ namespace osu.Game.Rulesets.Catch return new Mod[] { new MultiMod(new ModWindUp(), new ModWindDown()), - new CatchModFloatingFruits() + new CatchModFloatingFruits(), + new CatchModMuted(), }; default: diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModMuted.cs b/osu.Game.Rulesets.Catch/Mods/CatchModMuted.cs new file mode 100644 index 0000000000..4c73a6a21f --- /dev/null +++ b/osu.Game.Rulesets.Catch/Mods/CatchModMuted.cs @@ -0,0 +1,11 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Catch.Mods +{ + public class CatchModMuted : ModMuted + { + } +} diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index fe736766d9..3615d7e660 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -2,22 +2,16 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mania.Mods; -using osu.Game.Rulesets.Mania.UI; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.UI; using System.Collections.Generic; using System.Linq; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; -using osu.Game.Graphics; -using osu.Game.Rulesets.Mania.Replays; -using osu.Game.Rulesets.Replays.Types; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; using osu.Game.Configuration; +using osu.Game.Graphics; using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Difficulty; @@ -27,12 +21,18 @@ using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Configuration; using osu.Game.Rulesets.Mania.Difficulty; using osu.Game.Rulesets.Mania.Edit; +using osu.Game.Rulesets.Mania.Mods; +using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Mania.Skinning.Legacy; +using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Scoring; -using osu.Game.Skinning; +using osu.Game.Rulesets.UI; using osu.Game.Scoring; using osu.Game.Screens.Ranking.Statistics; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Mania { @@ -253,7 +253,8 @@ namespace osu.Game.Rulesets.Mania case ModType.Fun: return new Mod[] { - new MultiMod(new ModWindUp(), new ModWindDown()) + new MultiMod(new ModWindUp(), new ModWindDown()), + new ManiaModMuted(), }; default: diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModMuted.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModMuted.cs new file mode 100644 index 0000000000..e0b98e491e --- /dev/null +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModMuted.cs @@ -0,0 +1,11 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModMuted : ModMuted + { + } +} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModMuted.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMuted.cs new file mode 100644 index 0000000000..5e3ee37b61 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Mods/OsuModMuted.cs @@ -0,0 +1,12 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Objects; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModMuted : ModMuted + { + } +} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 5f37b0d040..30f56cdc7d 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -1,39 +1,39 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Rulesets.Osu.UI; -using osu.Game.Rulesets.UI; +using System; using System.Collections.Generic; +using System.Linq; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; -using osu.Game.Overlays.Settings; using osu.Framework.Input.Bindings; -using osu.Game.Rulesets.Osu.Edit; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Osu.Replays; -using osu.Game.Rulesets.Replays.Types; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; using osu.Game.Configuration; +using osu.Game.Graphics; +using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Difficulty; -using osu.Game.Rulesets.Osu.Scoring; -using osu.Game.Rulesets.Scoring; -using osu.Game.Scoring; -using osu.Game.Skinning; -using System; -using System.Linq; -using osu.Framework.Extensions.EnumExtensions; +using osu.Game.Rulesets.Osu.Edit; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Replays; +using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Osu.Skinning.Legacy; using osu.Game.Rulesets.Osu.Statistics; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.Replays.Types; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Scoring; using osu.Game.Screens.Ranking.Statistics; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu { @@ -188,6 +188,7 @@ namespace osu.Game.Rulesets.Osu new OsuModTraceable(), new OsuModBarrelRoll(), new OsuModApproachDifferent(), + new OsuModMuted(), }; case ModType.System: diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModMuted.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModMuted.cs new file mode 100644 index 0000000000..d6bd7c4ee8 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModMuted.cs @@ -0,0 +1,11 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Taiko.Objects; + +namespace osu.Game.Rulesets.Taiko.Mods +{ + public class TaikoModMuted : ModMuted + { + } +} diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index ab5fcf6336..428ae66a31 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -1,32 +1,32 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Taiko.Mods; -using osu.Game.Rulesets.Taiko.UI; -using osu.Game.Rulesets.UI; +using System; using System.Collections.Generic; +using System.Linq; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; -using osu.Game.Rulesets.Replays.Types; -using osu.Game.Rulesets.Taiko.Replays; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; +using osu.Game.Graphics; using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Beatmaps; using osu.Game.Rulesets.Taiko.Difficulty; -using osu.Game.Rulesets.Taiko.Scoring; -using osu.Game.Scoring; -using System; -using System.Linq; -using osu.Framework.Extensions.EnumExtensions; -using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Taiko.Edit; +using osu.Game.Rulesets.Taiko.Mods; using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Replays; +using osu.Game.Rulesets.Taiko.Scoring; using osu.Game.Rulesets.Taiko.Skinning.Legacy; +using osu.Game.Rulesets.Taiko.UI; +using osu.Game.Rulesets.UI; +using osu.Game.Scoring; using osu.Game.Screens.Ranking.Statistics; using osu.Game.Skinning; @@ -149,7 +149,8 @@ namespace osu.Game.Rulesets.Taiko case ModType.Fun: return new Mod[] { - new MultiMod(new ModWindUp(), new ModWindDown()) + new MultiMod(new ModWindUp(), new ModWindDown()), + new TaikoModMuted(), }; default: diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index a15f80ca21..0325fa588f 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -12,8 +12,8 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Audio; using osu.Framework.Graphics.Containers; -using osu.Framework.Utils; using osu.Framework.Threading; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; @@ -419,15 +419,20 @@ namespace osu.Game.Overlays } /// - /// Resets the speed adjustments currently applied on and applies the mod adjustments if is true. + /// Resets the track adjustments currently applied on and applies the mod adjustments if is true. /// /// - /// Does not reset speed adjustments applied directly to the beatmap track. + /// Does not reset any adjustments applied directly to the beatmap track. /// public void ResetTrackAdjustments() { CurrentTrack.ResetSpeedAdjustments(); + // Adjustments other than speed should also be reset + // e.g. ModMuted may apply volume adjustments + CurrentTrack.RemoveAllAdjustments(AdjustableProperty.Balance); + CurrentTrack.RemoveAllAdjustments(AdjustableProperty.Volume); + if (allowRateAdjustments) { foreach (var mod in mods.Value.OfType()) diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs new file mode 100644 index 0000000000..dff3982041 --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -0,0 +1,93 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Track; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Game.Audio; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Configuration; +using osu.Game.Graphics.Containers; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.UI; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModMuted : Mod + { + public override string Name => "Muted"; + public override string Acronym => "MU"; + public override IconUsage? Icon => FontAwesome.Solid.VolumeMute; + public override string Description => "Who needs music in a rhythm game?"; + public override ModType Type => ModType.Fun; + public override double ScoreMultiplier => 1; + } + + public abstract class ModMuted : ModMuted, IApplicableToDrawableRuleset, IApplicableToTrack + where TObject : HitObject + { + private readonly BindableNumber volumeAdjust = new BindableDouble(); + + [SettingSource("Enable metronome", "Add a metronome to help you keep track of the rhythm.")] + public BindableBool EnableMetronome { get; } = new BindableBool + { + Default = true, + Value = true + }; + + public void ApplyToTrack(ITrack track) + { + track.AddAdjustment(AdjustableProperty.Volume, volumeAdjust); + } + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + if (EnableMetronome.Value) + drawableRuleset.Overlays.Add(new MetronomeBeatContainer(drawableRuleset.Beatmap.HitObjects.First().StartTime)); + } + + public class MetronomeBeatContainer : BeatSyncedContainer + { + private readonly double firstHitTime; + + private PausableSkinnableSound sample; + + public MetronomeBeatContainer(double firstHitTime) + { + this.firstHitTime = firstHitTime; + AllowMistimedEventFiring = false; + Divisor = 1; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChildren = new Drawable[] + { + sample = new PausableSkinnableSound(new SampleInfo("Gameplay/catch-banana")) + }; + } + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) + { + base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + + if (!IsBeatSyncedWithTrack) return; + + int timeSignature = (int)timingPoint.TimeSignature; + + // play metronome from one measure before the first object. + if (BeatSyncClock.CurrentTime < firstHitTime - timingPoint.BeatLength * timeSignature) + return; + + sample.Frequency.Value = beatIndex % timeSignature == 0 ? 1 : 0.5f; + sample.Play(); + } + } + } +} From 22d83c75e3a612d77ab681c704aa3596c7d3834e Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Wed, 28 Jul 2021 18:32:38 +0800 Subject: [PATCH 0691/2442] Revert imports re-ordering Out of the scope of this PR --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 26 +++++++++--------- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 18 ++++++------- osu.Game.Rulesets.Osu/OsuRuleset.cs | 36 ++++++++++++------------- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 32 +++++++++++----------- osu.Game/Overlays/MusicController.cs | 2 +- 5 files changed, 57 insertions(+), 57 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index c77caf0cef..eafa1b9b9d 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -1,30 +1,30 @@ // 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 osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Rulesets.Catch.Mods; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; using System.Collections.Generic; -using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; -using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Replays; +using osu.Game.Rulesets.Replays.Types; using osu.Game.Beatmaps.Legacy; -using osu.Game.Graphics; using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Catch.Difficulty; -using osu.Game.Rulesets.Catch.Edit; -using osu.Game.Rulesets.Catch.Mods; -using osu.Game.Rulesets.Catch.Replays; using osu.Game.Rulesets.Catch.Scoring; -using osu.Game.Rulesets.Catch.Skinning.Legacy; -using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; using osu.Game.Scoring; +using System; +using osu.Framework.Extensions.EnumExtensions; +using osu.Game.Rulesets.Catch.Edit; +using osu.Game.Rulesets.Catch.Skinning.Legacy; +using osu.Game.Rulesets.Edit; using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 3615d7e660..f4b6e10af4 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -2,16 +2,22 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.Mods; +using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; using System.Collections.Generic; using System.Linq; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; -using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mania.Replays; +using osu.Game.Rulesets.Replays.Types; using osu.Game.Beatmaps.Legacy; using osu.Game.Configuration; -using osu.Game.Graphics; using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Difficulty; @@ -21,18 +27,12 @@ using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Configuration; using osu.Game.Rulesets.Mania.Difficulty; using osu.Game.Rulesets.Mania.Edit; -using osu.Game.Rulesets.Mania.Mods; -using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Mania.Skinning.Legacy; -using osu.Game.Rulesets.Mania.UI; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; +using osu.Game.Skinning; using osu.Game.Scoring; using osu.Game.Screens.Ranking.Statistics; -using osu.Game.Skinning; namespace osu.Game.Rulesets.Mania { diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 30f56cdc7d..21a49f9495 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -1,39 +1,39 @@ // 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 osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.UI; using System.Collections.Generic; -using System.Linq; -using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Game.Overlays.Settings; using osu.Framework.Input.Bindings; -using osu.Game.Beatmaps; +using osu.Game.Rulesets.Osu.Edit; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Osu.Replays; +using osu.Game.Rulesets.Replays.Types; using osu.Game.Beatmaps.Legacy; using osu.Game.Configuration; -using osu.Game.Graphics; -using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Difficulty; -using osu.Game.Rulesets.Osu.Edit; -using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.Osu.Scoring; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; +using osu.Game.Skinning; +using System; +using System.Linq; +using osu.Framework.Extensions.EnumExtensions; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Skinning.Legacy; using osu.Game.Rulesets.Osu.Statistics; -using osu.Game.Rulesets.Osu.UI; -using osu.Game.Rulesets.Replays.Types; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; -using osu.Game.Scoring; using osu.Game.Screens.Ranking.Statistics; -using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu { diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 428ae66a31..adc924ba38 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -1,32 +1,32 @@ // 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 osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Taiko.Mods; +using osu.Game.Rulesets.Taiko.UI; +using osu.Game.Rulesets.UI; using System.Collections.Generic; -using System.Linq; -using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Legacy; -using osu.Game.Graphics; -using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Replays.Types; +using osu.Game.Rulesets.Taiko.Replays; +using osu.Game.Beatmaps.Legacy; +using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Beatmaps; using osu.Game.Rulesets.Taiko.Difficulty; -using osu.Game.Rulesets.Taiko.Edit; -using osu.Game.Rulesets.Taiko.Mods; -using osu.Game.Rulesets.Taiko.Objects; -using osu.Game.Rulesets.Taiko.Replays; using osu.Game.Rulesets.Taiko.Scoring; -using osu.Game.Rulesets.Taiko.Skinning.Legacy; -using osu.Game.Rulesets.Taiko.UI; -using osu.Game.Rulesets.UI; using osu.Game.Scoring; +using System; +using System.Linq; +using osu.Framework.Extensions.EnumExtensions; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Taiko.Edit; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Skinning.Legacy; using osu.Game.Screens.Ranking.Statistics; using osu.Game.Skinning; diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 0325fa588f..3978d2aa6e 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -12,8 +12,8 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Audio; using osu.Framework.Graphics.Containers; -using osu.Framework.Threading; using osu.Framework.Utils; +using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; From 1ed4fdd5f559554a947e1889ef06c4d3366eeaf1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Jul 2021 20:13:40 +0900 Subject: [PATCH 0692/2442] Avoid deserialisation JSON request content when error is not present (or not relevant) --- osu.Game/Online/API/APIRequest.cs | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index e6bfca166e..df0901fd93 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -171,19 +171,24 @@ namespace osu.Game.Online.API WebRequest?.Abort(); - string responseString = WebRequest?.GetResponseString(); - - if (!string.IsNullOrEmpty(responseString)) + // in the case of a cancellation we don't care about whether there's an error in the response. + if (!(e is OperationCanceledException)) { - try - { - // attempt to decode a displayable error string. - var error = JsonConvert.DeserializeObject(responseString); - if (error != null) - e = new APIException(error.ErrorMessage, e); - } - catch + string responseString = WebRequest?.GetResponseString(); + + // naive check whether there's an error in the response to avoid unnecessary JSON deserialisation. + if (!string.IsNullOrEmpty(responseString) && responseString.Contains(@"""error""")) { + try + { + // attempt to decode a displayable error string. + var error = JsonConvert.DeserializeObject(responseString); + if (error != null) + e = new APIException(error.ErrorMessage, e); + } + catch + { + } } } From cd2a1af6de82dcb4c382890e6e176e8fbd479065 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Jul 2021 20:46:02 +0900 Subject: [PATCH 0693/2442] Fix `HubClientConnector` reconnecting with no delay on server-triggered error --- osu.Game/Online/HubClientConnector.cs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/osu.Game/Online/HubClientConnector.cs b/osu.Game/Online/HubClientConnector.cs index 3839762e46..90049a6501 100644 --- a/osu.Game/Online/HubClientConnector.cs +++ b/osu.Game/Online/HubClientConnector.cs @@ -116,10 +116,7 @@ namespace osu.Game.Online } catch (Exception e) { - Logger.Log($"{clientName} connection error: {e}", LoggingTarget.Network); - - // retry on any failure. - await Task.Delay(5000, cancellationToken).ConfigureAwait(false); + await handleErrorAndDelay(e, cancellationToken).ConfigureAwait(false); } } } @@ -129,6 +126,15 @@ namespace osu.Game.Online } } + /// + /// Handles an exception and delays an async flow. + /// + private async Task handleErrorAndDelay(Exception exception, CancellationToken cancellationToken) + { + Logger.Log($"{clientName} connection error: {exception}", LoggingTarget.Network); + await Task.Delay(5000, cancellationToken).ConfigureAwait(false); + } + private HubConnection buildConnection(CancellationToken cancellationToken) { var builder = new HubConnectionBuilder() @@ -155,17 +161,18 @@ namespace osu.Game.Online return newConnection; } - private Task onConnectionClosed(Exception? ex, CancellationToken cancellationToken) + private async Task onConnectionClosed(Exception? ex, CancellationToken cancellationToken) { isConnected.Value = false; - Logger.Log(ex != null ? $"{clientName} lost connection: {ex}" : $"{clientName} disconnected", LoggingTarget.Network); + if (ex != null) + await handleErrorAndDelay(ex, cancellationToken).ConfigureAwait(false); + else + Logger.Log($"{clientName} disconnected", LoggingTarget.Network); // make sure a disconnect wasn't triggered (and this is still the active connection). if (!cancellationToken.IsCancellationRequested) - Task.Run(connect, default); - - return Task.CompletedTask; + await Task.Run(connect, default).ConfigureAwait(false); } private async Task disconnect(bool takeLock) From 89f0739a4ad7d3bd190825e45cf2bc28a8ab09fc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 28 Jul 2021 21:54:11 +0900 Subject: [PATCH 0694/2442] Update with framework changes --- osu.Game/Audio/PreviewTrackManager.cs | 6 +++++- osu.Game/Tests/Visual/OsuTestScene.cs | 2 -- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Audio/PreviewTrackManager.cs b/osu.Game/Audio/PreviewTrackManager.cs index e2c4657057..dab5fcbe5f 100644 --- a/osu.Game/Audio/PreviewTrackManager.cs +++ b/osu.Game/Audio/PreviewTrackManager.cs @@ -148,8 +148,12 @@ namespace osu.Game.Audio if (dataStream == null) return null; - Track track = new TrackBass(dataStream, (IBassAudioMixer)defaultMixer); + // Todo: This is quite unsafe. TrackBass shouldn't be exposed as public. + Track track = new TrackBass(dataStream); + + defaultMixer.Add(track); AddItem(track); + return track; } diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 242c3298e8..57e400a77e 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -10,7 +10,6 @@ using JetBrains.Annotations; using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; -using osu.Framework.Audio.Mixing; using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -291,7 +290,6 @@ namespace osu.Game.Tests.Visual private bool running; public TrackVirtualManual(IFrameBasedClock referenceClock) - : base(new NullAudioMixer()) { this.referenceClock = referenceClock; Length = double.PositiveInfinity; From e89f33483dd796572fc89db8d267bb1a23d05c2a Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Wed, 28 Jul 2021 21:52:01 +0800 Subject: [PATCH 0695/2442] Code formatting fixes --- osu.Game.Rulesets.Catch/Mods/CatchModMuted.cs | 1 + osu.Game.Rulesets.Mania/Mods/ManiaModMuted.cs | 1 + osu.Game.Rulesets.Taiko/Mods/TaikoModMuted.cs | 1 + 3 files changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModMuted.cs b/osu.Game.Rulesets.Catch/Mods/CatchModMuted.cs index 4c73a6a21f..6d2565440a 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModMuted.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModMuted.cs @@ -1,5 +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 osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Mods; diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModMuted.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModMuted.cs index e0b98e491e..33ebcf303a 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModMuted.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModMuted.cs @@ -1,5 +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 osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mods; diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModMuted.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModMuted.cs index d6bd7c4ee8..0f1e0b2885 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModMuted.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModMuted.cs @@ -1,5 +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 osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Taiko.Objects; From fbd02dc8301446a380e479b9ed19a71ac116ee54 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 28 Jul 2021 18:24:29 +0200 Subject: [PATCH 0696/2442] Update framework. --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 3a9fdfebab..1c180a6b39 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 4baaf89c67..2efcfeb278 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 66cae06713..2630614c03 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 3a5324c94794af9e3985c2ef128cc80e66295bf3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Jul 2021 23:58:57 +0900 Subject: [PATCH 0697/2442] Fix aborting an `APIRequest` potentially resulting in incorrect success --- osu.Game/Online/API/APIRequest.cs | 90 +++++++++++++------------------ 1 file changed, 38 insertions(+), 52 deletions(-) diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index df0901fd93..e117293ce6 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -86,8 +86,6 @@ namespace osu.Game.Online.API /// private APIRequestCompletionState completionState; - private Action pendingFailure; - public void Perform(IAPIProvider api) { if (!(api is APIAccess apiAccess)) @@ -99,29 +97,23 @@ namespace osu.Game.Online.API API = apiAccess; User = apiAccess.LocalUser.Value; - if (checkAndScheduleFailure()) - return; + if (isFailing) return; WebRequest = CreateWebRequest(); WebRequest.Failed += Fail; WebRequest.AllowRetryOnTimeout = false; WebRequest.AddHeader("Authorization", $"Bearer {API.AccessToken}"); - if (checkAndScheduleFailure()) - return; + if (isFailing) return; - if (!WebRequest.Aborted) // could have been aborted by a Cancel() call - { - Logger.Log($@"Performing request {this}", LoggingTarget.Network); - WebRequest.Perform(); - } + Logger.Log($@"Performing request {this}", LoggingTarget.Network); + WebRequest.Perform(); - if (checkAndScheduleFailure()) - return; + if (isFailing) return; PostProcess(); - API.Schedule(TriggerSuccess); + TriggerSuccess(); } /// @@ -141,7 +133,10 @@ namespace osu.Game.Online.API completionState = APIRequestCompletionState.Completed; } - Success?.Invoke(); + if (API == null) + Success?.Invoke(); + else + API.Schedule(() => Success?.Invoke()); } internal void TriggerFailure(Exception e) @@ -154,7 +149,10 @@ namespace osu.Game.Online.API completionState = APIRequestCompletionState.Failed; } - Failure?.Invoke(e); + if (API == null) + Failure?.Invoke(e); + else + API.Schedule(() => Failure?.Invoke(e)); } public void Cancel() => Fail(new OperationCanceledException(@"Request cancelled")); @@ -163,59 +161,47 @@ namespace osu.Game.Online.API { lock (completionStateLock) { - // while it doesn't matter if code following this check is run more than once, - // this avoids unnecessarily performing work where we are already sure the user has been informed. if (completionState != APIRequestCompletionState.Waiting) return; - } - WebRequest?.Abort(); + WebRequest?.Abort(); - // in the case of a cancellation we don't care about whether there's an error in the response. - if (!(e is OperationCanceledException)) - { - string responseString = WebRequest?.GetResponseString(); - - // naive check whether there's an error in the response to avoid unnecessary JSON deserialisation. - if (!string.IsNullOrEmpty(responseString) && responseString.Contains(@"""error""")) + // in the case of a cancellation we don't care about whether there's an error in the response. + if (!(e is OperationCanceledException)) { - try - { - // attempt to decode a displayable error string. - var error = JsonConvert.DeserializeObject(responseString); - if (error != null) - e = new APIException(error.ErrorMessage, e); - } - catch + string responseString = WebRequest?.GetResponseString(); + + // naive check whether there's an error in the response to avoid unnecessary JSON deserialisation. + if (!string.IsNullOrEmpty(responseString) && responseString.Contains(@"""error""")) { + try + { + // attempt to decode a displayable error string. + var error = JsonConvert.DeserializeObject(responseString); + if (error != null) + e = new APIException(error.ErrorMessage, e); + } + catch + { + } } } - } - Logger.Log($@"Failing request {this} ({e})", LoggingTarget.Network); - pendingFailure = () => TriggerFailure(e); - checkAndScheduleFailure(); + Logger.Log($@"Failing request {this} ({e})", LoggingTarget.Network); + TriggerFailure(e); + } } /// - /// Checked for cancellation or error. Also queues up the Failed event if we can. + /// Whether this request is in a failing or failed state. /// - /// Whether we are in a failed or cancelled state. - private bool checkAndScheduleFailure() + private bool isFailing { - lock (completionStateLock) + get { - if (pendingFailure == null) + lock (completionStateLock) return completionState == APIRequestCompletionState.Failed; } - - if (API == null) - pendingFailure(); - else - API.Schedule(pendingFailure); - - pendingFailure = null; - return true; } private class DisplayableError From 0620cd130e6427b3c468c03b8a4a0a19a1854b42 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 29 Jul 2021 14:41:47 +0800 Subject: [PATCH 0698/2442] Change mod description --- osu.Game/Rulesets/Mods/ModMuted.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index dff3982041..aa2006cf73 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mods public override string Name => "Muted"; public override string Acronym => "MU"; public override IconUsage? Icon => FontAwesome.Solid.VolumeMute; - public override string Description => "Who needs music in a rhythm game?"; + public override string Description => "Can you still feel the rhythm without music?"; public override ModType Type => ModType.Fun; public override double ScoreMultiplier => 1; } From 18e760ee91916bcca6071a51cdca628498dd8439 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 29 Jul 2021 14:52:18 +0800 Subject: [PATCH 0699/2442] Extract metronome from `OsuModTarget` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 45 +--------------- .../Rulesets/Mods/MetronomeBeatContainer.cs | 53 +++++++++++++++++++ 2 files changed, 54 insertions(+), 44 deletions(-) create mode 100644 osu.Game/Rulesets/Mods/MetronomeBeatContainer.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index e21d1da009..576cc2da13 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -4,8 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; @@ -16,7 +14,6 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Timing; using osu.Game.Configuration; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; @@ -29,7 +26,6 @@ using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.Utils; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; -using osu.Game.Skinning; using osuTK; using osuTK.Graphics; @@ -341,46 +337,7 @@ namespace osu.Game.Rulesets.Osu.Mods public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { - drawableRuleset.Overlays.Add(new TargetBeatContainer(drawableRuleset.Beatmap.HitObjects.First().StartTime)); - } - - public class TargetBeatContainer : BeatSyncedContainer - { - private readonly double firstHitTime; - - private PausableSkinnableSound sample; - - public TargetBeatContainer(double firstHitTime) - { - this.firstHitTime = firstHitTime; - AllowMistimedEventFiring = false; - Divisor = 1; - } - - [BackgroundDependencyLoader] - private void load() - { - InternalChildren = new Drawable[] - { - sample = new PausableSkinnableSound(new SampleInfo("Gameplay/catch-banana")) - }; - } - - protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) - { - base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); - - if (!IsBeatSyncedWithTrack) return; - - int timeSignature = (int)timingPoint.TimeSignature; - - // play metronome from one measure before the first object. - if (BeatSyncClock.CurrentTime < firstHitTime - timingPoint.BeatLength * timeSignature) - return; - - sample.Frequency.Value = beatIndex % timeSignature == 0 ? 1 : 0.5f; - sample.Play(); - } + drawableRuleset.Overlays.Add(new MetronomeBeatContainer(drawableRuleset.Beatmap.HitObjects.First().StartTime)); } #endregion diff --git a/osu.Game/Rulesets/Mods/MetronomeBeatContainer.cs b/osu.Game/Rulesets/Mods/MetronomeBeatContainer.cs new file mode 100644 index 0000000000..c5bb2c918f --- /dev/null +++ b/osu.Game/Rulesets/Mods/MetronomeBeatContainer.cs @@ -0,0 +1,53 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Audio.Track; +using osu.Framework.Graphics; +using osu.Game.Audio; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics.Containers; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Mods +{ + public class MetronomeBeatContainer : BeatSyncedContainer + { + private readonly double firstHitTime; + + private PausableSkinnableSound sample; + + /// Start time of the first hit object, used for providing a count down. + public MetronomeBeatContainer(double firstHitTime) + { + this.firstHitTime = firstHitTime; + AllowMistimedEventFiring = false; + Divisor = 1; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChildren = new Drawable[] + { + sample = new PausableSkinnableSound(new SampleInfo("Gameplay/catch-banana")) + }; + } + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) + { + base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + + if (!IsBeatSyncedWithTrack) return; + + int timeSignature = (int)timingPoint.TimeSignature; + + // play metronome from one measure before the first object. + if (BeatSyncClock.CurrentTime < firstHitTime - timingPoint.BeatLength * timeSignature) + return; + + sample.Frequency.Value = beatIndex % timeSignature == 0 ? 1 : 0.5f; + sample.Play(); + } + } +} From 0196141335b11d0f5b21d0bf700eb86e1024bd0f Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 29 Jul 2021 14:52:40 +0800 Subject: [PATCH 0700/2442] Remove unused constants --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 576cc2da13..615700e6b9 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -63,11 +63,6 @@ namespace osu.Game.Rulesets.Osu.Mods /// private const float distance_cap = 380f; - // The distances from the hit objects to the borders of the playfield they start to "turn around" and curve towards the middle. - // The closer the hit objects draw to the border, the sharper the turn - private const byte border_distance_x = 192; - private const byte border_distance_y = 144; - /// /// The extent of rotation towards playfield centre when a circle is near the edge /// From 935984d20016d19ee0de9e057a19ae0f3f137a44 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 29 Jul 2021 15:17:21 +0800 Subject: [PATCH 0701/2442] Rename `MetronomeBeatContainer` to `Metronome` --- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 2 +- .../Rulesets/Mods/{MetronomeBeatContainer.cs => Metronome.cs} | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename osu.Game/Rulesets/Mods/{MetronomeBeatContainer.cs => Metronome.cs} (93%) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 615700e6b9..210d5e0403 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -332,7 +332,7 @@ namespace osu.Game.Rulesets.Osu.Mods public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { - drawableRuleset.Overlays.Add(new MetronomeBeatContainer(drawableRuleset.Beatmap.HitObjects.First().StartTime)); + drawableRuleset.Overlays.Add(new Metronome(drawableRuleset.Beatmap.HitObjects.First().StartTime)); } #endregion diff --git a/osu.Game/Rulesets/Mods/MetronomeBeatContainer.cs b/osu.Game/Rulesets/Mods/Metronome.cs similarity index 93% rename from osu.Game/Rulesets/Mods/MetronomeBeatContainer.cs rename to osu.Game/Rulesets/Mods/Metronome.cs index c5bb2c918f..ee0a42b4bc 100644 --- a/osu.Game/Rulesets/Mods/MetronomeBeatContainer.cs +++ b/osu.Game/Rulesets/Mods/Metronome.cs @@ -11,14 +11,14 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Mods { - public class MetronomeBeatContainer : BeatSyncedContainer + public class Metronome : BeatSyncedContainer { private readonly double firstHitTime; private PausableSkinnableSound sample; /// Start time of the first hit object, used for providing a count down. - public MetronomeBeatContainer(double firstHitTime) + public Metronome(double firstHitTime) { this.firstHitTime = firstHitTime; AllowMistimedEventFiring = false; From 89e8296eb1b17a42eae1f24422bf099a76a1ac37 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 29 Jul 2021 15:39:26 +0800 Subject: [PATCH 0702/2442] Reset all types of adjustments in `MusicController`; Rename `AllowRateAdjustments` to `AllowTrackAdjustments` --- osu.Game/OsuGame.cs | 2 +- osu.Game/Overlays/MusicController.cs | 23 +++++++++++-------- osu.Game/Screens/Edit/Editor.cs | 2 +- osu.Game/Screens/IOsuScreen.cs | 4 ++-- osu.Game/Screens/Menu/MainMenu.cs | 2 +- .../Spectate/MultiSpectatorScreen.cs | 2 +- osu.Game/Screens/OsuScreen.cs | 2 +- osu.Game/Screens/Play/Player.cs | 2 +- osu.Game/Screens/StartupScreen.cs | 2 +- 9 files changed, 22 insertions(+), 19 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 8e32b2e6a7..3cfa2cc755 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1058,7 +1058,7 @@ namespace osu.Game OverlayActivationMode.BindTo(newOsuScreen.OverlayActivationMode); API.Activity.BindTo(newOsuScreen.Activity); - MusicController.AllowRateAdjustments = newOsuScreen.AllowRateAdjustments; + MusicController.AllowTrackAdjustments = newOsuScreen.AllowTrackAdjustments; if (newOsuScreen.HideOverlaysOnEnter) CloseAllOverlays(); diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index a15f80ca21..8fd50c3df2 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -400,35 +400,38 @@ namespace osu.Game.Overlays NextTrack(); } - private bool allowRateAdjustments; + private bool allowTrackAdjustments; /// - /// Whether mod rate adjustments are allowed to be applied. + /// Whether mod track adjustments are allowed to be applied. /// - public bool AllowRateAdjustments + public bool AllowTrackAdjustments { - get => allowRateAdjustments; + get => allowTrackAdjustments; set { - if (allowRateAdjustments == value) + if (allowTrackAdjustments == value) return; - allowRateAdjustments = value; + allowTrackAdjustments = value; ResetTrackAdjustments(); } } /// - /// Resets the speed adjustments currently applied on and applies the mod adjustments if is true. + /// Resets the adjustments currently applied on and applies the mod adjustments if is true. /// /// - /// Does not reset speed adjustments applied directly to the beatmap track. + /// Does not reset any adjustments applied directly to the beatmap track. /// public void ResetTrackAdjustments() { - CurrentTrack.ResetSpeedAdjustments(); + CurrentTrack.RemoveAllAdjustments(AdjustableProperty.Balance); + CurrentTrack.RemoveAllAdjustments(AdjustableProperty.Frequency); + CurrentTrack.RemoveAllAdjustments(AdjustableProperty.Tempo); + CurrentTrack.RemoveAllAdjustments(AdjustableProperty.Volume); - if (allowRateAdjustments) + if (allowTrackAdjustments) { foreach (var mod in mods.Value.OfType()) mod.ApplyToTrack(CurrentTrack); diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index b6dc97a7f6..61a3b0f5cc 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -55,7 +55,7 @@ namespace osu.Game.Screens.Edit public override bool DisallowExternalBeatmapRulesetChanges => true; - public override bool AllowRateAdjustments => false; + public override bool AllowTrackAdjustments => false; protected bool HasUnsavedChanges => lastSavedHash != changeHandler.CurrentStateHash; diff --git a/osu.Game/Screens/IOsuScreen.cs b/osu.Game/Screens/IOsuScreen.cs index 0434135547..17384c161c 100644 --- a/osu.Game/Screens/IOsuScreen.cs +++ b/osu.Game/Screens/IOsuScreen.cs @@ -59,9 +59,9 @@ namespace osu.Game.Screens Bindable Ruleset { get; } /// - /// Whether mod rate adjustments are allowed to be applied. + /// Whether mod track adjustments are allowed to be applied. /// - bool AllowRateAdjustments { get; } + bool AllowTrackAdjustments { get; } /// /// Invoked when the back button has been pressed to close any overlays before exiting this . diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index e53b46f391..1d0182a945 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -36,7 +36,7 @@ namespace osu.Game.Screens.Menu public override bool AllowExternalScreenChange => true; - public override bool AllowRateAdjustments => false; + public override bool AllowTrackAdjustments => false; private Screen songSelect; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs index 2a2759e0dd..56ed7a9564 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs @@ -24,7 +24,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate public override bool DisallowExternalBeatmapRulesetChanges => true; // We are managing our own adjustments. For now, this happens inside the Player instances themselves. - public override bool AllowRateAdjustments => false; + public override bool AllowTrackAdjustments => false; /// /// Whether all spectating players have finished loading. diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index c3b2612e79..e3fe14a585 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -81,7 +81,7 @@ namespace osu.Game.Screens public virtual float BackgroundParallaxAmount => 1; - public virtual bool AllowRateAdjustments => true; + public virtual bool AllowTrackAdjustments => true; public Bindable Beatmap { get; private set; } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 0e4d38660b..d76d44767a 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -56,7 +56,7 @@ namespace osu.Game.Screens.Play protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.UserTriggered; // We are managing our own adjustments (see OnEntering/OnExiting). - public override bool AllowRateAdjustments => false; + public override bool AllowTrackAdjustments => false; private readonly IBindable gameActive = new Bindable(true); diff --git a/osu.Game/Screens/StartupScreen.cs b/osu.Game/Screens/StartupScreen.cs index e5e134fd39..15f75d7cff 100644 --- a/osu.Game/Screens/StartupScreen.cs +++ b/osu.Game/Screens/StartupScreen.cs @@ -16,7 +16,7 @@ namespace osu.Game.Screens public override bool CursorVisible => false; - public override bool AllowRateAdjustments => false; + public override bool AllowTrackAdjustments => false; protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled; } From 81f23e111100ca6ee6a5a4858ec9b63c01560a7f Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 29 Jul 2021 17:12:01 +0900 Subject: [PATCH 0703/2442] Manage catcher trails by lifetime entries --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 6 +- osu.Game.Rulesets.Catch/UI/CatcherTrail.cs | 44 +++++++--- .../UI/CatcherTrailAnimation.cs | 12 +++ .../UI/CatcherTrailDisplay.cs | 80 ++++++++++++------- .../UI/CatcherTrailEntry.cs | 31 +++++++ 5 files changed, 133 insertions(+), 40 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/UI/CatcherTrailAnimation.cs create mode 100644 osu.Game.Rulesets.Catch/UI/CatcherTrailEntry.cs diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 78f7e34649..21bf049ccc 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -110,14 +110,14 @@ namespace osu.Game.Rulesets.Catch.UI comboDisplay.X = Catcher.X; if (!lastHyperDashState && Catcher.HyperDashing && Time.Elapsed > 0) - catcherTrails.DisplayHyperDashAfterImage(Catcher.CurrentState, Catcher.X, Catcher.BodyScale); + displayCatcherTrail(CatcherTrailAnimation.HyperDashAfterimage); if (Catcher.Dashing || Catcher.HyperDashing) { double generationInterval = Catcher.HyperDashing ? 25 : 50; if (Time.Current - catcherTrails.LastDashTrailTime >= generationInterval) - catcherTrails.DisplayDashTrail(Catcher.CurrentState, Catcher.X, Catcher.BodyScale, Catcher.HyperDashing); + displayCatcherTrail(Catcher.HyperDashing ? CatcherTrailAnimation.HyperDashing : CatcherTrailAnimation.Dashing); } lastHyperDashState = Catcher.HyperDashing; @@ -173,5 +173,7 @@ namespace osu.Game.Rulesets.Catch.UI break; } } + + private void displayCatcherTrail(CatcherTrailAnimation animation) => catcherTrails.Add(new CatcherTrailEntry(Time.Current, Catcher.CurrentState, Catcher.X, Catcher.BodyScale, animation)); } } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs index ff1a7d7a61..a7a2941a7a 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs @@ -2,8 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; -using osu.Framework.Graphics.Pooling; using osu.Framework.Timing; +using osu.Game.Rulesets.Objects.Pooling; using osuTK; namespace osu.Game.Rulesets.Catch.UI @@ -12,13 +12,8 @@ namespace osu.Game.Rulesets.Catch.UI /// A trail of the catcher. /// It also represents a hyper dash afterimage. /// - public class CatcherTrail : PoolableDrawable + public class CatcherTrail : PoolableDrawableWithLifetime { - public CatcherAnimationState AnimationState - { - set => body.AnimationState.Value = value; - } - private readonly SkinnableCatcher body; public CatcherTrail() @@ -34,11 +29,40 @@ namespace osu.Game.Rulesets.Catch.UI }; } - protected override void FreeAfterUse() + protected override void OnApply(CatcherTrailEntry entry) + { + Scale = entry.Scale; + Position = new Vector2(entry.Position, 0); + Alpha = 1; + + body.AnimationState.Value = entry.CatcherState; + + using (BeginAbsoluteSequence(entry.LifetimeStart, false)) + applyTransforms(entry.Animation); + } + + protected override void OnFree(CatcherTrailEntry entry) { ClearTransforms(); - Alpha = 1; - base.FreeAfterUse(); + } + + private void applyTransforms(CatcherTrailAnimation animation) + { + switch (animation) + { + case CatcherTrailAnimation.Dashing: + case CatcherTrailAnimation.HyperDashing: + this.FadeTo(0.4f).FadeOut(800, Easing.OutQuint); + break; + + case CatcherTrailAnimation.HyperDashAfterimage: + this.MoveToOffset(new Vector2(0, -10), 1200, Easing.In); + this.ScaleTo(Scale * 0.95f).ScaleTo(Scale * 1.2f, 1200, Easing.In); + this.FadeOut(1200); + break; + } + + Expire(); } } } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailAnimation.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailAnimation.cs new file mode 100644 index 0000000000..3ea99badc1 --- /dev/null +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailAnimation.cs @@ -0,0 +1,12 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Catch.UI +{ + public enum CatcherTrailAnimation + { + Dashing, + HyperDashing, + HyperDashAfterimage + } +} diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs index abc76fc925..f451f84c95 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs @@ -2,12 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Pooling; using osu.Game.Rulesets.Catch.Skinning; +using osu.Game.Rulesets.Objects.Pooling; using osu.Game.Skinning; -using osuTK; using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.UI @@ -16,7 +17,7 @@ namespace osu.Game.Rulesets.Catch.UI /// Represents a component responsible for displaying /// the appropriate catcher trails when requested to. /// - public class CatcherTrailDisplay : SkinReloadableDrawable + public class CatcherTrailDisplay : PooledDrawableWithLifetimeContainer { /// /// The most recent time a dash trail was added to this container. @@ -29,12 +30,17 @@ namespace osu.Game.Rulesets.Catch.UI public Color4 HyperDashAfterImageColour => hyperDashAfterImages.Colour; + protected override bool RemoveRewoundEntry => true; + private readonly DrawablePool trailPool; private readonly Container dashTrails; private readonly Container hyperDashTrails; private readonly Container hyperDashAfterImages; + [Resolved] + private ISkinSource skin { get; set; } + public CatcherTrailDisplay() { RelativeSizeAxes = Axes.Both; @@ -48,50 +54,60 @@ namespace osu.Game.Rulesets.Catch.UI }; } - protected override void SkinChanged(ISkinSource skin) + protected override void LoadComplete() { - base.SkinChanged(skin); + base.LoadComplete(); + skin.SourceChanged += skinSourceChanged; + skinSourceChanged(); + } + + private void skinSourceChanged() + { hyperDashTrails.Colour = skin.GetConfig(CatchSkinColour.HyperDash)?.Value ?? Catcher.DEFAULT_HYPER_DASH_COLOUR; hyperDashAfterImages.Colour = skin.GetConfig(CatchSkinColour.HyperDashAfterImage)?.Value ?? hyperDashTrails.Colour; } - /// - /// Displays a hyper-dash after-image of the catcher. - /// - public void DisplayHyperDashAfterImage(CatcherAnimationState animationState, float x, Vector2 scale) + protected override void AddDrawable(CatcherTrailEntry entry, CatcherTrail drawable) { - var trail = createTrail(animationState, x, scale); + switch (entry.Animation) + { + case CatcherTrailAnimation.Dashing: + dashTrails.Add(drawable); + break; - hyperDashAfterImages.Add(trail); + case CatcherTrailAnimation.HyperDashing: + hyperDashTrails.Add(drawable); + break; - trail.MoveToOffset(new Vector2(0, -10), 1200, Easing.In); - trail.ScaleTo(trail.Scale * 0.95f).ScaleTo(trail.Scale * 1.2f, 1200, Easing.In); - trail.FadeOut(1200); - trail.Expire(true); + case CatcherTrailAnimation.HyperDashAfterimage: + hyperDashAfterImages.Add(drawable); + break; + } } - public void DisplayDashTrail(CatcherAnimationState animationState, float x, Vector2 scale, bool hyperDashing) + protected override void RemoveDrawable(CatcherTrailEntry entry, CatcherTrail drawable) { - var trail = createTrail(animationState, x, scale); + switch (entry.Animation) + { + case CatcherTrailAnimation.Dashing: + dashTrails.Remove(drawable); + break; - if (hyperDashing) - hyperDashTrails.Add(trail); - else - dashTrails.Add(trail); + case CatcherTrailAnimation.HyperDashing: + hyperDashTrails.Remove(drawable); + break; - trail.FadeTo(0.4f).FadeOut(800, Easing.OutQuint); - trail.Expire(true); + case CatcherTrailAnimation.HyperDashAfterimage: + hyperDashAfterImages.Remove(drawable); + break; + } } - private CatcherTrail createTrail(CatcherAnimationState animationState, float x, Vector2 scale) + protected override CatcherTrail GetDrawable(CatcherTrailEntry entry) { CatcherTrail trail = trailPool.Get(); - - trail.AnimationState = animationState; - trail.Scale = scale; - trail.Position = new Vector2(x, 0); - + trail.Apply(entry); return trail; } @@ -107,5 +123,13 @@ namespace osu.Game.Rulesets.Catch.UI return maxTime; } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (skin != null) + skin.SourceChanged -= skinSourceChanged; + } } } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailEntry.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailEntry.cs new file mode 100644 index 0000000000..3a40ab26cc --- /dev/null +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailEntry.cs @@ -0,0 +1,31 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics.Performance; +using osuTK; + +namespace osu.Game.Rulesets.Catch.UI +{ + public class CatcherTrailEntry : LifetimeEntry + { + public readonly CatcherAnimationState CatcherState; + + public readonly float Position; + + /// + /// The scaling of the catcher body. It also represents a flipped catcher (negative x component). + /// + public readonly Vector2 Scale; + + public readonly CatcherTrailAnimation Animation; + + public CatcherTrailEntry(double startTime, CatcherAnimationState catcherState, float position, Vector2 scale, CatcherTrailAnimation animation) + { + LifetimeStart = startTime; + CatcherState = catcherState; + Position = position; + Scale = scale; + Animation = animation; + } + } +} From a204ef39dd1663de2e191d29e8e96cd1ca772b2a Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 29 Jul 2021 17:32:20 +0900 Subject: [PATCH 0704/2442] Prevent catcher trail generation while rewinding --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 21bf049ccc..e99d33cd1b 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -109,7 +109,15 @@ namespace osu.Game.Rulesets.Catch.UI comboDisplay.X = Catcher.X; - if (!lastHyperDashState && Catcher.HyperDashing && Time.Elapsed > 0) + if (Time.Elapsed <= 0) + { + // This is probably a wrong value, but currently the true value is not recorded. + // Setting `true` will prevent generation of false-positive after-images (with more false-positives). + lastHyperDashState = true; + return; + } + + if (!lastHyperDashState && Catcher.HyperDashing) displayCatcherTrail(CatcherTrailAnimation.HyperDashAfterimage); if (Catcher.Dashing || Catcher.HyperDashing) From 888e8f1c8052c2abef891f60eecd18c079881eed Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Thu, 29 Jul 2021 21:18:07 +0800 Subject: [PATCH 0705/2442] Use shared metronome class --- osu.Game/Rulesets/Mods/ModMuted.cs | 47 +----------------------------- 1 file changed, 1 insertion(+), 46 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index aa2006cf73..9e69bc1386 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -2,19 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; -using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Bindables; -using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; -using osu.Game.Audio; -using osu.Game.Beatmaps.ControlPoints; using osu.Game.Configuration; -using osu.Game.Graphics.Containers; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; -using osu.Game.Skinning; namespace osu.Game.Rulesets.Mods { @@ -48,46 +42,7 @@ namespace osu.Game.Rulesets.Mods public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { if (EnableMetronome.Value) - drawableRuleset.Overlays.Add(new MetronomeBeatContainer(drawableRuleset.Beatmap.HitObjects.First().StartTime)); - } - - public class MetronomeBeatContainer : BeatSyncedContainer - { - private readonly double firstHitTime; - - private PausableSkinnableSound sample; - - public MetronomeBeatContainer(double firstHitTime) - { - this.firstHitTime = firstHitTime; - AllowMistimedEventFiring = false; - Divisor = 1; - } - - [BackgroundDependencyLoader] - private void load() - { - InternalChildren = new Drawable[] - { - sample = new PausableSkinnableSound(new SampleInfo("Gameplay/catch-banana")) - }; - } - - protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) - { - base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); - - if (!IsBeatSyncedWithTrack) return; - - int timeSignature = (int)timingPoint.TimeSignature; - - // play metronome from one measure before the first object. - if (BeatSyncClock.CurrentTime < firstHitTime - timingPoint.BeatLength * timeSignature) - return; - - sample.Frequency.Value = beatIndex % timeSignature == 0 ? 1 : 0.5f; - sample.Play(); - } + drawableRuleset.Overlays.Add(new Metronome(drawableRuleset.Beatmap.HitObjects.First().StartTime)); } } } From 5c5e33f4d744676baa7116aec7b576e950d61e41 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 01:53:08 +0900 Subject: [PATCH 0706/2442] Split out common logic for tournament game host tests --- .../NonVisual/CustomTourneyDirectoryTest.cs | 32 +++--------------- .../NonVisual/IPCLocationTest.cs | 28 ++-------------- .../NonVisual/TournamentHostTest.cs | 33 +++++++++++++++++++ 3 files changed, 41 insertions(+), 52 deletions(-) create mode 100644 osu.Game.Tournament.Tests/NonVisual/TournamentHostTest.cs diff --git a/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs b/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs index d2369056e1..fcc9f44f0c 100644 --- a/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs +++ b/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs @@ -1,21 +1,18 @@ // 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.IO; -using System.Threading; -using System.Threading.Tasks; using NUnit.Framework; using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Platform; -using osu.Game.Tournament.Configuration; using osu.Game.Tests; +using osu.Game.Tournament.Configuration; namespace osu.Game.Tournament.Tests.NonVisual { [TestFixture] - public class CustomTourneyDirectoryTest + public class CustomTourneyDirectoryTest : TournamentHostTest { [Test] public void TestDefaultDirectory() @@ -24,7 +21,7 @@ namespace osu.Game.Tournament.Tests.NonVisual { try { - var osu = loadOsu(host); + var osu = LoadTournament(host); var storage = osu.Dependencies.Get(); Assert.That(storage.GetFullPath("."), Is.EqualTo(Path.Combine(host.Storage.GetFullPath("."), "tournaments", "default"))); @@ -54,7 +51,7 @@ namespace osu.Game.Tournament.Tests.NonVisual try { - var osu = loadOsu(host); + var osu = LoadTournament(host); storage = osu.Dependencies.Get(); @@ -111,7 +108,7 @@ namespace osu.Game.Tournament.Tests.NonVisual try { - var osu = loadOsu(host); + var osu = LoadTournament(host); var storage = osu.Dependencies.Get(); @@ -151,25 +148,6 @@ namespace osu.Game.Tournament.Tests.NonVisual } } - private TournamentGameBase loadOsu(GameHost host) - { - var osu = new TournamentGameBase(); - Task.Run(() => host.Run(osu)) - .ContinueWith(t => Assert.Fail($"Host threw exception {t.Exception}"), TaskContinuationOptions.OnlyOnFaulted); - waitForOrAssert(() => osu.IsLoaded, @"osu! failed to start in a reasonable amount of time"); - return osu; - } - - private static void waitForOrAssert(Func result, string failureMessage, int timeout = 90000) - { - Task task = Task.Run(() => - { - while (!result()) Thread.Sleep(200); - }); - - Assert.IsTrue(task.Wait(timeout), failureMessage); - } - private string basePath(string testInstance) => Path.Combine(RuntimeInfo.StartupDirectory, "headless", testInstance); } } diff --git a/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs b/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs index e4eb5a36fb..eaa009c180 100644 --- a/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs +++ b/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs @@ -1,10 +1,7 @@ // 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.IO; -using System.Threading; -using System.Threading.Tasks; using NUnit.Framework; using osu.Framework; using osu.Framework.Allocation; @@ -15,7 +12,7 @@ using osu.Game.Tournament.IPC; namespace osu.Game.Tournament.Tests.NonVisual { [TestFixture] - public class IPCLocationTest + public class IPCLocationTest : TournamentHostTest { [Test] public void CheckIPCLocation() @@ -34,11 +31,11 @@ namespace osu.Game.Tournament.Tests.NonVisual try { - var osu = loadOsu(host); + var osu = LoadTournament(host); TournamentStorage storage = (TournamentStorage)osu.Dependencies.Get(); FileBasedIPC ipc = null; - waitForOrAssert(() => (ipc = osu.Dependencies.Get() as FileBasedIPC) != null, @"ipc could not be populated in a reasonable amount of time"); + WaitForOrAssert(() => (ipc = osu.Dependencies.Get() as FileBasedIPC) != null, @"ipc could not be populated in a reasonable amount of time"); Assert.True(ipc.SetIPCLocation(testStableInstallDirectory)); Assert.True(storage.AllTournaments.Exists("stable.json")); @@ -51,24 +48,5 @@ namespace osu.Game.Tournament.Tests.NonVisual } } } - - private TournamentGameBase loadOsu(GameHost host) - { - var osu = new TournamentGameBase(); - Task.Run(() => host.Run(osu)) - .ContinueWith(t => Assert.Fail($"Host threw exception {t.Exception}"), TaskContinuationOptions.OnlyOnFaulted); - waitForOrAssert(() => osu.IsLoaded, @"osu! failed to start in a reasonable amount of time"); - return osu; - } - - private static void waitForOrAssert(Func result, string failureMessage, int timeout = 90000) - { - Task task = Task.Run(() => - { - while (!result()) Thread.Sleep(200); - }); - - Assert.IsTrue(task.Wait(timeout), failureMessage); - } } } diff --git a/osu.Game.Tournament.Tests/NonVisual/TournamentHostTest.cs b/osu.Game.Tournament.Tests/NonVisual/TournamentHostTest.cs new file mode 100644 index 0000000000..b14684200f --- /dev/null +++ b/osu.Game.Tournament.Tests/NonVisual/TournamentHostTest.cs @@ -0,0 +1,33 @@ +// 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.Threading; +using System.Threading.Tasks; +using NUnit.Framework; +using osu.Framework.Platform; + +namespace osu.Game.Tournament.Tests.NonVisual +{ + public abstract class TournamentHostTest + { + public static TournamentGameBase LoadTournament(GameHost host, TournamentGameBase tournament = null) + { + tournament ??= new TournamentGameBase(); + Task.Run(() => host.Run(tournament)) + .ContinueWith(t => Assert.Fail($"Host threw exception {t.Exception}"), TaskContinuationOptions.OnlyOnFaulted); + WaitForOrAssert(() => tournament.IsLoaded, @"osu! failed to start in a reasonable amount of time"); + return tournament; + } + + public static void WaitForOrAssert(Func result, string failureMessage, int timeout = 90000) + { + Task task = Task.Run(() => + { + while (!result()) Thread.Sleep(200); + }); + + Assert.IsTrue(task.Wait(timeout), failureMessage); + } + } +} From 0b8ca667a94c5c5d962bbd66726e464deca21422 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 01:53:25 +0900 Subject: [PATCH 0707/2442] Add failing test coverage of loading with an unavailable ruleset --- .../NonVisual/DataLoadTest.cs | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 osu.Game.Tournament.Tests/NonVisual/DataLoadTest.cs diff --git a/osu.Game.Tournament.Tests/NonVisual/DataLoadTest.cs b/osu.Game.Tournament.Tests/NonVisual/DataLoadTest.cs new file mode 100644 index 0000000000..692cb3870c --- /dev/null +++ b/osu.Game.Tournament.Tests/NonVisual/DataLoadTest.cs @@ -0,0 +1,45 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.IO; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Platform; +using osu.Game.Rulesets; +using osu.Game.Tests; + +namespace osu.Game.Tournament.Tests.NonVisual +{ + public class DataLoadTest : TournamentHostTest + { + [Test] + public void TestUnavailableRuleset() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestUnavailableRuleset))) + { + try + { + var osu = new TestTournament(); + + LoadTournament(host, osu); + var storage = osu.Dependencies.Get(); + + Assert.That(storage.GetFullPath("."), Is.EqualTo(Path.Combine(host.Storage.GetFullPath("."), "tournaments", "default"))); + } + finally + { + host.Exit(); + } + } + } + + public class TestTournament : TournamentGameBase + { + [BackgroundDependencyLoader] + private void load() + { + Ruleset.Value = new RulesetInfo(); // not available + } + } + } +} From 46c72334fbd3d8427a92960cdffbc83d260439fb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 01:54:30 +0900 Subject: [PATCH 0708/2442] Fix stack overflow in ruleset change rejection logic --- 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 a7fac24351..f2d575550a 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -479,7 +479,7 @@ namespace osu.Game if (r.NewValue?.Available != true) { // reject the change if the ruleset is not available. - Ruleset.Value = r.OldValue; + Ruleset.Value = r.OldValue?.Available == true ? r.OldValue : RulesetStore.AvailableRulesets.First(); return; } From ceec74aacaa709e907b64c59833f999981ab28aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 02:00:07 +0900 Subject: [PATCH 0709/2442] Avoid throwing / logging an error when `drawings.txt` is missing --- .../Screens/Drawings/Components/StorageBackedTeamList.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs b/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs index f96ec01cbb..5d035a4028 100644 --- a/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/StorageBackedTeamList.cs @@ -27,6 +27,9 @@ namespace osu.Game.Tournament.Screens.Drawings.Components { var teams = new List(); + if (!storage.Exists(teams_filename)) + return teams; + try { using (Stream stream = storage.GetStream(teams_filename, FileAccess.Read, FileMode.Open)) From 77b354bfba5ad6ecf4675a359a8a6b45359b16a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 02:12:03 +0900 Subject: [PATCH 0710/2442] Resolve ruleset from store after loading tournament ladder --- osu.Game.Tournament/TournamentGameBase.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 92eb7ac713..531da00faf 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -66,7 +66,9 @@ namespace osu.Game.Tournament } ladder ??= new LadderInfo(); - ladder.Ruleset.Value ??= RulesetStore.AvailableRulesets.First(); + + ladder.Ruleset.Value = RulesetStore.GetRuleset(ladder.Ruleset.Value?.ShortName) + ?? RulesetStore.AvailableRulesets.First(); bool addedInfo = false; From 59a33b5d028fb688bb86956abe1e3eeac83a59c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 27 Jul 2021 18:46:49 +0200 Subject: [PATCH 0711/2442] Uncouple display logic from text in rankings overlay tables --- .../Rankings/Tables/CountriesTable.cs | 14 ++++---- .../Rankings/Tables/PerformanceTable.cs | 4 +-- .../Overlays/Rankings/Tables/RankingsTable.cs | 32 ++++++++++++------- .../Overlays/Rankings/Tables/ScoresTable.cs | 8 ++--- .../Rankings/Tables/UserBasedTable.cs | 32 +++++++++++-------- 5 files changed, 50 insertions(+), 40 deletions(-) diff --git a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs index c5e413c7fa..b28bf07adc 100644 --- a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs @@ -19,14 +19,14 @@ namespace osu.Game.Overlays.Rankings.Tables { } - protected override TableColumn[] CreateAdditionalHeaders() => new[] + protected override RankingsTableColumn[] CreateAdditionalHeaders() => new[] { - new TableColumn("Active Users", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("Ranked Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("Avg. Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("Performance", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("Avg. Perf.", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn("Active Users", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn("Ranked Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn("Avg. Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn("Performance", Anchor.Centre, new Dimension(GridSizeMode.AutoSize), true), + new RankingsTableColumn("Avg. Perf.", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), }; protected override Country GetCountry(CountryStatistics item) => item.Country; diff --git a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs index 1e6b2307e0..ddf4bdcfeb 100644 --- a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs @@ -15,9 +15,9 @@ namespace osu.Game.Overlays.Rankings.Tables { } - protected override TableColumn[] CreateUniqueHeaders() => new[] + protected override RankingsTableColumn[] CreateUniqueHeaders() => new[] { - new TableColumn("Performance", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn("Performance", Anchor.Centre, new Dimension(GridSizeMode.AutoSize), true), }; protected override Drawable[] CreateUniqueContent(UserStatistics item) => new Drawable[] diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index f568aae59c..0cdff0a77f 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -55,29 +55,24 @@ namespace osu.Game.Overlays.Rankings.Tables rankings.ForEach(_ => backgroundFlow.Add(new TableRowBackground { Height = row_height })); - Columns = mainHeaders.Concat(CreateAdditionalHeaders()).ToArray(); + Columns = mainHeaders.Concat(CreateAdditionalHeaders()).Cast().ToArray(); Content = rankings.Select((s, i) => createContent((page - 1) * items_per_page + i, s)).ToArray().ToRectangular(); } private Drawable[] createContent(int index, TModel item) => new Drawable[] { createIndexDrawable(index), createMainContent(item) }.Concat(CreateAdditionalContent(item)).ToArray(); - private static TableColumn[] mainHeaders => new[] + private static RankingsTableColumn[] mainHeaders => new[] { - new TableColumn(string.Empty, Anchor.Centre, new Dimension(GridSizeMode.Absolute, 40)), // place - new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension()), // flag and username (country name) + new RankingsTableColumn(string.Empty, Anchor.Centre, new Dimension(GridSizeMode.Absolute, 40)), // place + new RankingsTableColumn(string.Empty, Anchor.CentreLeft, new Dimension()), // flag and username (country name) }; - protected abstract TableColumn[] CreateAdditionalHeaders(); + protected abstract RankingsTableColumn[] CreateAdditionalHeaders(); protected abstract Drawable[] CreateAdditionalContent(TModel item); - protected virtual string HighlightedColumn => @"Performance"; - - protected override Drawable CreateHeader(int index, TableColumn column) - { - var title = column?.Header ?? default; - return new HeaderText(title, title == HighlightedColumn); - } + protected sealed override Drawable CreateHeader(int index, TableColumn column) + => (column as RankingsTableColumn)?.CreateHeaderText() ?? new HeaderText(column?.Header ?? default, false); protected abstract Country GetCountry(TModel item); @@ -106,6 +101,19 @@ namespace osu.Game.Overlays.Rankings.Tables } }; + protected class RankingsTableColumn : TableColumn + { + protected readonly bool Highlighted; + + public RankingsTableColumn(LocalisableString? header = null, Anchor anchor = Anchor.TopLeft, Dimension dimension = null, bool highlighted = false) + : base(header, anchor, dimension) + { + Highlighted = highlighted; + } + + public virtual HeaderText CreateHeaderText() => new HeaderText(Header, Highlighted); + } + protected class HeaderText : OsuSpriteText { private readonly bool isHighlighted; diff --git a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs index 9fae8e1897..508ad51112 100644 --- a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs @@ -15,10 +15,10 @@ namespace osu.Game.Overlays.Rankings.Tables { } - protected override TableColumn[] CreateUniqueHeaders() => new[] + protected override RankingsTableColumn[] CreateUniqueHeaders() => new[] { - new TableColumn("Total Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("Ranked Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)) + new RankingsTableColumn("Total Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn("Ranked Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize), true) }; protected override Drawable[] CreateUniqueContent(UserStatistics item) => new Drawable[] @@ -32,7 +32,5 @@ namespace osu.Game.Overlays.Rankings.Tables Text = $@"{item.RankedScore:N0}", } }; - - protected override string HighlightedColumn => @"Ranked Score"; } } diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs index 56814244c0..131ee86d44 100644 --- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -22,20 +22,14 @@ namespace osu.Game.Overlays.Rankings.Tables protected virtual IEnumerable GradeColumns => new List { "SS", "S", "A" }; - protected override TableColumn[] CreateAdditionalHeaders() => new[] + protected override RankingsTableColumn[] CreateAdditionalHeaders() => new[] { - new TableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), }.Concat(CreateUniqueHeaders()) - .Concat(GradeColumns.Select(grade => new TableColumn(grade, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)))) + .Concat(GradeColumns.Select(grade => new GradeTableColumn(grade, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)))) .ToArray(); - protected override Drawable CreateHeader(int index, TableColumn column) - { - var title = column?.Header ?? default; - return new UserTableHeaderText(title, HighlightedColumn == title, GradeColumns.Contains(title.ToString())); - } - protected sealed override Country GetCountry(UserStatistics item) => item.User.Country; protected sealed override Drawable CreateFlagContent(UserStatistics item) @@ -61,19 +55,29 @@ namespace osu.Game.Overlays.Rankings.Tables new ColoredRowText { Text = $@"{item.GradesCount[ScoreRank.A]:N0}", } }).ToArray(); - protected abstract TableColumn[] CreateUniqueHeaders(); + protected abstract RankingsTableColumn[] CreateUniqueHeaders(); protected abstract Drawable[] CreateUniqueContent(UserStatistics item); - private class UserTableHeaderText : HeaderText + private class GradeTableColumn : RankingsTableColumn { - public UserTableHeaderText(LocalisableString text, bool isHighlighted, bool isGrade) + public GradeTableColumn(LocalisableString? header = null, Anchor anchor = Anchor.TopLeft, Dimension dimension = null, bool highlighted = false) + : base(header, anchor, dimension, highlighted) + { + } + + public override HeaderText CreateHeaderText() => new GradeHeaderText(Header, Highlighted); + } + + private class GradeHeaderText : HeaderText + { + public GradeHeaderText(LocalisableString text, bool isHighlighted) : base(text, isHighlighted) { Margin = new MarginPadding { // Grade columns have extra horizontal padding for readibility - Horizontal = isGrade ? 20 : 10, + Horizontal = 20, Vertical = 5 }; } From 0691c0dd63d26238396c602332fc12fd90c4688e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 20 Jun 2021 19:37:09 +0200 Subject: [PATCH 0712/2442] Switch `Colour{Display,Palette}` to use `Colour4` --- .../UserInterface/TestSceneLabelledColourPalette.cs | 10 +++++----- osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs | 8 +++----- osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs | 3 +-- .../Graphics/UserInterfaceV2/LabelledColourPalette.cs | 4 ++-- osu.Game/Screens/Edit/Setup/ColoursSection.cs | 3 ++- 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs index 826da17ca8..d7af47a835 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs @@ -53,15 +53,15 @@ namespace osu.Game.Tests.Visual.UserInterface component.Colours.AddRange(new[] { - Color4.DarkRed, - Color4.Aquamarine, - Color4.Goldenrod, - Color4.Gainsboro + Colour4.DarkRed, + Colour4.Aquamarine, + Colour4.Goldenrod, + Colour4.Gainsboro }); }); } - private Color4 randomColour() => new Color4( + private Colour4 randomColour() => new Color4( RNG.NextSingle(), RNG.NextSingle(), RNG.NextSingle(), diff --git a/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs b/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs index 01d91f7cfd..dfdc76687f 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs @@ -3,7 +3,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -11,22 +10,21 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; using osu.Game.Graphics.Sprites; using osuTK; -using osuTK.Graphics; namespace osu.Game.Graphics.UserInterfaceV2 { /// /// A component which displays a colour along with related description text. /// - public class ColourDisplay : CompositeDrawable, IHasCurrentValue + public class ColourDisplay : CompositeDrawable, IHasCurrentValue { - private readonly BindableWithCurrent current = new BindableWithCurrent(); + private readonly BindableWithCurrent current = new BindableWithCurrent(); private Box fill; private OsuSpriteText colourHexCode; private OsuSpriteText colourName; - public Bindable Current + public Bindable Current { get => current.Current; set => current.Current = value; diff --git a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs index ba950048dc..a32257ef79 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Sprites; using osuTK; -using osuTK.Graphics; namespace osu.Game.Graphics.UserInterfaceV2 { @@ -17,7 +16,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 /// public class ColourPalette : CompositeDrawable { - public BindableList Colours { get; } = new BindableList(); + public BindableList Colours { get; } = new BindableList(); private string colourNamePrefix = "Colour"; diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledColourPalette.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledColourPalette.cs index 58443953bc..8970ef1115 100644 --- a/osu.Game/Graphics/UserInterfaceV2/LabelledColourPalette.cs +++ b/osu.Game/Graphics/UserInterfaceV2/LabelledColourPalette.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; -using osuTK.Graphics; +using osu.Framework.Graphics; namespace osu.Game.Graphics.UserInterfaceV2 { @@ -13,7 +13,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 { } - public BindableList Colours => Component.Colours; + public BindableList Colours => Component.Colours; public string ColourNamePrefix { diff --git a/osu.Game/Screens/Edit/Setup/ColoursSection.cs b/osu.Game/Screens/Edit/Setup/ColoursSection.cs index 4a81959a54..d7e16645f2 100644 --- a/osu.Game/Screens/Edit/Setup/ColoursSection.cs +++ b/osu.Game/Screens/Edit/Setup/ColoursSection.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Localisation; @@ -32,7 +33,7 @@ namespace osu.Game.Screens.Edit.Setup var colours = Beatmap.BeatmapSkin?.GetConfig>(GlobalSkinColours.ComboColours)?.Value; if (colours != null) - comboColours.Colours.AddRange(colours); + comboColours.Colours.AddRange(colours.Select(c => (Colour4)c)); } } } From c8891d4504145b07e97b8f34b7bb9e5f68e912c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 20 Jun 2021 20:20:46 +0200 Subject: [PATCH 0713/2442] Integrate editor colour display with colour picker & popover --- .../Graphics/UserInterfaceV2/ColourDisplay.cs | 19 +++++++++++++++--- .../Graphics/UserInterfaceV2/ColourPalette.cs | 20 ++++++++++++++----- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs b/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs index dfdc76687f..25f89fdcaa 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs @@ -3,11 +3,14 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osuTK; @@ -16,7 +19,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 /// /// A component which displays a colour along with related description text. /// - public class ColourDisplay : CompositeDrawable, IHasCurrentValue + public class ColourDisplay : CompositeDrawable, IHasCurrentValue, IHasPopover { private readonly BindableWithCurrent current = new BindableWithCurrent(); @@ -60,10 +63,11 @@ namespace osu.Game.Graphics.UserInterfaceV2 Spacing = new Vector2(0, 10), Children = new Drawable[] { - new CircularContainer + new OsuClickableContainer { RelativeSizeAxes = Axes.X, Height = 100, + CornerRadius = 50, Masking = true, Children = new Drawable[] { @@ -77,7 +81,8 @@ namespace osu.Game.Graphics.UserInterfaceV2 Origin = Anchor.Centre, Font = OsuFont.Default.With(size: 12) } - } + }, + Action = this.ShowPopover }, colourName = new OsuSpriteText { @@ -101,5 +106,13 @@ namespace osu.Game.Graphics.UserInterfaceV2 colourHexCode.Text = current.Value.ToHex(); colourHexCode.Colour = OsuColour.ForegroundTextColourFor(current.Value); } + + public Popover GetPopover() => new OsuPopover(false) + { + Child = new OsuColourPicker + { + Current = { BindTarget = Current } + } + }; } } diff --git a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs index a32257ef79..d8edd00c16 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Specialized; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -72,14 +73,17 @@ namespace osu.Game.Graphics.UserInterfaceV2 { base.LoadComplete(); - Colours.BindCollectionChanged((_, __) => updatePalette(), true); + Colours.BindCollectionChanged((_, args) => updatePalette(args), true); FinishTransforms(true); } private const int fade_duration = 200; - private void updatePalette() + private void updatePalette(NotifyCollectionChangedEventArgs args) { + if (args.Action == NotifyCollectionChangedAction.Replace) + return; + palette.Clear(); if (Colours.Any()) @@ -93,12 +97,18 @@ namespace osu.Game.Graphics.UserInterfaceV2 placeholder.FadeIn(fade_duration, Easing.OutQuint); } - foreach (var item in Colours) + for (int i = 0; i < Colours.Count; ++i) { - palette.Add(new ColourDisplay + // copy to avoid accesses to modified closure. + int colourIndex = i; + ColourDisplay display; + + palette.Add(display = new ColourDisplay { - Current = { Value = item } + Current = { Value = Colours[colourIndex] } }); + + display.Current.BindValueChanged(colour => Colours[colourIndex] = colour.NewValue); } reindexItems(); From 3a347188a5f20e79b6bdaeb16d79cbcd432efcb0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 13:21:26 +0900 Subject: [PATCH 0714/2442] Allow `LinkFlowContainer` to still open external URLs when `OsuGame` is not available --- osu.Game/Graphics/Containers/LinkFlowContainer.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 5ff2fdf6b2..85ef779e48 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Localisation; +using osu.Framework.Platform; using osu.Game.Graphics.Sprites; using osu.Game.Users; @@ -25,6 +26,9 @@ namespace osu.Game.Graphics.Containers [Resolved(CanBeNull = true)] private OsuGame game { get; set; } + [Resolved] + private GameHost host { get; set; } + public void AddLinks(string text, List links) { if (string.IsNullOrEmpty(text) || links == null) @@ -91,8 +95,11 @@ namespace osu.Game.Graphics.Containers { if (action != null) action(); - else - game?.HandleLink(link); + else if (game != null) + game.HandleLink(link); + // fallback to handle cases where OsuGame is not available, ie. tournament client. + else if (link.Action == LinkAction.External) + host.OpenUrlExternally(link.Argument); }, }); } From 6249ce0ea3ebbd12718b3688a3d3dc9aff6bea43 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 13:21:50 +0900 Subject: [PATCH 0715/2442] Add a warning and link for more information on `drawings.txt` population --- .../Screens/Drawings/DrawingsScreen.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs index 4c3adeae76..d02e0ebf86 100644 --- a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs +++ b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs @@ -9,11 +9,13 @@ using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Tournament.Components; using osu.Game.Tournament.Models; using osu.Game.Tournament.Screens.Drawings.Components; @@ -51,6 +53,29 @@ namespace osu.Game.Tournament.Screens.Drawings if (!TeamList.Teams.Any()) { + LinkFlowContainer links; + + InternalChildren = new Drawable[] + { + new Box + { + Colour = Color4.Black, + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Height = 0.3f, + }, + new WarningBox("No drawings.txt file found. Please create one and restart the client."), + links = new LinkFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Y = 60, + AutoSizeAxes = Axes.Both + } + }; + + links.AddLink("Click for details on the file format", "https://osu.ppy.sh/wiki/en/Tournament_Drawings", t => t.Colour = Color4.White); return; } From 665bd3690aa67f3746e8bd0d5f0ddd2a05907ff4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 15:27:24 +0900 Subject: [PATCH 0716/2442] Add a few cases of missing `ConfigureAwait` calls in tests project --- .../Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs | 4 ++-- .../Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs index 42848ffc0c..7e7e5ebc45 100644 --- a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs @@ -168,8 +168,8 @@ namespace osu.Game.Tests.Online public override async Task Import(BeatmapSetInfo item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default) { - await AllowImport.Task; - return await (CurrentImportTask = base.Import(item, archive, lowPriority, cancellationToken)); + await AllowImport.Task.ConfigureAwait(false); + return await (CurrentImportTask = base.Import(item, archive, lowPriority, cancellationToken)).ConfigureAwait(false); } } diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs index 449401c0bf..66ac700c51 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -143,9 +143,9 @@ namespace osu.Game.Tests.Visual.SongSelect public override async Task GetDifficultyAsync(BeatmapInfo beatmapInfo, RulesetInfo rulesetInfo = null, IEnumerable mods = null, CancellationToken cancellationToken = default) { if (blockCalculation) - await calculationBlocker.Task; + await calculationBlocker.Task.ConfigureAwait(false); - return await base.GetDifficultyAsync(beatmapInfo, rulesetInfo, mods, cancellationToken); + return await base.GetDifficultyAsync(beatmapInfo, rulesetInfo, mods, cancellationToken).ConfigureAwait(false); } } } From 451c65a2c894e05f57b03a593fc3d8ccfc740828 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Thu, 29 Jul 2021 23:41:01 -0700 Subject: [PATCH 0717/2442] Fix song progress graph not being correctly hidden --- osu.Game/Screens/Play/SongProgress.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index bd861dc598..f28622f42e 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -178,7 +178,7 @@ namespace osu.Game.Screens.Play float barHeight = bottom_bar_height + handle_size.Y; bar.ResizeHeightTo(ShowGraph.Value ? barHeight + graph_height : barHeight, transition_duration, Easing.In); - graph.MoveToY(ShowGraph.Value ? 0 : bottom_bar_height + graph_height, transition_duration, Easing.In); + graph.FadeTo(ShowGraph.Value ? 1 : 0, transition_duration, Easing.In); updateInfoMargin(); } From 4e2f928d65ab6d962cc8026ade8473dbad4c206f Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 30 Jul 2021 15:44:09 +0900 Subject: [PATCH 0718/2442] Fix comment --- 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 e99d33cd1b..9422d6d51b 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Catch.UI if (Time.Elapsed <= 0) { // This is probably a wrong value, but currently the true value is not recorded. - // Setting `true` will prevent generation of false-positive after-images (with more false-positives). + // Setting `true` will prevent generation of false-positive after-images (with more false-negatives). lastHyperDashState = true; return; } From a2f3edbfc0cfda19ce03fa388ab0436c96d43904 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 15:49:11 +0900 Subject: [PATCH 0719/2442] Fade track volume out as combo increases --- osu.Game/Rulesets/Mods/ModMuted.cs | 48 ++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index 9e69bc1386..a39d798bc4 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -1,14 +1,18 @@ // 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.Linq; using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Bindables; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Configuration; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; +using osu.Game.Scoring; namespace osu.Game.Rulesets.Mods { @@ -22,10 +26,15 @@ namespace osu.Game.Rulesets.Mods public override double ScoreMultiplier => 1; } - public abstract class ModMuted : ModMuted, IApplicableToDrawableRuleset, IApplicableToTrack + public abstract class ModMuted : ModMuted, IApplicableToDrawableRuleset, IApplicableToTrack, IApplicableToScoreProcessor where TObject : HitObject { - private readonly BindableNumber volumeAdjust = new BindableDouble(); + private readonly BindableNumber trackVolumeAdjust = new BindableDouble(0.5); + private readonly BindableNumber metronomeVolumeAdjust = new BindableDouble(0.5); + + private BindableNumber currentCombo; + + private AudioContainer metronomeContainer; [SettingSource("Enable metronome", "Add a metronome to help you keep track of the rhythm.")] public BindableBool EnableMetronome { get; } = new BindableBool @@ -34,15 +43,44 @@ namespace osu.Game.Rulesets.Mods Value = true }; + [SettingSource("Muted at combo", "The combo count at which point the music is completely muted.")] + public BindableInt MuteComboCount { get; } = new BindableInt + { + Default = 100, + Value = 100, + MinValue = 0, + MaxValue = 500, + }; + public void ApplyToTrack(ITrack track) { - track.AddAdjustment(AdjustableProperty.Volume, volumeAdjust); + track.AddAdjustment(AdjustableProperty.Volume, trackVolumeAdjust); } public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { - if (EnableMetronome.Value) - drawableRuleset.Overlays.Add(new Metronome(drawableRuleset.Beatmap.HitObjects.First().StartTime)); + if (!EnableMetronome.Value) return; + + drawableRuleset.Overlays.Add(metronomeContainer = new AudioContainer + { + Child = new Metronome(drawableRuleset.Beatmap.HitObjects.First().StartTime) + }); + + metronomeContainer.AddAdjustment(AdjustableProperty.Volume, metronomeVolumeAdjust); } + + public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) + { + currentCombo = scoreProcessor.Combo.GetBoundCopy(); + currentCombo.BindValueChanged(combo => + { + double dimFactor = Math.Min(1, (double)combo.NewValue / MuteComboCount.Value); + + metronomeVolumeAdjust.Value = dimFactor; + trackVolumeAdjust.Value = 1 - dimFactor; + }, true); + } + + public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank; } } From b399ddaea047ce22092566f46685bc984f5bfcc3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 16:10:10 +0900 Subject: [PATCH 0720/2442] Add inverse setting --- osu.Game/Rulesets/Mods/ModMuted.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index a39d798bc4..5fc9ab1eda 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -52,6 +52,13 @@ namespace osu.Game.Rulesets.Mods MaxValue = 500, }; + [SettingSource("Start muted", "Increase volume as combo builds.")] + public BindableBool InverseMuting { get; } = new BindableBool + { + Default = false, + Value = false + }; + public void ApplyToTrack(ITrack track) { track.AddAdjustment(AdjustableProperty.Volume, trackVolumeAdjust); @@ -78,6 +85,8 @@ namespace osu.Game.Rulesets.Mods metronomeVolumeAdjust.Value = dimFactor; trackVolumeAdjust.Value = 1 - dimFactor; + if (InverseMuting.Value) + dimFactor = 1 - dimFactor; }, true); } From 3cfd235b7f53b14c63fadd21b4775ad865c971c9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 16:10:20 +0900 Subject: [PATCH 0721/2442] Add tween when missing to avoid sudden volume difference --- osu.Game/Rulesets/Mods/ModMuted.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index 5fc9ab1eda..469f3073d6 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Configuration; @@ -43,7 +44,7 @@ namespace osu.Game.Rulesets.Mods Value = true }; - [SettingSource("Muted at combo", "The combo count at which point the music is completely muted.")] + [SettingSource("Final volume at combo", "The combo count at which point the music reaches its final volume.")] public BindableInt MuteComboCount { get; } = new BindableInt { Default = 100, @@ -83,10 +84,19 @@ namespace osu.Game.Rulesets.Mods { double dimFactor = Math.Min(1, (double)combo.NewValue / MuteComboCount.Value); - metronomeVolumeAdjust.Value = dimFactor; - trackVolumeAdjust.Value = 1 - dimFactor; if (InverseMuting.Value) dimFactor = 1 - dimFactor; + + if (combo.NewValue < combo.OldValue) + { + scoreProcessor.TransformBindableTo(metronomeVolumeAdjust, dimFactor, 200, Easing.OutQuint); + scoreProcessor.TransformBindableTo(trackVolumeAdjust, 1 - dimFactor, 200, Easing.OutQuint); + } + else + { + metronomeVolumeAdjust.Value = dimFactor; + trackVolumeAdjust.Value = 1 - dimFactor; + } }, true); } From 0c3f1195e91329c4dce028797c085c2354f63657 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 16:47:07 +0900 Subject: [PATCH 0722/2442] Allow audio adjustments to be applied to `DrawableRuleset`s --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 43 +++++++++++++++---------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index daf46dcdcc..b3242a8b4b 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -1,29 +1,29 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Drawables; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using JetBrains.Annotations; +using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Input; using osu.Framework.Input.Events; +using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Graphics.Cursor; using osu.Game.Input.Handlers; using osu.Game.Overlays; using osu.Game.Replays; using osu.Game.Rulesets.Configuration; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play; @@ -98,6 +98,14 @@ namespace osu.Game.Rulesets.UI private DrawableRulesetDependencies dependencies; + /// + /// An audio container which can be used to apply adjustments to playfield content. + /// + /// + /// Does not affect . + /// + public AudioContainer AudioContainer { get; private set; } + /// /// Creates a ruleset visualisation for the provided ruleset and beatmap. /// @@ -155,21 +163,22 @@ namespace osu.Game.Rulesets.UI [BackgroundDependencyLoader] private void load(CancellationToken? cancellationToken) { - InternalChildren = new Drawable[] + InternalChild = frameStabilityContainer = new FrameStabilityContainer(GameplayStartTime) { - frameStabilityContainer = new FrameStabilityContainer(GameplayStartTime) + FrameStablePlayback = FrameStablePlayback, + Children = new Drawable[] { - FrameStablePlayback = FrameStablePlayback, - Children = new Drawable[] + FrameStableComponents, + AudioContainer = new AudioContainer { - FrameStableComponents, - KeyBindingInputManager + RelativeSizeAxes = Axes.Both, + Child = KeyBindingInputManager .WithChild(CreatePlayfieldAdjustmentContainer() .WithChild(Playfield) ), - Overlays, - } - }, + }, + Overlays, + } }; if ((ResumeOverlay = CreateResumeOverlay()) != null) From bdc5eb6d3dcd9a4e75e1cd301886cd163d4560ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 16:46:42 +0900 Subject: [PATCH 0723/2442] Add ability to also mute hitsounds --- osu.Game/Rulesets/Mods/ModMuted.cs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index 469f3073d6..e29c1166e4 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mods public abstract class ModMuted : ModMuted, IApplicableToDrawableRuleset, IApplicableToTrack, IApplicableToScoreProcessor where TObject : HitObject { - private readonly BindableNumber trackVolumeAdjust = new BindableDouble(0.5); + private readonly BindableNumber mainVolumeAdjust = new BindableDouble(0.5); private readonly BindableNumber metronomeVolumeAdjust = new BindableDouble(0.5); private BindableNumber currentCombo; @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Mods Value = true }; - [SettingSource("Final volume at combo", "The combo count at which point the music reaches its final volume.")] + [SettingSource("Final volume at combo", "The combo count at which point the track reaches its final volume.")] public BindableInt MuteComboCount { get; } = new BindableInt { Default = 100, @@ -60,9 +60,16 @@ namespace osu.Game.Rulesets.Mods Value = false }; + [SettingSource("Mute hit sounds", "Hit sounds are also muted alongside the track.")] + public BindableBool AffectsHitSounds { get; } = new BindableBool + { + Default = false, + Value = false + }; + public void ApplyToTrack(ITrack track) { - track.AddAdjustment(AdjustableProperty.Volume, trackVolumeAdjust); + track.AddAdjustment(AdjustableProperty.Volume, mainVolumeAdjust); } public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) @@ -75,6 +82,9 @@ namespace osu.Game.Rulesets.Mods }); metronomeContainer.AddAdjustment(AdjustableProperty.Volume, metronomeVolumeAdjust); + + if (AffectsHitSounds.Value) + drawableRuleset.AudioContainer.AddAdjustment(AdjustableProperty.Volume, mainVolumeAdjust); } public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) @@ -90,12 +100,12 @@ namespace osu.Game.Rulesets.Mods if (combo.NewValue < combo.OldValue) { scoreProcessor.TransformBindableTo(metronomeVolumeAdjust, dimFactor, 200, Easing.OutQuint); - scoreProcessor.TransformBindableTo(trackVolumeAdjust, 1 - dimFactor, 200, Easing.OutQuint); + scoreProcessor.TransformBindableTo(mainVolumeAdjust, 1 - dimFactor, 200, Easing.OutQuint); } else { metronomeVolumeAdjust.Value = dimFactor; - trackVolumeAdjust.Value = 1 - dimFactor; + mainVolumeAdjust.Value = 1 - dimFactor; } }, true); } From d5e68f53b5e005bf93d359f6e81521d17dbd15d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 17:38:04 +0900 Subject: [PATCH 0724/2442] Change some defaults and always tween --- osu.Game/Rulesets/Mods/ModMuted.cs | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index e29c1166e4..0ec45f4fbb 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mods private AudioContainer metronomeContainer; - [SettingSource("Enable metronome", "Add a metronome to help you keep track of the rhythm.")] + [SettingSource("Enable metronome", "Add a metronome beat to help you keep track of the rhythm.")] public BindableBool EnableMetronome { get; } = new BindableBool { Default = true, @@ -63,8 +63,8 @@ namespace osu.Game.Rulesets.Mods [SettingSource("Mute hit sounds", "Hit sounds are also muted alongside the track.")] public BindableBool AffectsHitSounds { get; } = new BindableBool { - Default = false, - Value = false + Default = true, + Value = true }; public void ApplyToTrack(ITrack track) @@ -97,16 +97,8 @@ namespace osu.Game.Rulesets.Mods if (InverseMuting.Value) dimFactor = 1 - dimFactor; - if (combo.NewValue < combo.OldValue) - { - scoreProcessor.TransformBindableTo(metronomeVolumeAdjust, dimFactor, 200, Easing.OutQuint); - scoreProcessor.TransformBindableTo(mainVolumeAdjust, 1 - dimFactor, 200, Easing.OutQuint); - } - else - { - metronomeVolumeAdjust.Value = dimFactor; - mainVolumeAdjust.Value = 1 - dimFactor; - } + scoreProcessor.TransformBindableTo(metronomeVolumeAdjust, dimFactor, 500, Easing.OutQuint); + scoreProcessor.TransformBindableTo(mainVolumeAdjust, 1 - dimFactor, 500, Easing.OutQuint); }, true); } From 185ea776f5e1f88855fbde040b27848bfc6e82c4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 18:11:35 +0900 Subject: [PATCH 0725/2442] Fix incorrect authorisation loss exception handling with recent changes --- osu.Game/Online/API/APIAccess.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index eb3abff2b7..b35dfa11cb 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -150,7 +150,7 @@ namespace osu.Game.Online.API userReq.Failure += ex => { - if (ex.InnerException is WebException webException && webException.Message == @"Unauthorized") + if (ex is WebException webException && webException.Message == @"Unauthorized") { log.Add(@"Login no longer valid"); Logout(); From cd516c4ac7644689e9fdd8b8f3758ed06a9547de Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 19:38:43 +0900 Subject: [PATCH 0726/2442] Fix regressed metronome handling --- osu.Game/Rulesets/Mods/ModMuted.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index 0ec45f4fbb..5454483571 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -74,14 +74,15 @@ namespace osu.Game.Rulesets.Mods public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { - if (!EnableMetronome.Value) return; - - drawableRuleset.Overlays.Add(metronomeContainer = new AudioContainer + if (EnableMetronome.Value) { - Child = new Metronome(drawableRuleset.Beatmap.HitObjects.First().StartTime) - }); + drawableRuleset.Overlays.Add(metronomeContainer = new AudioContainer + { + Child = new Metronome(drawableRuleset.Beatmap.HitObjects.First().StartTime) + }); - metronomeContainer.AddAdjustment(AdjustableProperty.Volume, metronomeVolumeAdjust); + metronomeContainer.AddAdjustment(AdjustableProperty.Volume, metronomeVolumeAdjust); + } if (AffectsHitSounds.Value) drawableRuleset.AudioContainer.AddAdjustment(AdjustableProperty.Volume, mainVolumeAdjust); From fcfa6d5bd10773ae4a5533de6166b4b95c4b7a21 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 30 Jul 2021 14:17:43 +0200 Subject: [PATCH 0727/2442] Localise rankings overlay header. --- .../Rankings/RankingsOverlayHeader.cs | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs index 92e22f5873..4edd8ab1a7 100644 --- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs @@ -5,6 +5,9 @@ using osu.Framework.Graphics; using osu.Framework.Bindables; using osu.Game.Rulesets; using osu.Game.Users; +using osu.Game.Resources.Localisation.Web; +using osu.Framework.Localisation; +using System; namespace osu.Game.Overlays.Rankings { @@ -29,13 +32,14 @@ namespace osu.Game.Overlays.Rankings { public RankingsTitle() { - Title = "ranking"; + Title = PageTitleStrings.MainRankingControllerDefault; Description = "find out who's the best right now"; IconTexture = "Icons/Hexacons/rankings"; } } } + [LocalisableEnum(typeof(RankingsScopeEnumLocalisationMapper))] public enum RankingsScope { Performance, @@ -43,4 +47,28 @@ namespace osu.Game.Overlays.Rankings Score, Country } + + public class RankingsScopeEnumLocalisationMapper : EnumLocalisationMapper + { + public override LocalisableString Map(RankingsScope value) + { + switch (value) + { + case RankingsScope.Performance: + return RankingsStrings.TypePerformance; + + case RankingsScope.Spotlights: + return RankingsStrings.TypeCharts; + + case RankingsScope.Score: + return RankingsStrings.TypeScore; + + case RankingsScope.Country: + return RankingsStrings.TypeCountry; + + default: + throw new ArgumentOutOfRangeException(nameof(value), value, null); + } + } + } } From 9515a67f57ff5ae73100c75c6a4ce042fa047449 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 30 Jul 2021 14:35:25 +0200 Subject: [PATCH 0728/2442] Localise ranking sort tab control. --- .../Rankings/RankingsSortTabControl.cs | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Rankings/RankingsSortTabControl.cs b/osu.Game/Overlays/Rankings/RankingsSortTabControl.cs index c0bbf46e30..782055181f 100644 --- a/osu.Game/Overlays/Rankings/RankingsSortTabControl.cs +++ b/osu.Game/Overlays/Rankings/RankingsSortTabControl.cs @@ -1,19 +1,42 @@ // 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 osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; + namespace osu.Game.Overlays.Rankings { public class RankingsSortTabControl : OverlaySortTabControl { public RankingsSortTabControl() { - Title = "Show"; + Title = RankingsStrings.FilterTitle; } } + [LocalisableEnum(typeof(RankingsSortCriteriaEnumLocalisationMapper))] public enum RankingsSortCriteria { All, Friends } + + public class RankingsSortCriteriaEnumLocalisationMapper : EnumLocalisationMapper + { + public override LocalisableString Map(RankingsSortCriteria value) + { + switch (value) + { + case RankingsSortCriteria.All: + return SortStrings.All; + + case RankingsSortCriteria.Friends: + return SortStrings.Friends; + + default: + throw new ArgumentOutOfRangeException(nameof(value), value, null); + } + } + } } From be3c02ff7ffef7454e7246b587f86af3e5b39ddc Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 29 Jul 2021 20:29:11 +0900 Subject: [PATCH 0729/2442] Remove 'Soft' select sample variant usage (soft is the new default) --- osu.Game/Graphics/UserInterface/HoverSampleSet.cs | 2 -- .../Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs | 2 +- osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs | 2 +- osu.Game/Screens/Edit/EditorTable.cs | 1 - 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/HoverSampleSet.cs b/osu.Game/Graphics/UserInterface/HoverSampleSet.cs index b4afb4831f..e7b8625175 100644 --- a/osu.Game/Graphics/UserInterface/HoverSampleSet.cs +++ b/osu.Game/Graphics/UserInterface/HoverSampleSet.cs @@ -10,8 +10,6 @@ namespace osu.Game.Graphics.UserInterface [Description("default")] Default, - [Description("soft")] - Soft, [Description("button")] Button, diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs index 794c728e56..618c7dabfa 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorDirectory.cs @@ -32,7 +32,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 { Depth = 1 }, - new HoverClickSounds(HoverSampleSet.Soft) + new HoverClickSounds() }); } diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs b/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs index e4c78e723d..3d09d09833 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs @@ -57,7 +57,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 { Depth = 1 }, - new HoverClickSounds(HoverSampleSet.Soft) + new HoverClickSounds() }); } diff --git a/osu.Game/Screens/Edit/EditorTable.cs b/osu.Game/Screens/Edit/EditorTable.cs index 756339405c..d0a83e94a1 100644 --- a/osu.Game/Screens/Edit/EditorTable.cs +++ b/osu.Game/Screens/Edit/EditorTable.cs @@ -67,7 +67,6 @@ namespace osu.Game.Screens.Edit private EditorClock clock { get; set; } public RowBackground(object item) - : base(HoverSampleSet.Soft) { Item = item; From c1d8a7e2ad6b3d7aeb5794048c55dd4e8f04566f Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 30 Jul 2021 21:23:49 +0900 Subject: [PATCH 0730/2442] Add and use 'Submit' select sample variant for particular components --- osu.Game/Graphics/UserInterface/DialogButton.cs | 1 + osu.Game/Graphics/UserInterface/HoverSampleSet.cs | 2 ++ osu.Game/Online/Chat/DrawableLinkCompiler.cs | 1 + osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs | 1 + osu.Game/Overlays/News/NewsCard.cs | 2 ++ .../Overlays/Profile/Sections/BeatmapMetadataContainer.cs | 2 ++ osu.Game/Screens/Edit/EditorTable.cs | 1 - osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs | 2 ++ osu.Game/Users/Drawables/ClickableAvatar.cs | 6 ++++++ osu.Game/Users/UserPanel.cs | 1 + 10 files changed, 18 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs index 2d75dad828..2f9e4dae51 100644 --- a/osu.Game/Graphics/UserInterface/DialogButton.cs +++ b/osu.Game/Graphics/UserInterface/DialogButton.cs @@ -56,6 +56,7 @@ namespace osu.Game.Graphics.UserInterface private Vector2 hoverSpacing => new Vector2(3f, 0f); public DialogButton() + : base(HoverSampleSet.Submit) { RelativeSizeAxes = Axes.X; diff --git a/osu.Game/Graphics/UserInterface/HoverSampleSet.cs b/osu.Game/Graphics/UserInterface/HoverSampleSet.cs index e7b8625175..a5ea6fcfbf 100644 --- a/osu.Game/Graphics/UserInterface/HoverSampleSet.cs +++ b/osu.Game/Graphics/UserInterface/HoverSampleSet.cs @@ -10,6 +10,8 @@ namespace osu.Game.Graphics.UserInterface [Description("default")] Default, + [Description("submit")] + Submit, [Description("button")] Button, diff --git a/osu.Game/Online/Chat/DrawableLinkCompiler.cs b/osu.Game/Online/Chat/DrawableLinkCompiler.cs index 53ea1d6f99..4df60eba69 100644 --- a/osu.Game/Online/Chat/DrawableLinkCompiler.cs +++ b/osu.Game/Online/Chat/DrawableLinkCompiler.cs @@ -31,6 +31,7 @@ namespace osu.Game.Online.Chat protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new LinkHoverSounds(sampleSet, Parts); public DrawableLinkCompiler(IEnumerable parts) + : base(HoverSampleSet.Submit) { Parts = parts.ToList(); } diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs index afb5eeda36..9ff39ce1dd 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs @@ -50,6 +50,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels protected Action ViewBeatmap; protected BeatmapPanel(BeatmapSetInfo setInfo) + : base(HoverSampleSet.Submit) { Debug.Assert(setInfo.OnlineBeatmapSetID != null); diff --git a/osu.Game/Overlays/News/NewsCard.cs b/osu.Game/Overlays/News/NewsCard.cs index 599b45fa78..cc2fa7e1e1 100644 --- a/osu.Game/Overlays/News/NewsCard.cs +++ b/osu.Game/Overlays/News/NewsCard.cs @@ -14,6 +14,7 @@ using osu.Framework.Platform; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.News @@ -28,6 +29,7 @@ namespace osu.Game.Overlays.News private TextFlowContainer main; public NewsCard(APINewsPost post) + : base(HoverSampleSet.Submit) { this.post = post; diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs index 67a976fe6f..a8a4cfc365 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Profile.Sections { @@ -17,6 +18,7 @@ namespace osu.Game.Overlays.Profile.Sections private readonly BeatmapInfo beatmap; protected BeatmapMetadataContainer(BeatmapInfo beatmap) + : base(HoverSampleSet.Submit) { this.beatmap = beatmap; diff --git a/osu.Game/Screens/Edit/EditorTable.cs b/osu.Game/Screens/Edit/EditorTable.cs index d0a83e94a1..ab8bd6a3bc 100644 --- a/osu.Game/Screens/Edit/EditorTable.cs +++ b/osu.Game/Screens/Edit/EditorTable.cs @@ -11,7 +11,6 @@ using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osuTK.Graphics; diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs index 845c0a914e..6ecb96f723 100644 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs @@ -14,6 +14,7 @@ using osu.Game.Graphics.Sprites; using osuTK; using osuTK.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Screens.Select.Options { @@ -76,6 +77,7 @@ namespace osu.Game.Screens.Select.Options public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => box.ReceivePositionalInputAt(screenSpacePos); public BeatmapOptionsButton() + : base(HoverSampleSet.Submit) { Width = width; RelativeSizeAxes = Axes.Y; diff --git a/osu.Game/Users/Drawables/ClickableAvatar.cs b/osu.Game/Users/Drawables/ClickableAvatar.cs index f73489ac61..c8af8d80e4 100644 --- a/osu.Game/Users/Drawables/ClickableAvatar.cs +++ b/osu.Game/Users/Drawables/ClickableAvatar.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Users.Drawables { @@ -71,6 +72,11 @@ namespace osu.Game.Users.Drawables { private LocalisableString tooltip = default_tooltip_text; + public ClickableArea() + : base(HoverSampleSet.Submit) + { + } + public override LocalisableString TooltipText { get => Enabled.Value ? tooltip : default; diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index 0981136dba..ff0d03a036 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -31,6 +31,7 @@ namespace osu.Game.Users protected Drawable Background { get; private set; } protected UserPanel(User user) + : base(HoverSampleSet.Submit) { if (user == null) throw new ArgumentNullException(nameof(user)); From 7dc1de74233897eb49f2a0c76b7d8e1042aa598d Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 30 Jul 2021 21:25:44 +0900 Subject: [PATCH 0731/2442] Use 'Submit' select sample variant for back button --- osu.Game/Graphics/UserInterface/BackButton.cs | 2 +- osu.Game/Graphics/UserInterface/TwoLayerButton.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/BackButton.cs b/osu.Game/Graphics/UserInterface/BackButton.cs index b941e5fcbd..b8196c6360 100644 --- a/osu.Game/Graphics/UserInterface/BackButton.cs +++ b/osu.Game/Graphics/UserInterface/BackButton.cs @@ -20,7 +20,7 @@ namespace osu.Game.Graphics.UserInterface { Size = TwoLayerButton.SIZE_EXTENDED; - Child = button = new TwoLayerButton + Child = button = new TwoLayerButton(HoverSampleSet.Submit) { Anchor = Anchor.TopLeft, Origin = Anchor.TopLeft, diff --git a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs index 8f03c7073c..969309bc79 100644 --- a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs +++ b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs @@ -71,7 +71,8 @@ namespace osu.Game.Graphics.UserInterface } } - public TwoLayerButton() + public TwoLayerButton(HoverSampleSet sampleSet = HoverSampleSet.Default) + : base(sampleSet) { Size = SIZE_RETRACTED; Shear = shear; From 9b7bb3724444930716569814d73aa8d9124a3e16 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 30 Jul 2021 21:32:08 +0900 Subject: [PATCH 0732/2442] Add hover+select sounds to some components that are missing them --- .../Containers/OsuRearrangeableListItem.cs | 47 ++++++++++--------- .../UserInterface/ExternalLinkButton.cs | 16 +++++-- osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs | 19 +++++++- .../Chat/Tabs/PrivateChannelTabItem.cs | 7 ++- .../Settings/Sections/Input/KeyBindingRow.cs | 4 +- osu.Game/Users/Drawables/UpdateableFlag.cs | 11 ++++- 6 files changed, 70 insertions(+), 34 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuRearrangeableListItem.cs b/osu.Game/Graphics/Containers/OsuRearrangeableListItem.cs index 911d47704a..d43c3a608b 100644 --- a/osu.Game/Graphics/Containers/OsuRearrangeableListItem.cs +++ b/osu.Game/Graphics/Containers/OsuRearrangeableListItem.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; +using osu.Game.Graphics.UserInterface; using osuTK; using osuTK.Graphics; @@ -59,33 +60,37 @@ namespace osu.Game.Graphics.Containers [BackgroundDependencyLoader] private void load() { - InternalChild = new GridContainer + InternalChildren = new Drawable[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Content = new[] + new GridContainer { - new[] + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Content = new[] { - handleContainer = new Container + new[] { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 5 }, - Child = handle = new PlaylistItemHandle + handleContainer = new Container { - Size = new Vector2(12), - Colour = HandleColour, - AlwaysPresent = true, - Alpha = 0 - } - }, - CreateContent() - } + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = 5 }, + Child = handle = new PlaylistItemHandle + { + Size = new Vector2(12), + Colour = HandleColour, + AlwaysPresent = true, + Alpha = 0 + } + }, + CreateContent() + } + }, + ColumnDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }, + RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) } }, - ColumnDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }, - RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) } + new HoverClickSounds() }; } diff --git a/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs b/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs index 6ad88eaaba..0df69a5b54 100644 --- a/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs +++ b/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs @@ -23,14 +23,20 @@ namespace osu.Game.Graphics.UserInterface [Resolved] private GameHost host { get; set; } + private readonly SpriteIcon linkIcon; + public ExternalLinkButton(string link = null) { Link = link; Size = new Vector2(12); - InternalChild = new SpriteIcon + InternalChildren = new Drawable[] { - Icon = FontAwesome.Solid.ExternalLinkAlt, - RelativeSizeAxes = Axes.Both + linkIcon = new SpriteIcon + { + Icon = FontAwesome.Solid.ExternalLinkAlt, + RelativeSizeAxes = Axes.Both + }, + new HoverClickSounds(HoverSampleSet.Submit) }; } @@ -42,13 +48,13 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnHover(HoverEvent e) { - InternalChild.FadeColour(hoverColour, 500, Easing.OutQuint); + linkIcon.FadeColour(hoverColour, 500, Easing.OutQuint); return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { - InternalChild.FadeColour(Color4.White, 500, Easing.OutQuint); + linkIcon.FadeColour(Color4.White, 500, Easing.OutQuint); base.OnHoverLost(e); } diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs index cca4dc33e5..58b402c164 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs @@ -3,6 +3,9 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -13,6 +16,7 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.Chat; using osuTK; using osuTK.Graphics; @@ -39,6 +43,8 @@ namespace osu.Game.Overlays.Chat.Tabs protected override Container Content => content; + private Sample sampleTabSwitched; + public ChannelTabItem(Channel value) : base(value) { @@ -112,6 +118,7 @@ namespace osu.Game.Overlays.Chat.Tabs }, }, }, + new HoverSounds() }; } @@ -152,11 +159,12 @@ namespace osu.Game.Overlays.Chat.Tabs } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, AudioManager audio) { BackgroundActive = colours.ChatBlue; BackgroundInactive = colours.Gray4; backgroundHover = colours.Gray7; + sampleTabSwitched = audio.Samples.Get($@"UI/{HoverSampleSet.Default.GetDescription()}-select"); highlightBox.Colour = colours.Yellow; } @@ -217,7 +225,14 @@ namespace osu.Game.Overlays.Chat.Tabs Text.Font = Text.Font.With(weight: FontWeight.Medium); } - protected override void OnActivated() => updateState(); + protected override void OnActivated() + { + if (IsLoaded) + sampleTabSwitched?.Play(); + + updateState(); + } + protected override void OnDeactivated() => updateState(); } } diff --git a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs index 7c82420e08..d01aec630e 100644 --- a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs @@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Chat.Tabs if (value.Type != ChannelType.PM) throw new ArgumentException("Argument value needs to have the targettype user!"); - ClickableAvatar avatar; + DrawableAvatar avatar; AddRange(new Drawable[] { @@ -48,10 +48,9 @@ namespace osu.Game.Overlays.Chat.Tabs Anchor = Anchor.Centre, Origin = Anchor.Centre, Masking = true, - Child = new DelayedLoadWrapper(avatar = new ClickableAvatar(value.Users.First()) + Child = new DelayedLoadWrapper(avatar = new DrawableAvatar(value.Users.First()) { - RelativeSizeAxes = Axes.Both, - OpenOnClick = false, + RelativeSizeAxes = Axes.Both }) { RelativeSizeAxes = Axes.Both, diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs index 4f7deebb5b..6e018597be 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs @@ -138,7 +138,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input }, } } - } + }, + new HoverClickSounds() }; foreach (var b in bindings) @@ -458,6 +459,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input Origin = Anchor.Centre, Text = keyBinding.KeyCombination.ReadableString(), }, + new HoverSounds() }; } diff --git a/osu.Game/Users/Drawables/UpdateableFlag.cs b/osu.Game/Users/Drawables/UpdateableFlag.cs index 1d30720889..7db834bf83 100644 --- a/osu.Game/Users/Drawables/UpdateableFlag.cs +++ b/osu.Game/Users/Drawables/UpdateableFlag.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; +using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; namespace osu.Game.Users.Drawables @@ -32,9 +33,17 @@ namespace osu.Game.Users.Drawables if (country == null && !ShowPlaceholderOnNull) return null; - return new DrawableFlag(country) + return new Container { RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new DrawableFlag(country) + { + RelativeSizeAxes = Axes.Both + }, + new HoverClickSounds(HoverSampleSet.Submit) + } }; } From 9538c4c7f29d2d9692fb7f4259b19b1fcfb95507 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 30 Jul 2021 21:35:07 +0900 Subject: [PATCH 0733/2442] Make the news collapsable month sections sound like dropdowns, because they pseudo kinda are --- .../Overlays/News/Sidebar/MonthSection.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/osu.Game/Overlays/News/Sidebar/MonthSection.cs b/osu.Game/Overlays/News/Sidebar/MonthSection.cs index b300a755f9..cd6ab224a9 100644 --- a/osu.Game/Overlays/News/Sidebar/MonthSection.cs +++ b/osu.Game/Overlays/News/Sidebar/MonthSection.cs @@ -15,13 +15,18 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; using System.Diagnostics; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Platform; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.News.Sidebar { public class MonthSection : CompositeDrawable { private const int animation_duration = 250; + private Sample sampleOpen; + private Sample sampleClose; public readonly BindableBool Expanded = new BindableBool(); @@ -51,6 +56,21 @@ namespace osu.Game.Overlays.News.Sidebar } } }; + + Expanded.ValueChanged += expanded => + { + if (expanded.NewValue) + sampleOpen?.Play(); + else + sampleClose?.Play(); + }; + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sampleOpen = audio.Samples.Get(@"UI/dropdown-open"); + sampleClose = audio.Samples.Get(@"UI/dropdown-close"); } private class DropdownHeader : OsuClickableContainer @@ -59,6 +79,8 @@ namespace osu.Game.Overlays.News.Sidebar private readonly SpriteIcon icon; + protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds(); + public DropdownHeader(int month, int year) { var date = new DateTime(year, month, 1); @@ -104,6 +126,7 @@ namespace osu.Game.Overlays.News.Sidebar private readonly APINewsPost post; public PostButton(APINewsPost post) + : base(HoverSampleSet.Submit) { this.post = post; From 971728196902b9cd905e967363a7265ce71b576e Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 30 Jul 2021 14:51:16 +0200 Subject: [PATCH 0734/2442] Localise rankings Spotlight selector. --- .../Overlays/Rankings/SpotlightSelector.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Rankings/SpotlightSelector.cs b/osu.Game/Overlays/Rankings/SpotlightSelector.cs index 89dd4eafdd..7f642c1832 100644 --- a/osu.Game/Overlays/Rankings/SpotlightSelector.cs +++ b/osu.Game/Overlays/Rankings/SpotlightSelector.cs @@ -16,6 +16,8 @@ using System.Collections.Generic; using osu.Framework.Graphics.UserInterface; using osu.Game.Online.API.Requests; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Rankings { @@ -92,10 +94,10 @@ namespace osu.Game.Overlays.Rankings Margin = new MarginPadding { Bottom = 5 }, Children = new Drawable[] { - startDateColumn = new InfoColumn(@"Start Date"), - endDateColumn = new InfoColumn(@"End Date"), - mapCountColumn = new InfoColumn(@"Map Count"), - participantsColumn = new InfoColumn(@"Participants") + startDateColumn = new InfoColumn(RankingsStrings.SpotlightStartDate), + endDateColumn = new InfoColumn(RankingsStrings.SpotlightEndDate), + mapCountColumn = new InfoColumn(RankingsStrings.SpotlightMapCount), + participantsColumn = new InfoColumn(RankingsStrings.SpotlightParticipants) } }, new RankingsSortTabControl @@ -123,21 +125,21 @@ namespace osu.Game.Overlays.Rankings startDateColumn.Value = dateToString(response.Spotlight.StartDate); endDateColumn.Value = dateToString(response.Spotlight.EndDate); mapCountColumn.Value = response.BeatmapSets.Count.ToString(); - participantsColumn.Value = response.Spotlight.Participants?.ToString("N0"); + participantsColumn.Value = response.Spotlight.Participants?.ToLocalisableString("N0"); } - private string dateToString(DateTimeOffset date) => date.ToString("yyyy-MM-dd"); + private LocalisableString dateToString(DateTimeOffset date) => date.ToLocalisableString("yyyy-MM-dd"); private class InfoColumn : FillFlowContainer { - public string Value + public LocalisableString Value { set => valueText.Text = value; } private readonly OsuSpriteText valueText; - public InfoColumn(string name) + public InfoColumn(LocalisableString name) { AutoSizeAxes = Axes.Both; Direction = FillDirection.Vertical; From db1ed873e42e6a43fdf4589ee2ade961a9d86b85 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 30 Jul 2021 15:24:10 +0200 Subject: [PATCH 0735/2442] Localise ranking tables. --- .../Rankings/Tables/CountriesTable.cs | 26 ++++++++++--------- .../Rankings/Tables/PerformanceTable.cs | 6 +++-- .../Overlays/Rankings/Tables/RankingsTable.cs | 2 +- .../Overlays/Rankings/Tables/ScoresTable.cs | 10 ++++--- .../Rankings/Tables/UserBasedTable.cs | 17 ++++++------ 5 files changed, 34 insertions(+), 27 deletions(-) diff --git a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs index b28bf07adc..a09629de57 100644 --- a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs @@ -9,6 +9,8 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using System.Collections.Generic; using osu.Framework.Allocation; +using osu.Game.Resources.Localisation.Web; +using osu.Framework.Localisation; namespace osu.Game.Overlays.Rankings.Tables { @@ -21,12 +23,12 @@ namespace osu.Game.Overlays.Rankings.Tables protected override RankingsTableColumn[] CreateAdditionalHeaders() => new[] { - new RankingsTableColumn("Active Users", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new RankingsTableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new RankingsTableColumn("Ranked Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new RankingsTableColumn("Avg. Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new RankingsTableColumn("Performance", Anchor.Centre, new Dimension(GridSizeMode.AutoSize), true), - new RankingsTableColumn("Avg. Perf.", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn(RankingsStrings.StatActiveUsers, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn(RankingsStrings.StatPlayCount, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn(RankingsStrings.StatRankedScore, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn(RankingsStrings.StatAverageScore, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn(RankingsStrings.StatPerformance, Anchor.Centre, new Dimension(GridSizeMode.AutoSize), true), + new RankingsTableColumn(RankingsStrings.StatAveragePerformance, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), }; protected override Country GetCountry(CountryStatistics item) => item.Country; @@ -37,27 +39,27 @@ namespace osu.Game.Overlays.Rankings.Tables { new ColoredRowText { - Text = $@"{item.ActiveUsers:N0}", + Text = item.ActiveUsers.ToLocalisableString("N0") }, new ColoredRowText { - Text = $@"{item.PlayCount:N0}", + Text = item.PlayCount.ToLocalisableString("N0") }, new ColoredRowText { - Text = $@"{item.RankedScore:N0}", + Text = item.RankedScore.ToLocalisableString("N0") }, new ColoredRowText { - Text = $@"{item.RankedScore / Math.Max(item.ActiveUsers, 1):N0}", + Text = (item.RankedScore / Math.Max(item.ActiveUsers, 1)).ToLocalisableString("N0") }, new RowText { - Text = $@"{item.Performance:N0}", + Text = item.Performance.ToLocalisableString("N0") }, new ColoredRowText { - Text = $@"{item.Performance / Math.Max(item.ActiveUsers, 1):N0}", + Text = (item.Performance / Math.Max(item.ActiveUsers, 1)).ToLocalisableString("N0") } }; diff --git a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs index ddf4bdcfeb..ae49445702 100644 --- a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; namespace osu.Game.Overlays.Rankings.Tables @@ -17,12 +19,12 @@ namespace osu.Game.Overlays.Rankings.Tables protected override RankingsTableColumn[] CreateUniqueHeaders() => new[] { - new RankingsTableColumn("Performance", Anchor.Centre, new Dimension(GridSizeMode.AutoSize), true), + new RankingsTableColumn(RankingsStrings.StatPerformance, Anchor.Centre, new Dimension(GridSizeMode.AutoSize), true), }; protected override Drawable[] CreateUniqueContent(UserStatistics item) => new Drawable[] { - new RowText { Text = $@"{item.PP:N0}", } + new RowText { Text = item.PP.ToLocalisableString("N0"), } }; } } diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 0cdff0a77f..59f38920d6 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -80,7 +80,7 @@ namespace osu.Game.Overlays.Rankings.Tables private OsuSpriteText createIndexDrawable(int index) => new RowText { - Text = $"#{index + 1}", + Text = (index + 1).ToLocalisableString("\\##"), Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.SemiBold) }; diff --git a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs index 508ad51112..4cf75eda81 100644 --- a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; namespace osu.Game.Overlays.Rankings.Tables @@ -17,19 +19,19 @@ namespace osu.Game.Overlays.Rankings.Tables protected override RankingsTableColumn[] CreateUniqueHeaders() => new[] { - new RankingsTableColumn("Total Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new RankingsTableColumn("Ranked Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize), true) + new RankingsTableColumn(RankingsStrings.StatTotalScore, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn(RankingsStrings.StatRankedScore, Anchor.Centre, new Dimension(GridSizeMode.AutoSize), true) }; protected override Drawable[] CreateUniqueContent(UserStatistics item) => new Drawable[] { new ColoredRowText { - Text = $@"{item.TotalScore:N0}", + Text = item.TotalScore.ToLocalisableString("N0"), }, new RowText { - Text = $@"{item.RankedScore:N0}", + Text = item.RankedScore.ToLocalisableString("N0") } }; } diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs index 131ee86d44..a7fa6899f8 100644 --- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -10,6 +10,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Users; using osu.Game.Scoring; using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Rankings.Tables { @@ -20,12 +21,12 @@ namespace osu.Game.Overlays.Rankings.Tables { } - protected virtual IEnumerable GradeColumns => new List { "SS", "S", "A" }; + protected virtual IEnumerable GradeColumns => new List { RankingsStrings.Statss, RankingsStrings.Stats, RankingsStrings.Stata }; protected override RankingsTableColumn[] CreateAdditionalHeaders() => new[] { - new RankingsTableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new RankingsTableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn(RankingsStrings.StatAccuracy, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new RankingsTableColumn(RankingsStrings.StatPlayCount, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), }.Concat(CreateUniqueHeaders()) .Concat(GradeColumns.Select(grade => new GradeTableColumn(grade, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)))) .ToArray(); @@ -47,12 +48,12 @@ namespace osu.Game.Overlays.Rankings.Tables protected sealed override Drawable[] CreateAdditionalContent(UserStatistics item) => new[] { new ColoredRowText { Text = item.DisplayAccuracy, }, - new ColoredRowText { Text = $@"{item.PlayCount:N0}", }, + new ColoredRowText { Text = item.PlayCount.ToLocalisableString("N0") }, }.Concat(CreateUniqueContent(item)).Concat(new[] { - new ColoredRowText { Text = $@"{item.GradesCount[ScoreRank.XH] + item.GradesCount[ScoreRank.X]:N0}", }, - new ColoredRowText { Text = $@"{item.GradesCount[ScoreRank.SH] + item.GradesCount[ScoreRank.S]:N0}", }, - new ColoredRowText { Text = $@"{item.GradesCount[ScoreRank.A]:N0}", } + new ColoredRowText { Text = (item.GradesCount[ScoreRank.XH] + item.GradesCount[ScoreRank.X]).ToLocalisableString("N0"), }, + new ColoredRowText { Text = (item.GradesCount[ScoreRank.SH] + item.GradesCount[ScoreRank.S]).ToLocalisableString("N0"), }, + new ColoredRowText { Text = item.GradesCount[ScoreRank.A].ToLocalisableString("N0"), } }).ToArray(); protected abstract RankingsTableColumn[] CreateUniqueHeaders(); @@ -78,7 +79,7 @@ namespace osu.Game.Overlays.Rankings.Tables { // Grade columns have extra horizontal padding for readibility Horizontal = 20, - Vertical = 5 + Vertical = 150 }; } } From 8a42d88793fe0592aa17c684f3c9e87a25c6a3ee Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 30 Jul 2021 16:28:18 +0200 Subject: [PATCH 0736/2442] Fix whitespace inspections. --- osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs index 4edd8ab1a7..1d6d884d58 100644 --- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs @@ -56,7 +56,7 @@ namespace osu.Game.Overlays.Rankings { case RankingsScope.Performance: return RankingsStrings.TypePerformance; - + case RankingsScope.Spotlights: return RankingsStrings.TypeCharts; From 7e870235576dffb34f2282bc0660bb8c364ed10a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 23:57:40 +0900 Subject: [PATCH 0737/2442] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 1c180a6b39..c1075cfb17 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 2efcfeb278..936fe8db94 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 2630614c03..7b7d5f80fe 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From 5381e11880be447bef613b0c1a9af7e34b5c0eb1 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 30 Jul 2021 19:06:25 +0200 Subject: [PATCH 0738/2442] Revert unintentional change. --- osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs index a7fa6899f8..60c3c76a34 100644 --- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -79,7 +79,7 @@ namespace osu.Game.Overlays.Rankings.Tables { // Grade columns have extra horizontal padding for readibility Horizontal = 20, - Vertical = 150 + Vertical = 5 }; } } From 652fe6c413aec6f3bd5f0e6be18d0c8d7ea1188f Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 30 Jul 2021 19:07:49 +0200 Subject: [PATCH 0739/2442] Uppercase sort filter control text. --- osu.Game/Overlays/Rankings/RankingsSortTabControl.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Rankings/RankingsSortTabControl.cs b/osu.Game/Overlays/Rankings/RankingsSortTabControl.cs index 782055181f..c04eb5bdd1 100644 --- a/osu.Game/Overlays/Rankings/RankingsSortTabControl.cs +++ b/osu.Game/Overlays/Rankings/RankingsSortTabControl.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; @@ -11,7 +12,7 @@ namespace osu.Game.Overlays.Rankings { public RankingsSortTabControl() { - Title = RankingsStrings.FilterTitle; + Title = RankingsStrings.FilterTitle.ToUpper(); } } From c7e9d09ce3747a95f91e0264c3648e277bff5a32 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 30 Jul 2021 19:08:27 +0200 Subject: [PATCH 0740/2442] Localise left over numeric value. --- osu.Game/Overlays/Rankings/SpotlightSelector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Rankings/SpotlightSelector.cs b/osu.Game/Overlays/Rankings/SpotlightSelector.cs index 7f642c1832..89d40277fc 100644 --- a/osu.Game/Overlays/Rankings/SpotlightSelector.cs +++ b/osu.Game/Overlays/Rankings/SpotlightSelector.cs @@ -124,7 +124,7 @@ namespace osu.Game.Overlays.Rankings { startDateColumn.Value = dateToString(response.Spotlight.StartDate); endDateColumn.Value = dateToString(response.Spotlight.EndDate); - mapCountColumn.Value = response.BeatmapSets.Count.ToString(); + mapCountColumn.Value = response.BeatmapSets.Count.ToLocalisableString("N0"); participantsColumn.Value = response.Spotlight.Participants?.ToLocalisableString("N0"); } From 3409bc6b27550580e8de777ff1e88200aeddc862 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 31 Jul 2021 01:38:54 +0300 Subject: [PATCH 0741/2442] Update mapper usages with `LocalisableDescription` --- osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs | 52 ++++-------- .../Overlays/BeatmapListing/SearchCategory.cs | 55 ++++--------- .../Overlays/BeatmapListing/SearchExplicit.cs | 23 +----- .../Overlays/BeatmapListing/SearchExtra.cs | 22 +---- .../Overlays/BeatmapListing/SearchGeneral.cs | 26 +----- .../Overlays/BeatmapListing/SearchGenre.cs | 81 ++++++------------- .../Overlays/BeatmapListing/SearchLanguage.cs | 74 ++++------------- .../Overlays/BeatmapListing/SearchPlayed.cs | 28 ++----- .../Overlays/BeatmapListing/SortCriteria.cs | 53 ++++-------- .../Dashboard/DashboardOverlayHeader.cs | 21 +---- .../Dashboard/Friends/OnlineStatus.cs | 28 ++----- .../Dashboard/Friends/UserSortTabControl.cs | 28 ++----- .../OverlayPanelDisplayStyleControl.cs | 28 ++----- osu.Game/Scoring/ScoreRank.cs | 46 ++--------- 14 files changed, 120 insertions(+), 445 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs b/osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs index 6003e23a84..edaf044466 100644 --- a/osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs +++ b/osu.Game/Beatmaps/BeatmapSetOnlineStatus.cs @@ -1,22 +1,34 @@ // 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 osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Beatmaps { - [LocalisableEnum(typeof(BeatmapSetOnlineStatusEnumLocalisationMapper))] public enum BeatmapSetOnlineStatus { None = -3, + + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowStatusGraveyard))] Graveyard = -2, + + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowStatusWip))] WIP = -1, + + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowStatusPending))] Pending = 0, + + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowStatusRanked))] Ranked = 1, + + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowStatusApproved))] Approved = 2, + + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowStatusQualified))] Qualified = 3, + + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowStatusLoved))] Loved = 4, } @@ -25,40 +37,4 @@ namespace osu.Game.Beatmaps public static bool GrantsPerformancePoints(this BeatmapSetOnlineStatus status) => status == BeatmapSetOnlineStatus.Ranked || status == BeatmapSetOnlineStatus.Approved; } - - public class BeatmapSetOnlineStatusEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(BeatmapSetOnlineStatus value) - { - switch (value) - { - case BeatmapSetOnlineStatus.None: - return string.Empty; - - case BeatmapSetOnlineStatus.Graveyard: - return BeatmapsetsStrings.ShowStatusGraveyard; - - case BeatmapSetOnlineStatus.WIP: - return BeatmapsetsStrings.ShowStatusWip; - - case BeatmapSetOnlineStatus.Pending: - return BeatmapsetsStrings.ShowStatusPending; - - case BeatmapSetOnlineStatus.Ranked: - return BeatmapsetsStrings.ShowStatusRanked; - - case BeatmapSetOnlineStatus.Approved: - return BeatmapsetsStrings.ShowStatusApproved; - - case BeatmapSetOnlineStatus.Qualified: - return BeatmapsetsStrings.ShowStatusQualified; - - case BeatmapSetOnlineStatus.Loved: - return BeatmapsetsStrings.ShowStatusLoved; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/BeatmapListing/SearchCategory.cs b/osu.Game/Overlays/BeatmapListing/SearchCategory.cs index 8a9df76af3..d6ae41aba1 100644 --- a/osu.Game/Overlays/BeatmapListing/SearchCategory.cs +++ b/osu.Game/Overlays/BeatmapListing/SearchCategory.cs @@ -1,69 +1,42 @@ // 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.ComponentModel; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapListing { - [LocalisableEnum(typeof(SearchCategoryEnumLocalisationMapper))] public enum SearchCategory { + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.StatusAny))] Any, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.StatusLeaderboard))] [Description("Has Leaderboard")] Leaderboard, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.StatusRanked))] Ranked, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.StatusQualified))] Qualified, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.StatusLoved))] Loved, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.StatusFavourites))] Favourites, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.StatusPending))] [Description("Pending & WIP")] Pending, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.StatusGraveyard))] Graveyard, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.StatusMine))] [Description("My Maps")] Mine, } - - public class SearchCategoryEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(SearchCategory value) - { - switch (value) - { - case SearchCategory.Any: - return BeatmapsStrings.StatusAny; - - case SearchCategory.Leaderboard: - return BeatmapsStrings.StatusLeaderboard; - - case SearchCategory.Ranked: - return BeatmapsStrings.StatusRanked; - - case SearchCategory.Qualified: - return BeatmapsStrings.StatusQualified; - - case SearchCategory.Loved: - return BeatmapsStrings.StatusLoved; - - case SearchCategory.Favourites: - return BeatmapsStrings.StatusFavourites; - - case SearchCategory.Pending: - return BeatmapsStrings.StatusPending; - - case SearchCategory.Graveyard: - return BeatmapsStrings.StatusGraveyard; - - case SearchCategory.Mine: - return BeatmapsStrings.StatusMine; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/BeatmapListing/SearchExplicit.cs b/osu.Game/Overlays/BeatmapListing/SearchExplicit.cs index 78e6a4e094..80482b32a0 100644 --- a/osu.Game/Overlays/BeatmapListing/SearchExplicit.cs +++ b/osu.Game/Overlays/BeatmapListing/SearchExplicit.cs @@ -1,34 +1,17 @@ // 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 osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapListing { - [LocalisableEnum(typeof(SearchExplicitEnumLocalisationMapper))] public enum SearchExplicit { + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.NsfwExclude))] Hide, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.NsfwInclude))] Show } - - public class SearchExplicitEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(SearchExplicit value) - { - switch (value) - { - case SearchExplicit.Hide: - return BeatmapsStrings.NsfwExclude; - - case SearchExplicit.Show: - return BeatmapsStrings.NsfwInclude; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/BeatmapListing/SearchExtra.cs b/osu.Game/Overlays/BeatmapListing/SearchExtra.cs index 4b3fb6e833..e54632acd8 100644 --- a/osu.Game/Overlays/BeatmapListing/SearchExtra.cs +++ b/osu.Game/Overlays/BeatmapListing/SearchExtra.cs @@ -1,38 +1,20 @@ // 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.ComponentModel; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapListing { - [LocalisableEnum(typeof(SearchExtraEnumLocalisationMapper))] public enum SearchExtra { + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ExtraVideo))] [Description("Has Video")] Video, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ExtraStoryboard))] [Description("Has Storyboard")] Storyboard } - - public class SearchExtraEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(SearchExtra value) - { - switch (value) - { - case SearchExtra.Video: - return BeatmapsStrings.ExtraVideo; - - case SearchExtra.Storyboard: - return BeatmapsStrings.ExtraStoryboard; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/BeatmapListing/SearchGeneral.cs b/osu.Game/Overlays/BeatmapListing/SearchGeneral.cs index b4c629f7fa..d334b82e88 100644 --- a/osu.Game/Overlays/BeatmapListing/SearchGeneral.cs +++ b/osu.Game/Overlays/BeatmapListing/SearchGeneral.cs @@ -1,44 +1,24 @@ // 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.ComponentModel; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapListing { - [LocalisableEnum(typeof(SearchGeneralEnumLocalisationMapper))] public enum SearchGeneral { + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GeneralRecommended))] [Description("Recommended difficulty")] Recommended, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GeneralConverts))] [Description("Include converted beatmaps")] Converts, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GeneralFollows))] [Description("Subscribed mappers")] Follows } - - public class SearchGeneralEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(SearchGeneral value) - { - switch (value) - { - case SearchGeneral.Recommended: - return BeatmapsStrings.GeneralRecommended; - - case SearchGeneral.Converts: - return BeatmapsStrings.GeneralConverts; - - case SearchGeneral.Follows: - return BeatmapsStrings.GeneralFollows; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/BeatmapListing/SearchGenre.cs b/osu.Game/Overlays/BeatmapListing/SearchGenre.cs index b2709ecd2e..08855284cb 100644 --- a/osu.Game/Overlays/BeatmapListing/SearchGenre.cs +++ b/osu.Game/Overlays/BeatmapListing/SearchGenre.cs @@ -1,87 +1,56 @@ // 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.ComponentModel; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapListing { - [LocalisableEnum(typeof(SearchGenreEnumLocalisationMapper))] public enum SearchGenre { + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreAny))] Any = 0, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreUnspecified))] Unspecified = 1, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreVideoGame))] [Description("Video Game")] VideoGame = 2, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreAnime))] Anime = 3, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreRock))] Rock = 4, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenrePop))] Pop = 5, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreOther))] Other = 6, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreNovelty))] Novelty = 7, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreHipHop))] [Description("Hip Hop")] HipHop = 9, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreElectronic))] Electronic = 10, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreMetal))] Metal = 11, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreClassical))] Classical = 12, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreFolk))] Folk = 13, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GenreJazz))] Jazz = 14 } - - public class SearchGenreEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(SearchGenre value) - { - switch (value) - { - case SearchGenre.Any: - return BeatmapsStrings.GenreAny; - - case SearchGenre.Unspecified: - return BeatmapsStrings.GenreUnspecified; - - case SearchGenre.VideoGame: - return BeatmapsStrings.GenreVideoGame; - - case SearchGenre.Anime: - return BeatmapsStrings.GenreAnime; - - case SearchGenre.Rock: - return BeatmapsStrings.GenreRock; - - case SearchGenre.Pop: - return BeatmapsStrings.GenrePop; - - case SearchGenre.Other: - return BeatmapsStrings.GenreOther; - - case SearchGenre.Novelty: - return BeatmapsStrings.GenreNovelty; - - case SearchGenre.HipHop: - return BeatmapsStrings.GenreHipHop; - - case SearchGenre.Electronic: - return BeatmapsStrings.GenreElectronic; - - case SearchGenre.Metal: - return BeatmapsStrings.GenreMetal; - - case SearchGenre.Classical: - return BeatmapsStrings.GenreClassical; - - case SearchGenre.Folk: - return BeatmapsStrings.GenreFolk; - - case SearchGenre.Jazz: - return BeatmapsStrings.GenreJazz; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/BeatmapListing/SearchLanguage.cs b/osu.Game/Overlays/BeatmapListing/SearchLanguage.cs index fc176c305a..7ffa0282b7 100644 --- a/osu.Game/Overlays/BeatmapListing/SearchLanguage.cs +++ b/osu.Game/Overlays/BeatmapListing/SearchLanguage.cs @@ -1,117 +1,73 @@ // 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 osu.Framework.Localisation; using osu.Framework.Utils; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapListing { - [LocalisableEnum(typeof(SearchLanguageEnumLocalisationMapper))] [HasOrderedElements] public enum SearchLanguage { + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageAny))] [Order(0)] Any, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageUnspecified))] [Order(14)] Unspecified, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageEnglish))] [Order(1)] English, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageJapanese))] [Order(6)] Japanese, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageChinese))] [Order(2)] Chinese, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageInstrumental))] [Order(12)] Instrumental, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageKorean))] [Order(7)] Korean, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageFrench))] [Order(3)] French, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageGerman))] [Order(4)] German, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageSwedish))] [Order(9)] Swedish, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageSpanish))] [Order(8)] Spanish, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageItalian))] [Order(5)] Italian, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageRussian))] [Order(10)] Russian, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguagePolish))] [Order(11)] Polish, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.LanguageOther))] [Order(13)] Other } - - public class SearchLanguageEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(SearchLanguage value) - { - switch (value) - { - case SearchLanguage.Any: - return BeatmapsStrings.LanguageAny; - - case SearchLanguage.Unspecified: - return BeatmapsStrings.LanguageUnspecified; - - case SearchLanguage.English: - return BeatmapsStrings.LanguageEnglish; - - case SearchLanguage.Japanese: - return BeatmapsStrings.LanguageJapanese; - - case SearchLanguage.Chinese: - return BeatmapsStrings.LanguageChinese; - - case SearchLanguage.Instrumental: - return BeatmapsStrings.LanguageInstrumental; - - case SearchLanguage.Korean: - return BeatmapsStrings.LanguageKorean; - - case SearchLanguage.French: - return BeatmapsStrings.LanguageFrench; - - case SearchLanguage.German: - return BeatmapsStrings.LanguageGerman; - - case SearchLanguage.Swedish: - return BeatmapsStrings.LanguageSwedish; - - case SearchLanguage.Spanish: - return BeatmapsStrings.LanguageSpanish; - - case SearchLanguage.Italian: - return BeatmapsStrings.LanguageItalian; - - case SearchLanguage.Russian: - return BeatmapsStrings.LanguageRussian; - - case SearchLanguage.Polish: - return BeatmapsStrings.LanguagePolish; - - case SearchLanguage.Other: - return BeatmapsStrings.LanguageOther; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/BeatmapListing/SearchPlayed.cs b/osu.Game/Overlays/BeatmapListing/SearchPlayed.cs index f24cf46c2d..3b04ac01ca 100644 --- a/osu.Game/Overlays/BeatmapListing/SearchPlayed.cs +++ b/osu.Game/Overlays/BeatmapListing/SearchPlayed.cs @@ -1,38 +1,20 @@ // 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 osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapListing { - [LocalisableEnum(typeof(SearchPlayedEnumLocalisationMapper))] public enum SearchPlayed { + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.PlayedAny))] Any, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.PlayedPlayed))] Played, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.PlayedUnplayed))] Unplayed } - - public class SearchPlayedEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(SearchPlayed value) - { - switch (value) - { - case SearchPlayed.Any: - return BeatmapsStrings.PlayedAny; - - case SearchPlayed.Played: - return BeatmapsStrings.PlayedPlayed; - - case SearchPlayed.Unplayed: - return BeatmapsStrings.PlayedUnplayed; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/BeatmapListing/SortCriteria.cs b/osu.Game/Overlays/BeatmapListing/SortCriteria.cs index 5ea885eecc..871b3c162b 100644 --- a/osu.Game/Overlays/BeatmapListing/SortCriteria.cs +++ b/osu.Game/Overlays/BeatmapListing/SortCriteria.cs @@ -1,58 +1,35 @@ // 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 osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapListing { - [LocalisableEnum(typeof(SortCriteriaLocalisationMapper))] public enum SortCriteria { + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingTitle))] Title, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingArtist))] Artist, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingDifficulty))] Difficulty, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingRanked))] Ranked, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingRating))] Rating, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingPlays))] Plays, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingFavourites))] Favourites, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingRelevance))] Relevance } - - public class SortCriteriaLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(SortCriteria value) - { - switch (value) - { - case SortCriteria.Title: - return BeatmapsStrings.ListingSearchSortingTitle; - - case SortCriteria.Artist: - return BeatmapsStrings.ListingSearchSortingArtist; - - case SortCriteria.Difficulty: - return BeatmapsStrings.ListingSearchSortingDifficulty; - - case SortCriteria.Ranked: - return BeatmapsStrings.ListingSearchSortingRanked; - - case SortCriteria.Rating: - return BeatmapsStrings.ListingSearchSortingRating; - - case SortCriteria.Plays: - return BeatmapsStrings.ListingSearchSortingPlays; - - case SortCriteria.Favourites: - return BeatmapsStrings.ListingSearchSortingFavourites; - - case SortCriteria.Relevance: - return BeatmapsStrings.ListingSearchSortingRelevance; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs index 056d4ad6f7..79dadf5e0d 100644 --- a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs +++ b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.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.ComponentModel; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; @@ -23,30 +22,12 @@ namespace osu.Game.Overlays.Dashboard } } - [LocalisableEnum(typeof(DashboardOverlayTabsEnumLocalisationMapper))] public enum DashboardOverlayTabs { + [LocalisableDescription(typeof(FriendsStrings), nameof(FriendsStrings.TitleCompact))] Friends, [Description("Currently Playing")] CurrentlyPlaying } - - public class DashboardOverlayTabsEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(DashboardOverlayTabs value) - { - switch (value) - { - case DashboardOverlayTabs.Friends: - return FriendsStrings.TitleCompact; - - case DashboardOverlayTabs.CurrentlyPlaying: - return @"Currently Playing"; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/Dashboard/Friends/OnlineStatus.cs b/osu.Game/Overlays/Dashboard/Friends/OnlineStatus.cs index 4b5a7ef066..853c94d8ae 100644 --- a/osu.Game/Overlays/Dashboard/Friends/OnlineStatus.cs +++ b/osu.Game/Overlays/Dashboard/Friends/OnlineStatus.cs @@ -1,38 +1,20 @@ // 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 osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Dashboard.Friends { - [LocalisableEnum(typeof(OnlineStatusEnumLocalisationMapper))] public enum OnlineStatus { + [LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.StatusAll))] All, + + [LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.StatusOnline))] Online, + + [LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.StatusOffline))] Offline } - - public class OnlineStatusEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(OnlineStatus value) - { - switch (value) - { - case OnlineStatus.All: - return SortStrings.All; - - case OnlineStatus.Online: - return UsersStrings.StatusOnline; - - case OnlineStatus.Offline: - return UsersStrings.StatusOffline; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/Dashboard/Friends/UserSortTabControl.cs b/osu.Game/Overlays/Dashboard/Friends/UserSortTabControl.cs index dc756e2957..7fee5f4668 100644 --- a/osu.Game/Overlays/Dashboard/Friends/UserSortTabControl.cs +++ b/osu.Game/Overlays/Dashboard/Friends/UserSortTabControl.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.ComponentModel; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; @@ -12,33 +11,16 @@ namespace osu.Game.Overlays.Dashboard.Friends { } - [LocalisableEnum(typeof(UserSortCriteriaEnumLocalisationMappper))] public enum UserSortCriteria { + [LocalisableDescription(typeof(SortStrings), nameof(SortStrings.LastVisit))] [Description(@"Recently Active")] LastVisit, + + [LocalisableDescription(typeof(SortStrings), nameof(SortStrings.Rank))] Rank, + + [LocalisableDescription(typeof(SortStrings), nameof(SortStrings.Username))] Username } - - public class UserSortCriteriaEnumLocalisationMappper : EnumLocalisationMapper - { - public override LocalisableString Map(UserSortCriteria value) - { - switch (value) - { - case UserSortCriteria.LastVisit: - return SortStrings.LastVisit; - - case UserSortCriteria.Rank: - return SortStrings.Rank; - - case UserSortCriteria.Username: - return SortStrings.Username; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/OverlayPanelDisplayStyleControl.cs b/osu.Game/Overlays/OverlayPanelDisplayStyleControl.cs index c2268ff43c..d7a3b052ae 100644 --- a/osu.Game/Overlays/OverlayPanelDisplayStyleControl.cs +++ b/osu.Game/Overlays/OverlayPanelDisplayStyleControl.cs @@ -12,7 +12,6 @@ using osu.Framework.Allocation; using osuTK.Graphics; using osu.Framework.Graphics.Cursor; using osu.Framework.Localisation; -using System; using osu.Game.Resources.Localisation.Web; using osu.Framework.Extensions; @@ -101,32 +100,15 @@ namespace osu.Game.Overlays } } - [LocalisableEnum(typeof(OverlayPanelDisplayStyleEnumLocalisationMapper))] public enum OverlayPanelDisplayStyle { + [LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.ViewModeCard))] Card, + + [LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.ViewModeList))] List, + + [LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.ViewModeBrick))] Brick } - - public class OverlayPanelDisplayStyleEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(OverlayPanelDisplayStyle value) - { - switch (value) - { - case OverlayPanelDisplayStyle.Card: - return UsersStrings.ViewModeCard; - - case OverlayPanelDisplayStyle.List: - return UsersStrings.ViewModeList; - - case OverlayPanelDisplayStyle.Brick: - return UsersStrings.ViewModeBrick; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Scoring/ScoreRank.cs b/osu.Game/Scoring/ScoreRank.cs index f3b4551ff8..64f7da9ba3 100644 --- a/osu.Game/Scoring/ScoreRank.cs +++ b/osu.Game/Scoring/ScoreRank.cs @@ -1,74 +1,44 @@ // 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.ComponentModel; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Scoring { - [LocalisableEnum(typeof(ScoreRankEnumLocalisationMapper))] public enum ScoreRank { + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.RankD))] [Description(@"D")] D, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.RankC))] [Description(@"C")] C, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.RankB))] [Description(@"B")] B, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.RankA))] [Description(@"A")] A, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.RankS))] [Description(@"S")] S, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.RankSH))] [Description(@"S+")] SH, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.RankX))] [Description(@"SS")] X, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.RankXH))] [Description(@"SS+")] XH, } - - public class ScoreRankEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(ScoreRank value) - { - switch (value) - { - case ScoreRank.XH: - return BeatmapsStrings.RankXH; - - case ScoreRank.X: - return BeatmapsStrings.RankX; - - case ScoreRank.SH: - return BeatmapsStrings.RankSH; - - case ScoreRank.S: - return BeatmapsStrings.RankS; - - case ScoreRank.A: - return BeatmapsStrings.RankA; - - case ScoreRank.B: - return BeatmapsStrings.RankB; - - case ScoreRank.C: - return BeatmapsStrings.RankC; - - case ScoreRank.D: - return BeatmapsStrings.RankD; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } From 397c73e786aa8c49ff233fc74bab2ec1bdd24657 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 31 Jul 2021 02:09:25 +0300 Subject: [PATCH 0742/2442] Add audio adjustment support to `Metronome` --- osu.Game/Rulesets/Mods/Metronome.cs | 62 +++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/osu.Game/Rulesets/Mods/Metronome.cs b/osu.Game/Rulesets/Mods/Metronome.cs index ee0a42b4bc..8b6d86c45f 100644 --- a/osu.Game/Rulesets/Mods/Metronome.cs +++ b/osu.Game/Rulesets/Mods/Metronome.cs @@ -1,9 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Audio.Track; -using osu.Framework.Graphics; +using osu.Framework.Bindables; using osu.Game.Audio; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics.Containers; @@ -11,11 +11,11 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Mods { - public class Metronome : BeatSyncedContainer + public class Metronome : BeatSyncedContainer, IAdjustableAudioComponent { private readonly double firstHitTime; - private PausableSkinnableSound sample; + private readonly PausableSkinnableSound sample; /// Start time of the first hit object, used for providing a count down. public Metronome(double firstHitTime) @@ -23,15 +23,8 @@ namespace osu.Game.Rulesets.Mods this.firstHitTime = firstHitTime; AllowMistimedEventFiring = false; Divisor = 1; - } - [BackgroundDependencyLoader] - private void load() - { - InternalChildren = new Drawable[] - { - sample = new PausableSkinnableSound(new SampleInfo("Gameplay/catch-banana")) - }; + InternalChild = sample = new PausableSkinnableSound(new SampleInfo("Gameplay/catch-banana")); } protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) @@ -49,5 +42,50 @@ namespace osu.Game.Rulesets.Mods sample.Frequency.Value = beatIndex % timeSignature == 0 ? 1 : 0.5f; sample.Play(); } + + #region IAdjustableAudioComponent + + public IBindable AggregateVolume => sample.AggregateVolume; + + public IBindable AggregateBalance => sample.AggregateBalance; + + public IBindable AggregateFrequency => sample.AggregateFrequency; + + public IBindable AggregateTempo => sample.AggregateTempo; + + public BindableNumber Volume => sample.Volume; + + public BindableNumber Balance => sample.Balance; + + public BindableNumber Frequency => sample.Frequency; + + public BindableNumber Tempo => sample.Tempo; + + public void BindAdjustments(IAggregateAudioAdjustment component) + { + sample.BindAdjustments(component); + } + + public void UnbindAdjustments(IAggregateAudioAdjustment component) + { + sample.UnbindAdjustments(component); + } + + public void AddAdjustment(AdjustableProperty type, IBindable adjustBindable) + { + sample.AddAdjustment(type, adjustBindable); + } + + public void RemoveAdjustment(AdjustableProperty type, IBindable adjustBindable) + { + sample.RemoveAdjustment(type, adjustBindable); + } + + public void RemoveAllAdjustments(AdjustableProperty type) + { + sample.RemoveAllAdjustments(type); + } + + #endregion } } From 4c68268d98186e445ce123ee57677a8bd577bcd7 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Sat, 31 Jul 2021 08:31:08 +0900 Subject: [PATCH 0743/2442] Call `ApplyTransformsAt` on free --- osu.Game.Rulesets.Catch/UI/CatcherTrail.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs index a7a2941a7a..f3f996d2c5 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs @@ -31,9 +31,8 @@ namespace osu.Game.Rulesets.Catch.UI protected override void OnApply(CatcherTrailEntry entry) { - Scale = entry.Scale; Position = new Vector2(entry.Position, 0); - Alpha = 1; + Scale = entry.Scale; body.AnimationState.Value = entry.CatcherState; @@ -43,6 +42,7 @@ namespace osu.Game.Rulesets.Catch.UI protected override void OnFree(CatcherTrailEntry entry) { + ApplyTransformsAt(double.MinValue); ClearTransforms(); } From e6f337a3c8c753681c8a22f91f84378db6e43e2b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 31 Jul 2021 14:27:20 +0900 Subject: [PATCH 0744/2442] User verbatim string for `ToLocalisableString` calls (and rename US spelling) --- .../Overlays/Rankings/SpotlightSelector.cs | 6 ++--- .../Rankings/Tables/CountriesTable.cs | 22 +++++++++---------- .../Rankings/Tables/PerformanceTable.cs | 2 +- .../Overlays/Rankings/Tables/RankingsTable.cs | 4 ++-- .../Overlays/Rankings/Tables/ScoresTable.cs | 2 +- .../Rankings/Tables/UserBasedTable.cs | 10 ++++----- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/osu.Game/Overlays/Rankings/SpotlightSelector.cs b/osu.Game/Overlays/Rankings/SpotlightSelector.cs index 89d40277fc..5309778a47 100644 --- a/osu.Game/Overlays/Rankings/SpotlightSelector.cs +++ b/osu.Game/Overlays/Rankings/SpotlightSelector.cs @@ -124,11 +124,11 @@ namespace osu.Game.Overlays.Rankings { startDateColumn.Value = dateToString(response.Spotlight.StartDate); endDateColumn.Value = dateToString(response.Spotlight.EndDate); - mapCountColumn.Value = response.BeatmapSets.Count.ToLocalisableString("N0"); - participantsColumn.Value = response.Spotlight.Participants?.ToLocalisableString("N0"); + mapCountColumn.Value = response.BeatmapSets.Count.ToLocalisableString(@"N0"); + participantsColumn.Value = response.Spotlight.Participants?.ToLocalisableString(@"N0"); } - private LocalisableString dateToString(DateTimeOffset date) => date.ToLocalisableString("yyyy-MM-dd"); + private LocalisableString dateToString(DateTimeOffset date) => date.ToLocalisableString(@"yyyy-MM-dd"); private class InfoColumn : FillFlowContainer { diff --git a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs index a09629de57..85a317728f 100644 --- a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs @@ -37,29 +37,29 @@ namespace osu.Game.Overlays.Rankings.Tables protected override Drawable[] CreateAdditionalContent(CountryStatistics item) => new Drawable[] { - new ColoredRowText + new ColouredRowText { - Text = item.ActiveUsers.ToLocalisableString("N0") + Text = item.ActiveUsers.ToLocalisableString(@"N0") }, - new ColoredRowText + new ColouredRowText { - Text = item.PlayCount.ToLocalisableString("N0") + Text = item.PlayCount.ToLocalisableString(@"N0") }, - new ColoredRowText + new ColouredRowText { - Text = item.RankedScore.ToLocalisableString("N0") + Text = item.RankedScore.ToLocalisableString(@"N0") }, - new ColoredRowText + new ColouredRowText { - Text = (item.RankedScore / Math.Max(item.ActiveUsers, 1)).ToLocalisableString("N0") + Text = (item.RankedScore / Math.Max(item.ActiveUsers, 1)).ToLocalisableString(@"N0") }, new RowText { - Text = item.Performance.ToLocalisableString("N0") + Text = item.Performance.ToLocalisableString(@"N0") }, - new ColoredRowText + new ColouredRowText { - Text = (item.Performance / Math.Max(item.ActiveUsers, 1)).ToLocalisableString("N0") + Text = (item.Performance / Math.Max(item.ActiveUsers, 1)).ToLocalisableString(@"N0") } }; diff --git a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs index ae49445702..6facf1e7a2 100644 --- a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Rankings.Tables protected override Drawable[] CreateUniqueContent(UserStatistics item) => new Drawable[] { - new RowText { Text = item.PP.ToLocalisableString("N0"), } + new RowText { Text = item.PP.ToLocalisableString(@"N0"), } }; } } diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 59f38920d6..bc8eac16a9 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -80,7 +80,7 @@ namespace osu.Game.Overlays.Rankings.Tables private OsuSpriteText createIndexDrawable(int index) => new RowText { - Text = (index + 1).ToLocalisableString("\\##"), + Text = (index + 1).ToLocalisableString(@"\##"), Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.SemiBold) }; @@ -144,7 +144,7 @@ namespace osu.Game.Overlays.Rankings.Tables } } - protected class ColoredRowText : RowText + protected class ColouredRowText : RowText { [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) diff --git a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs index 4cf75eda81..312c894055 100644 --- a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs @@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Rankings.Tables protected override Drawable[] CreateUniqueContent(UserStatistics item) => new Drawable[] { - new ColoredRowText + new ColouredRowText { Text = item.TotalScore.ToLocalisableString("N0"), }, diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs index 60c3c76a34..ce7a0e7e79 100644 --- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -47,13 +47,13 @@ namespace osu.Game.Overlays.Rankings.Tables protected sealed override Drawable[] CreateAdditionalContent(UserStatistics item) => new[] { - new ColoredRowText { Text = item.DisplayAccuracy, }, - new ColoredRowText { Text = item.PlayCount.ToLocalisableString("N0") }, + new ColouredRowText { Text = item.DisplayAccuracy, }, + new ColouredRowText { Text = item.PlayCount.ToLocalisableString("N0") }, }.Concat(CreateUniqueContent(item)).Concat(new[] { - new ColoredRowText { Text = (item.GradesCount[ScoreRank.XH] + item.GradesCount[ScoreRank.X]).ToLocalisableString("N0"), }, - new ColoredRowText { Text = (item.GradesCount[ScoreRank.SH] + item.GradesCount[ScoreRank.S]).ToLocalisableString("N0"), }, - new ColoredRowText { Text = item.GradesCount[ScoreRank.A].ToLocalisableString("N0"), } + new ColouredRowText { Text = (item.GradesCount[ScoreRank.XH] + item.GradesCount[ScoreRank.X]).ToLocalisableString("N0"), }, + new ColouredRowText { Text = (item.GradesCount[ScoreRank.SH] + item.GradesCount[ScoreRank.S]).ToLocalisableString("N0"), }, + new ColouredRowText { Text = item.GradesCount[ScoreRank.A].ToLocalisableString("N0"), } }).ToArray(); protected abstract RankingsTableColumn[] CreateUniqueHeaders(); From c0824989558bc389b9bee043d5ded289f3e2f48d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 31 Jul 2021 14:29:38 +0900 Subject: [PATCH 0745/2442] Fix some missed instances of verbatim string conversion --- osu.Game/Overlays/Rankings/Tables/ScoresTable.cs | 4 ++-- osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs index 312c894055..b6bb66e2c8 100644 --- a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs @@ -27,11 +27,11 @@ namespace osu.Game.Overlays.Rankings.Tables { new ColouredRowText { - Text = item.TotalScore.ToLocalisableString("N0"), + Text = item.TotalScore.ToLocalisableString(@"N0"), }, new RowText { - Text = item.RankedScore.ToLocalisableString("N0") + Text = item.RankedScore.ToLocalisableString(@"N0") } }; } diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs index ce7a0e7e79..b96ab556df 100644 --- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -48,12 +48,12 @@ namespace osu.Game.Overlays.Rankings.Tables protected sealed override Drawable[] CreateAdditionalContent(UserStatistics item) => new[] { new ColouredRowText { Text = item.DisplayAccuracy, }, - new ColouredRowText { Text = item.PlayCount.ToLocalisableString("N0") }, + new ColouredRowText { Text = item.PlayCount.ToLocalisableString(@"N0") }, }.Concat(CreateUniqueContent(item)).Concat(new[] { - new ColouredRowText { Text = (item.GradesCount[ScoreRank.XH] + item.GradesCount[ScoreRank.X]).ToLocalisableString("N0"), }, - new ColouredRowText { Text = (item.GradesCount[ScoreRank.SH] + item.GradesCount[ScoreRank.S]).ToLocalisableString("N0"), }, - new ColouredRowText { Text = item.GradesCount[ScoreRank.A].ToLocalisableString("N0"), } + new ColouredRowText { Text = (item.GradesCount[ScoreRank.XH] + item.GradesCount[ScoreRank.X]).ToLocalisableString(@"N0"), }, + new ColouredRowText { Text = (item.GradesCount[ScoreRank.SH] + item.GradesCount[ScoreRank.S]).ToLocalisableString(@"N0"), }, + new ColouredRowText { Text = item.GradesCount[ScoreRank.A].ToLocalisableString(@"N0"), } }).ToArray(); protected abstract RankingsTableColumn[] CreateUniqueHeaders(); From 29328bdf7f49a35f15e4bf87e9a2fd6151dd2567 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 31 Jul 2021 15:03:26 +0900 Subject: [PATCH 0746/2442] Use metronome's audio adjustments directly --- osu.Game/Rulesets/Mods/ModMuted.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index 5454483571..9eb218fd9b 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -35,8 +35,6 @@ namespace osu.Game.Rulesets.Mods private BindableNumber currentCombo; - private AudioContainer metronomeContainer; - [SettingSource("Enable metronome", "Add a metronome beat to help you keep track of the rhythm.")] public BindableBool EnableMetronome { get; } = new BindableBool { @@ -76,12 +74,11 @@ namespace osu.Game.Rulesets.Mods { if (EnableMetronome.Value) { - drawableRuleset.Overlays.Add(metronomeContainer = new AudioContainer - { - Child = new Metronome(drawableRuleset.Beatmap.HitObjects.First().StartTime) - }); + Metronome metronome; - metronomeContainer.AddAdjustment(AdjustableProperty.Volume, metronomeVolumeAdjust); + drawableRuleset.Overlays.Add(metronome = new Metronome(drawableRuleset.Beatmap.HitObjects.First().StartTime)); + + metronome.AddAdjustment(AdjustableProperty.Volume, metronomeVolumeAdjust); } if (AffectsHitSounds.Value) From 53c901bfa8051bab3ff96aee65ec8c16472764b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 31 Jul 2021 15:05:54 +0900 Subject: [PATCH 0747/2442] Expose `DrawableRuleset` audio adjustments as non-container --- ...rDisplayExtensions.cs => TimeDisplayExtensions.cs} | 6 ++++++ osu.Game/Rulesets/Mods/ModMuted.cs | 3 +-- osu.Game/Rulesets/UI/DrawableRuleset.cs | 11 ++++++++--- 3 files changed, 15 insertions(+), 5 deletions(-) rename osu.Game/Extensions/{EditorDisplayExtensions.cs => TimeDisplayExtensions.cs} (77%) diff --git a/osu.Game/Extensions/EditorDisplayExtensions.cs b/osu.Game/Extensions/TimeDisplayExtensions.cs similarity index 77% rename from osu.Game/Extensions/EditorDisplayExtensions.cs rename to osu.Game/Extensions/TimeDisplayExtensions.cs index f749b88b46..dacde44ca1 100644 --- a/osu.Game/Extensions/EditorDisplayExtensions.cs +++ b/osu.Game/Extensions/TimeDisplayExtensions.cs @@ -22,5 +22,11 @@ namespace osu.Game.Extensions /// An editor formatted display string. public static string ToEditorFormattedString(this TimeSpan timeSpan) => $"{(timeSpan < TimeSpan.Zero ? "-" : string.Empty)}{timeSpan:mm\\:ss\\:fff}"; + + public static string ToFormattedDuration(this double milliseconds) => + ToFormattedDuration(TimeSpan.FromMilliseconds(milliseconds)); + + public static string ToFormattedDuration(this TimeSpan timeSpan) => + timeSpan.Hours == 0 ? $"{timeSpan:mm\\:ss}" : $"{timeSpan:HH\\:mm\\:ss}"; } } diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index 9eb218fd9b..7fde14d6ca 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -7,7 +7,6 @@ using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Configuration; using osu.Game.Rulesets.Objects; @@ -82,7 +81,7 @@ namespace osu.Game.Rulesets.Mods } if (AffectsHitSounds.Value) - drawableRuleset.AudioContainer.AddAdjustment(AdjustableProperty.Volume, mainVolumeAdjust); + drawableRuleset.Audio.AddAdjustment(AdjustableProperty.Volume, mainVolumeAdjust); } public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index b3242a8b4b..29559f5036 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Threading; using JetBrains.Annotations; using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -99,12 +100,12 @@ namespace osu.Game.Rulesets.UI private DrawableRulesetDependencies dependencies; /// - /// An audio container which can be used to apply adjustments to playfield content. + /// Audio adjustments which are applied to the playfield. /// /// /// Does not affect . /// - public AudioContainer AudioContainer { get; private set; } + public IAdjustableAudioComponent Audio { get; private set; } /// /// Creates a ruleset visualisation for the provided ruleset and beatmap. @@ -163,13 +164,15 @@ namespace osu.Game.Rulesets.UI [BackgroundDependencyLoader] private void load(CancellationToken? cancellationToken) { + AudioContainer audioContainer; + InternalChild = frameStabilityContainer = new FrameStabilityContainer(GameplayStartTime) { FrameStablePlayback = FrameStablePlayback, Children = new Drawable[] { FrameStableComponents, - AudioContainer = new AudioContainer + audioContainer = new AudioContainer { RelativeSizeAxes = Axes.Both, Child = KeyBindingInputManager @@ -181,6 +184,8 @@ namespace osu.Game.Rulesets.UI } }; + Audio = audioContainer; + if ((ResumeOverlay = CreateResumeOverlay()) != null) { AddInternal(CreateInputManager() From f3d4f47e62551fe313070879a11dcca10a98ba98 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 31 Jul 2021 15:52:36 +0900 Subject: [PATCH 0748/2442] Revert unrelated changes --- ...{TimeDisplayExtensions.cs => EditorDisplayExtensions.cs} | 6 ------ 1 file changed, 6 deletions(-) rename osu.Game/Extensions/{TimeDisplayExtensions.cs => EditorDisplayExtensions.cs} (77%) diff --git a/osu.Game/Extensions/TimeDisplayExtensions.cs b/osu.Game/Extensions/EditorDisplayExtensions.cs similarity index 77% rename from osu.Game/Extensions/TimeDisplayExtensions.cs rename to osu.Game/Extensions/EditorDisplayExtensions.cs index dacde44ca1..f749b88b46 100644 --- a/osu.Game/Extensions/TimeDisplayExtensions.cs +++ b/osu.Game/Extensions/EditorDisplayExtensions.cs @@ -22,11 +22,5 @@ namespace osu.Game.Extensions /// An editor formatted display string. public static string ToEditorFormattedString(this TimeSpan timeSpan) => $"{(timeSpan < TimeSpan.Zero ? "-" : string.Empty)}{timeSpan:mm\\:ss\\:fff}"; - - public static string ToFormattedDuration(this double milliseconds) => - ToFormattedDuration(TimeSpan.FromMilliseconds(milliseconds)); - - public static string ToFormattedDuration(this TimeSpan timeSpan) => - timeSpan.Hours == 0 ? $"{timeSpan:mm\\:ss}" : $"{timeSpan:HH\\:mm\\:ss}"; } } From 472c0137ec856381ff6f5802007d268aad662a8a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 31 Jul 2021 16:45:53 +0900 Subject: [PATCH 0749/2442] Add new extension for formatting time durations --- .../NonVisual/TimeDisplayExtensionTest.cs | 41 +++++++++++++++ .../Extensions/EditorDisplayExtensions.cs | 26 ---------- osu.Game/Extensions/TimeDisplayExtensions.cs | 51 +++++++++++++++++++ 3 files changed, 92 insertions(+), 26 deletions(-) create mode 100644 osu.Game.Tests/NonVisual/TimeDisplayExtensionTest.cs delete mode 100644 osu.Game/Extensions/EditorDisplayExtensions.cs create mode 100644 osu.Game/Extensions/TimeDisplayExtensions.cs diff --git a/osu.Game.Tests/NonVisual/TimeDisplayExtensionTest.cs b/osu.Game.Tests/NonVisual/TimeDisplayExtensionTest.cs new file mode 100644 index 0000000000..97d7880def --- /dev/null +++ b/osu.Game.Tests/NonVisual/TimeDisplayExtensionTest.cs @@ -0,0 +1,41 @@ +// 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 NUnit.Framework; +using osu.Game.Extensions; + +namespace osu.Game.Tests.NonVisual +{ + [TestFixture] + public class TimeDisplayExtensionTest + { + private static readonly object[][] editor_formatted_duration_tests = + { + new object[] { new TimeSpan(0, 0, 0, 0, 50), "00:00:050" }, + new object[] { new TimeSpan(0, 0, 0, 10, 50), "00:10:050" }, + new object[] { new TimeSpan(0, 0, 5, 10), "05:10:000" }, + new object[] { new TimeSpan(0, 1, 5, 10), "65:10:000" }, + }; + + [TestCaseSource(nameof(editor_formatted_duration_tests))] + public void TestEditorFormat(TimeSpan input, string expectedOutput) + { + Assert.AreEqual(expectedOutput, input.ToEditorFormattedString()); + } + + private static readonly object[][] formatted_duration_tests = + { + new object[] { new TimeSpan(0, 0, 10), "00:10" }, + new object[] { new TimeSpan(0, 5, 10), "05:10" }, + new object[] { new TimeSpan(1, 5, 10), "01:05:10" }, + new object[] { new TimeSpan(1, 1, 5, 10), "01:01:05:10" }, + }; + + [TestCaseSource(nameof(formatted_duration_tests))] + public void TestFormattedDuration(TimeSpan input, string expectedOutput) + { + Assert.AreEqual(expectedOutput, input.ToFormattedDuration().ToString()); + } + } +} diff --git a/osu.Game/Extensions/EditorDisplayExtensions.cs b/osu.Game/Extensions/EditorDisplayExtensions.cs deleted file mode 100644 index f749b88b46..0000000000 --- a/osu.Game/Extensions/EditorDisplayExtensions.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; - -namespace osu.Game.Extensions -{ - public static class EditorDisplayExtensions - { - /// - /// Get an editor formatted string (mm:ss:mss) - /// - /// A time value in milliseconds. - /// An editor formatted display string. - public static string ToEditorFormattedString(this double milliseconds) => - ToEditorFormattedString(TimeSpan.FromMilliseconds(milliseconds)); - - /// - /// Get an editor formatted string (mm:ss:mss) - /// - /// A time value. - /// An editor formatted display string. - public static string ToEditorFormattedString(this TimeSpan timeSpan) => - $"{(timeSpan < TimeSpan.Zero ? "-" : string.Empty)}{timeSpan:mm\\:ss\\:fff}"; - } -} diff --git a/osu.Game/Extensions/TimeDisplayExtensions.cs b/osu.Game/Extensions/TimeDisplayExtensions.cs new file mode 100644 index 0000000000..821686a349 --- /dev/null +++ b/osu.Game/Extensions/TimeDisplayExtensions.cs @@ -0,0 +1,51 @@ +// 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 osu.Framework.Localisation; + +namespace osu.Game.Extensions +{ + public static class TimeDisplayExtensions + { + /// + /// Get an editor formatted string (mm:ss:mss) + /// + /// A time value in milliseconds. + /// An editor formatted display string. + public static string ToEditorFormattedString(this double milliseconds) => + ToEditorFormattedString(TimeSpan.FromMilliseconds(milliseconds)); + + /// + /// Get an editor formatted string (mm:ss:mss) + /// + /// A time value. + /// An editor formatted display string. + public static string ToEditorFormattedString(this TimeSpan timeSpan) => + $"{(timeSpan < TimeSpan.Zero ? "-" : string.Empty)}{(int)timeSpan.TotalMinutes:00}:{timeSpan:ss\\:fff}"; + + /// + /// Get a formatted duration (mm:ss or HH:mm:ss if more than an hour). + /// + /// A duration in milliseconds. + /// A formatted duration string. + public static LocalisableString ToFormattedDuration(this double milliseconds) => + ToFormattedDuration(TimeSpan.FromMilliseconds(milliseconds)); + + /// + /// Get a formatted duration (dd:hh:mm:ss with days/hours omitted if zero). + /// + /// A duration value. + /// A formatted duration string. + public static LocalisableString ToFormattedDuration(this TimeSpan timeSpan) + { + if (timeSpan.TotalDays >= 1) + return new LocalisableFormattableString(timeSpan, @"dd\:hh\:mm\:ss"); + + if (timeSpan.TotalHours >= 1) + return new LocalisableFormattableString(timeSpan, @"hh\:mm\:ss"); + + return new LocalisableFormattableString(timeSpan, @"mm\:ss"); + } + } +} From 081dafc4e4682b8595808696acdaace63f5ce060 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 31 Jul 2021 16:46:02 +0900 Subject: [PATCH 0750/2442] Update existing inline usages to use new extension method --- osu.Game.Tournament/Components/SongBar.cs | 6 +++--- osu.Game/Overlays/BeatmapSet/BasicStats.cs | 3 ++- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index cafec0a88b..6080f7b636 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.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 osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -11,6 +10,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; +using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Rulesets; using osu.Game.Screens.Menu; @@ -198,8 +198,8 @@ namespace osu.Game.Tournament.Components Direction = FillDirection.Vertical, Children = new Drawable[] { - new DiffPiece(("Length", TimeSpan.FromMilliseconds(length).ToString(@"mm\:ss"))), - new DiffPiece(("BPM", $"{bpm:0.#}")) + new DiffPiece(("Length", length.ToFormattedDuration().ToString())), + new DiffPiece(("BPM", $"{bpm:0.#}")), } }, new Container diff --git a/osu.Game/Overlays/BeatmapSet/BasicStats.cs b/osu.Game/Overlays/BeatmapSet/BasicStats.cs index b81c60a5b9..3f1034759e 100644 --- a/osu.Game/Overlays/BeatmapSet/BasicStats.cs +++ b/osu.Game/Overlays/BeatmapSet/BasicStats.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Beatmaps; +using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK; @@ -62,7 +63,7 @@ namespace osu.Game.Overlays.BeatmapSet } else { - length.Value = TimeSpan.FromMilliseconds(beatmap.Length).ToString(@"m\:ss"); + length.Value = TimeSpan.FromMilliseconds(beatmap.Length).ToFormattedDuration(); circleCount.Value = beatmap.OnlineInfo.CircleCount.ToString(); sliderCount.Value = beatmap.OnlineInfo.SliderCount.ToString(); } diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 4a35202df2..763c27bcbb 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -24,6 +24,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Framework.Logging; using osu.Game.Configuration; +using osu.Game.Extensions; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; @@ -333,7 +334,7 @@ namespace osu.Game.Screens.Select { Name = "Length", CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Length), - Content = TimeSpan.FromMilliseconds(beatmap.BeatmapInfo.Length).ToString(@"m\:ss"), + Content = beatmap.BeatmapInfo.Length.ToFormattedDuration().ToString(), }), bpmLabelContainer = new Container { From 7a44ddb36baeafdab1fae0637571fab793ad145a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 31 Jul 2021 16:48:43 +0900 Subject: [PATCH 0751/2442] Update incorrect xmldoc --- osu.Game/Extensions/TimeDisplayExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Extensions/TimeDisplayExtensions.cs b/osu.Game/Extensions/TimeDisplayExtensions.cs index 821686a349..dc05482a05 100644 --- a/osu.Game/Extensions/TimeDisplayExtensions.cs +++ b/osu.Game/Extensions/TimeDisplayExtensions.cs @@ -25,7 +25,7 @@ namespace osu.Game.Extensions $"{(timeSpan < TimeSpan.Zero ? "-" : string.Empty)}{(int)timeSpan.TotalMinutes:00}:{timeSpan:ss\\:fff}"; /// - /// Get a formatted duration (mm:ss or HH:mm:ss if more than an hour). + /// Get a formatted duration (dd:hh:mm:ss with days/hours omitted if zero). /// /// A duration in milliseconds. /// A formatted duration string. From 73393a5a0d102795f1f895e7f49b6ae2cda609e9 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 31 Jul 2021 15:56:25 +0200 Subject: [PATCH 0752/2442] Localise weighting percentage. --- .../Profile/Sections/Ranks/DrawableProfileWeightedScore.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs index 63305d004c..4e4a665a60 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Resources.Localisation.Web; @@ -52,7 +53,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks new OsuSpriteText { Font = OsuFont.GetFont(size: 12), - Text = UsersStrings.ShowExtraTopRanksPpWeight(weight.ToString("0%")) + Text = UsersStrings.ShowExtraTopRanksPpWeight(weight.ToLocalisableString("0%")) } } }; From 5b5cf30cbd5b862496a26453b6edb5ee69281d1c Mon Sep 17 00:00:00 2001 From: 02Naitsirk <57512128+02Naitsirk@users.noreply.github.com> Date: Sat, 31 Jul 2021 12:23:03 -0400 Subject: [PATCH 0753/2442] Fix incorrect performance formula MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 2f0b7da789..299d8b8970 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -35,8 +35,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; - double baseAimPerformance = Math.Pow(5 * Math.Max(1, aimRating) - 4, 3) / 100000; - double baseSpeedPerformance = Math.Pow(5 * Math.Max(1, speedRating) - 4, 3) / 100000; + double baseAimPerformance = Math.Pow(5 * Math.Max(1, aimRating / 0.0675) - 4, 3) / 100000; + double baseSpeedPerformance = Math.Pow(5 * Math.Max(1, speedRating / 0.0675) - 4, 3) / 100000; double basePerformance = Math.Pow(Math.Pow(baseAimPerformance, 1.1) + Math.Pow(baseSpeedPerformance, 1.1), 1 / 1.1); double starRating = basePerformance > 0.00001 ? 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0; From 9a7537cd569c9401efbcda2eca04b4dcbca166a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 11 Jul 2021 19:48:21 +0200 Subject: [PATCH 0754/2442] Add support for adding new colours to palette --- .../Graphics/UserInterfaceV2/ColourPalette.cs | 131 ++++++++++++------ 1 file changed, 91 insertions(+), 40 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs index d8edd00c16..1956eb045b 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs @@ -1,12 +1,17 @@ // 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.Collections.Specialized; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osuTK; @@ -36,36 +41,24 @@ namespace osu.Game.Graphics.UserInterfaceV2 } } - private FillFlowContainer palette; - private Container placeholder; + private FillFlowContainer palette; + + private IEnumerable colourDisplays => palette.OfType(); [BackgroundDependencyLoader] private void load() { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; + AutoSizeDuration = fade_duration; + AutoSizeEasing = Easing.OutQuint; - InternalChildren = new Drawable[] + InternalChild = palette = new FillFlowContainer { - palette = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(10), - Direction = FillDirection.Full - }, - placeholder = new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Child = new OsuSpriteText - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Text = "(none)", - Font = OsuFont.Default.With(weight: FontWeight.Bold) - } - } + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(10), + Direction = FillDirection.Full }; } @@ -73,30 +66,20 @@ namespace osu.Game.Graphics.UserInterfaceV2 { base.LoadComplete(); - Colours.BindCollectionChanged((_, args) => updatePalette(args), true); + Colours.BindCollectionChanged((_, args) => + { + if (args.Action != NotifyCollectionChangedAction.Replace) + updatePalette(); + }, true); FinishTransforms(true); } private const int fade_duration = 200; - private void updatePalette(NotifyCollectionChangedEventArgs args) + private void updatePalette() { - if (args.Action == NotifyCollectionChangedAction.Replace) - return; - palette.Clear(); - if (Colours.Any()) - { - palette.FadeIn(fade_duration, Easing.OutQuint); - placeholder.FadeOut(fade_duration, Easing.OutQuint); - } - else - { - palette.FadeOut(fade_duration, Easing.OutQuint); - placeholder.FadeIn(fade_duration, Easing.OutQuint); - } - for (int i = 0; i < Colours.Count; ++i) { // copy to avoid accesses to modified closure. @@ -111,6 +94,11 @@ namespace osu.Game.Graphics.UserInterfaceV2 display.Current.BindValueChanged(colour => Colours[colourIndex] = colour.NewValue); } + palette.Add(new AddColourButton + { + Action = () => Colours.Add(Colour4.White) + }); + reindexItems(); } @@ -118,11 +106,74 @@ namespace osu.Game.Graphics.UserInterfaceV2 { int index = 1; - foreach (var colour in palette) + foreach (var colourDisplay in colourDisplays) { - colour.ColourName = $"{colourNamePrefix} {index}"; + colourDisplay.ColourName = $"{colourNamePrefix} {index}"; index += 1; } } + + private class AddColourButton : CompositeDrawable + { + public Action Action + { + set => circularButton.Action = value; + } + + private readonly OsuClickableContainer circularButton; + + public AddColourButton() + { + AutoSizeAxes = Axes.Y; + Width = 100; + + InternalChild = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10), + Children = new Drawable[] + { + circularButton = new OsuClickableContainer + { + RelativeSizeAxes = Axes.X, + Height = 100, + CornerRadius = 50, + Masking = true, + BorderThickness = 5, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Colour4.Transparent, + AlwaysPresent = true + }, + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(20), + Icon = FontAwesome.Solid.Plus + } + } + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = "New" + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + circularButton.BorderColour = colours.BlueDarker; + } + } } } From 3f005886d66e1c4c19e7fd60e5589861ec616cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 11 Jul 2021 20:02:39 +0200 Subject: [PATCH 0755/2442] Add support for removing colours from palette --- .../TestSceneLabelledColourPalette.cs | 19 ++-- .../Graphics/UserInterfaceV2/ColourDisplay.cs | 103 +++++++++++------- .../Graphics/UserInterfaceV2/ColourPalette.cs | 3 + 3 files changed, 81 insertions(+), 44 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs index d7af47a835..bbb5ebd0d9 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs @@ -5,6 +5,7 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Utils; +using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterfaceV2; using osuTK.Graphics; @@ -34,17 +35,21 @@ namespace osu.Game.Tests.Visual.UserInterface { AddStep("create component", () => { - Child = new Container + Child = new OsuContextMenuContainer { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Width = 500, - AutoSizeAxes = Axes.Y, - Child = component = new LabelledColourPalette + RelativeSizeAxes = Axes.Both, + Child = new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, - ColourNamePrefix = "My colour #" + Width = 500, + AutoSizeAxes = Axes.Y, + Child = component = new LabelledColourPalette + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + ColourNamePrefix = "My colour #" + } } }; diff --git a/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs b/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs index 25f89fdcaa..85e18a6865 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs @@ -1,6 +1,7 @@ // 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 osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; @@ -12,6 +13,7 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osuTK; namespace osu.Game.Graphics.UserInterfaceV2 @@ -19,12 +21,17 @@ namespace osu.Game.Graphics.UserInterfaceV2 /// /// A component which displays a colour along with related description text. /// - public class ColourDisplay : CompositeDrawable, IHasCurrentValue, IHasPopover + public class ColourDisplay : CompositeDrawable, IHasCurrentValue { + /// + /// Invoked when the user has requested the colour corresponding to this + /// to be removed from its palette. + /// + public event Action DeleteRequested; + private readonly BindableWithCurrent current = new BindableWithCurrent(); - private Box fill; - private OsuSpriteText colourHexCode; + private OsuClickableContainer colourCircle; private OsuSpriteText colourName; public Bindable Current @@ -63,26 +70,10 @@ namespace osu.Game.Graphics.UserInterfaceV2 Spacing = new Vector2(0, 10), Children = new Drawable[] { - new OsuClickableContainer + colourCircle = new ColourCircle { - RelativeSizeAxes = Axes.X, - Height = 100, - CornerRadius = 50, - Masking = true, - Children = new Drawable[] - { - fill = new Box - { - RelativeSizeAxes = Axes.Both - }, - colourHexCode = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.Default.With(size: 12) - } - }, - Action = this.ShowPopover + Current = { BindTarget = Current }, + DeleteRequested = () => DeleteRequested?.Invoke(this) }, colourName = new OsuSpriteText { @@ -93,26 +84,64 @@ namespace osu.Game.Graphics.UserInterfaceV2 }; } - protected override void LoadComplete() + private class ColourCircle : OsuClickableContainer, IHasPopover, IHasContextMenu { - base.LoadComplete(); + public Bindable Current { get; } = new Bindable(); - current.BindValueChanged(_ => updateColour(), true); - } + public Action DeleteRequested { get; set; } - private void updateColour() - { - fill.Colour = current.Value; - colourHexCode.Text = current.Value.ToHex(); - colourHexCode.Colour = OsuColour.ForegroundTextColourFor(current.Value); - } + private readonly Box fill; + private readonly OsuSpriteText colourHexCode; - public Popover GetPopover() => new OsuPopover(false) - { - Child = new OsuColourPicker + public ColourCircle() { - Current = { BindTarget = Current } + RelativeSizeAxes = Axes.X; + Height = 100; + CornerRadius = 50; + Masking = true; + Action = this.ShowPopover; + + Children = new Drawable[] + { + fill = new Box + { + RelativeSizeAxes = Axes.Both + }, + colourHexCode = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.Default.With(size: 12) + } + }; } - }; + + protected override void LoadComplete() + { + base.LoadComplete(); + + Current.BindValueChanged(_ => updateColour(), true); + } + + private void updateColour() + { + fill.Colour = Current.Value; + colourHexCode.Text = Current.Value.ToHex(); + colourHexCode.Colour = OsuColour.ForegroundTextColourFor(Current.Value); + } + + public Popover GetPopover() => new OsuPopover(false) + { + Child = new OsuColourPicker + { + Current = { BindTarget = Current } + } + }; + + public MenuItem[] ContextMenuItems => new MenuItem[] + { + new OsuMenuItem("Delete", MenuItemType.Destructive, () => DeleteRequested?.Invoke()) + }; + } } } diff --git a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs index 1956eb045b..02d03bf07e 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs @@ -92,6 +92,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 }); display.Current.BindValueChanged(colour => Colours[colourIndex] = colour.NewValue); + display.DeleteRequested += colourDeletionRequested; } palette.Add(new AddColourButton @@ -102,6 +103,8 @@ namespace osu.Game.Graphics.UserInterfaceV2 reindexItems(); } + private void colourDeletionRequested(ColourDisplay display) => Colours.RemoveAt(palette.IndexOf(display)); + private void reindexItems() { int index = 1; From 4334121e8ecff738618e04006e89f6dcd61ae549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 11 Jul 2021 20:14:42 +0200 Subject: [PATCH 0756/2442] Add testing for colour palette behaviour --- .../TestSceneLabelledColourPalette.cs | 53 ++++++++++++++++++- .../Graphics/UserInterfaceV2/ColourPalette.cs | 2 +- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs index bbb5ebd0d9..6fafb8f87a 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs @@ -1,17 +1,21 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Graphics.Cursor; +using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; using osuTK.Graphics; +using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface { - public class TestSceneLabelledColourPalette : OsuTestScene + public class TestSceneLabelledColourPalette : OsuManualInputManagerTestScene { private LabelledColourPalette component; @@ -31,6 +35,22 @@ namespace osu.Game.Tests.Visual.UserInterface }, 8); } + [Test] + public void TestUserInteractions() + { + createColourPalette(); + assertColourCount(4); + + clickAddColour(); + assertColourCount(5); + + deleteFirstColour(); + assertColourCount(4); + + clickFirstColour(); + AddAssert("colour picker spawned", () => this.ChildrenOfType().Any()); + } + private void createColourPalette(bool hasDescription = false) { AddStep("create component", () => @@ -71,5 +91,36 @@ namespace osu.Game.Tests.Visual.UserInterface RNG.NextSingle(), RNG.NextSingle(), 1); + + private void assertColourCount(int count) => AddAssert($"colour count is {count}", () => component.Colours.Count == count); + + private void clickAddColour() => AddStep("click new colour button", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().Single()); + InputManager.Click(MouseButton.Left); + }); + + private void clickFirstColour() => AddStep("click first colour", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().First()); + InputManager.Click(MouseButton.Left); + }); + + private void deleteFirstColour() + { + AddStep("right-click first colour", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().First()); + InputManager.Click(MouseButton.Right); + }); + + AddUntilStep("wait for menu", () => this.ChildrenOfType().Any()); + + AddStep("click delete", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().Single()); + InputManager.Click(MouseButton.Left); + }); + } } } diff --git a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs index 02d03bf07e..a966f61b74 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ColourPalette.cs @@ -116,7 +116,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 } } - private class AddColourButton : CompositeDrawable + internal class AddColourButton : CompositeDrawable { public Action Action { From 708b50fdba0016e950b1a4446e35534f50bf3720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 1 Aug 2021 00:11:56 +0200 Subject: [PATCH 0757/2442] Remove unused field --- osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs b/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs index 85e18a6865..5240df74a2 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ColourDisplay.cs @@ -31,7 +31,6 @@ namespace osu.Game.Graphics.UserInterfaceV2 private readonly BindableWithCurrent current = new BindableWithCurrent(); - private OsuClickableContainer colourCircle; private OsuSpriteText colourName; public Bindable Current @@ -70,7 +69,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 Spacing = new Vector2(0, 10), Children = new Drawable[] { - colourCircle = new ColourCircle + new ColourCircle { Current = { BindTarget = Current }, DeleteRequested = () => DeleteRequested?.Invoke(this) From f868a201f5347c607c65cf62e614faa8c268d9cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 1 Aug 2021 15:03:51 +0200 Subject: [PATCH 0758/2442] Ensure proxied judgement content is correctly depth-ordered --- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 6 +++++- osu.Game/Rulesets/Judgements/DrawableJudgement.cs | 8 ++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 8993a9b18a..c1c2ea2299 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Osu.UI private void onJudgementLoaded(DrawableOsuJudgement judgement) { - judgementAboveHitObjectLayer.Add(judgement.GetProxyAboveHitObjectsContent()); + judgementAboveHitObjectLayer.Add(judgement.ProxiedAboveHitObjectsContent); } [BackgroundDependencyLoader(true)] @@ -150,6 +150,10 @@ namespace osu.Game.Rulesets.Osu.UI DrawableOsuJudgement explosion = poolDictionary[result.Type].Get(doj => doj.Apply(result, judgedObject)); judgementLayer.Add(explosion); + + // the proxied content is added to judgementAboveHitObjectLayer once, on first load, and never removed from it. + // ensure that ordering is consistent with expectations (latest judgement should be front-most). + judgementAboveHitObjectLayer.ChangeChildDepth(explosion.ProxiedAboveHitObjectsContent, (float)-result.TimeAbsolute); } public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => HitObjectContainer.ReceivePositionalInputAt(screenSpacePos); diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index 0f22d35bb5..d25d46c6e2 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -1,6 +1,7 @@ // 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.Diagnostics; using JetBrains.Annotations; using osu.Framework.Allocation; @@ -31,6 +32,9 @@ namespace osu.Game.Rulesets.Judgements private readonly Container aboveHitObjectsContent; + private readonly Lazy proxiedAboveHitObjectsContent; + public Drawable ProxiedAboveHitObjectsContent => proxiedAboveHitObjectsContent.Value; + /// /// Creates a drawable which visualises a . /// @@ -52,6 +56,8 @@ namespace osu.Game.Rulesets.Judgements Depth = float.MinValue, RelativeSizeAxes = Axes.Both }); + + proxiedAboveHitObjectsContent = new Lazy(() => aboveHitObjectsContent.CreateProxy()); } [BackgroundDependencyLoader] @@ -60,8 +66,6 @@ namespace osu.Game.Rulesets.Judgements prepareDrawables(); } - public Drawable GetProxyAboveHitObjectsContent() => aboveHitObjectsContent.CreateProxy(); - /// /// Apply top-level animations to the current judgement when successfully hit. /// If displaying components which require lifetime extensions, manually adjusting is required. From ea2ef55a8bcc2867ae414e84c67857263cac8be4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 1 Aug 2021 15:27:05 +0200 Subject: [PATCH 0759/2442] Remove unnecessary whitespace --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 299d8b8970..b75bf81071 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; - + double baseAimPerformance = Math.Pow(5 * Math.Max(1, aimRating / 0.0675) - 4, 3) / 100000; double baseSpeedPerformance = Math.Pow(5 * Math.Max(1, speedRating / 0.0675) - 4, 3) / 100000; double basePerformance = Math.Pow(Math.Pow(baseAimPerformance, 1.1) + Math.Pow(baseSpeedPerformance, 1.1), 1 / 1.1); From c371e67d705df820ded3c66344a27cf3d1b07b20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 1 Aug 2021 15:32:10 +0200 Subject: [PATCH 0760/2442] Update diffcalc test to match ground-truth formula --- .../OsuDifficultyCalculatorTest.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index 8d8387378e..32fba0b70c 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -15,13 +15,13 @@ namespace osu.Game.Rulesets.Osu.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; - [TestCase(6.7568168283591499d, "diffcalc-test")] - [TestCase(1.0348244046058293d, "zero-length-sliders")] + [TestCase(6.4164199087433484d, "diffcalc-test")] + [TestCase(1.0028132594779837d, "zero-length-sliders")] public void Test(double expected, string name) => base.Test(expected, name); - [TestCase(8.4783236764532557d, "diffcalc-test")] - [TestCase(1.2708532136987165d, "zero-length-sliders")] + [TestCase(8.0749334911818469d, "diffcalc-test")] + [TestCase(1.2251606765323657d, "zero-length-sliders")] public void TestClockRateAdjusted(double expected, string name) => Test(expected, name, new OsuModDoubleTime()); From ac930b8918fcaa731965674cc6610fcb33f9a153 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 1 Aug 2021 19:16:30 +0300 Subject: [PATCH 0761/2442] Fix judgement processors provided to mods while not completely loaded --- .../Rulesets/Mods/IApplicableToHealthProcessor.cs | 2 +- .../Rulesets/Mods/IApplicableToScoreProcessor.cs | 2 +- osu.Game/Screens/Play/Player.cs | 14 ++++++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Mods/IApplicableToHealthProcessor.cs b/osu.Game/Rulesets/Mods/IApplicableToHealthProcessor.cs index a181955653..c12b215d41 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToHealthProcessor.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToHealthProcessor.cs @@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Mods public interface IApplicableToHealthProcessor : IApplicableMod { /// - /// Provide a to a mod. Called once on initialisation of a play instance. + /// Provides a ready to a mod. Called once on initialisation of a play instance. /// void ApplyToHealthProcessor(HealthProcessor healthProcessor); } diff --git a/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs b/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs index cb00770868..b93e50921f 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mods public interface IApplicableToScoreProcessor : IApplicableMod { /// - /// Provide a to a mod. Called once on initialisation of a play instance. + /// Provides a loaded to a mod. Called once on initialisation of a play instance. /// void ApplyToScoreProcessor(ScoreProcessor scoreProcessor); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index d76d44767a..db6ec1f4a9 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -297,11 +297,17 @@ namespace osu.Game.Screens.Play ScoreProcessor.HasCompleted.BindValueChanged(scoreCompletionChanged); HealthProcessor.Failed += onFail; - foreach (var mod in Mods.Value.OfType()) - mod.ApplyToScoreProcessor(ScoreProcessor); + ScoreProcessor.OnLoadComplete += _ => + { + foreach (var mod in Mods.Value.OfType()) + mod.ApplyToScoreProcessor(ScoreProcessor); + }; - foreach (var mod in Mods.Value.OfType()) - mod.ApplyToHealthProcessor(HealthProcessor); + HealthProcessor.OnLoadComplete += _ => + { + foreach (var mod in Mods.Value.OfType()) + mod.ApplyToHealthProcessor(HealthProcessor); + }; IsBreakTime.BindTo(breakTracker.IsBreakTime); IsBreakTime.BindValueChanged(onBreakTimeChanged, true); From f12e66052c99581040e8d25822e9a979e041bf84 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 1 Aug 2021 19:22:33 +0300 Subject: [PATCH 0762/2442] Reword outdated doc --- osu.Game/Rulesets/Mods/IApplicableToHealthProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/IApplicableToHealthProcessor.cs b/osu.Game/Rulesets/Mods/IApplicableToHealthProcessor.cs index c12b215d41..2676060efa 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToHealthProcessor.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToHealthProcessor.cs @@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Mods public interface IApplicableToHealthProcessor : IApplicableMod { /// - /// Provides a ready to a mod. Called once on initialisation of a play instance. + /// Provides a loaded to a mod. Called once on initialisation of a play instance. /// void ApplyToHealthProcessor(HealthProcessor healthProcessor); } From c93533fa2e14407baec6c761bf2bf4ca06eb641b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 17:04:24 +0000 Subject: [PATCH 0763/2442] Bump Xamarin.Essentials from 1.6.1 to 1.7.0 Bumps [Xamarin.Essentials](https://github.com/xamarin/Essentials) from 1.6.1 to 1.7.0. - [Release notes](https://github.com/xamarin/Essentials/releases) - [Commits](https://github.com/xamarin/Essentials/compare/1.6.1...1.7.0) --- updated-dependencies: - dependency-name: Xamarin.Essentials dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- osu.Android/osu.Android.csproj | 2 +- osu.iOS/osu.iOS.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj index 582c856a47..b2599535ae 100644 --- a/osu.Android/osu.Android.csproj +++ b/osu.Android/osu.Android.csproj @@ -64,7 +64,7 @@ - + \ No newline at end of file diff --git a/osu.iOS/osu.iOS.csproj b/osu.iOS/osu.iOS.csproj index 1cbe4422cc..1203c3659b 100644 --- a/osu.iOS/osu.iOS.csproj +++ b/osu.iOS/osu.iOS.csproj @@ -117,7 +117,7 @@ - + From 81f42da38678c1f3b6ebb8980f8d6ad4c759afb1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 17:04:30 +0000 Subject: [PATCH 0764/2442] Bump MessagePack from 2.2.113 to 2.3.75 Bumps [MessagePack](https://github.com/neuecc/MessagePack-CSharp) from 2.2.113 to 2.3.75. - [Release notes](https://github.com/neuecc/MessagePack-CSharp/releases) - [Changelog](https://github.com/neuecc/MessagePack-CSharp/blob/master/prepare_release.ps1) - [Commits](https://github.com/neuecc/MessagePack-CSharp/compare/v2.2.113...v2.3.75) --- updated-dependencies: - dependency-name: MessagePack dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- 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 936fe8db94..7ca52aaed5 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -22,7 +22,7 @@ - + From aadd1c67816b5affa75b6df49b4a955db4ee2a40 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Aug 2021 17:04:37 +0000 Subject: [PATCH 0765/2442] Bump Sentry from 3.8.2 to 3.8.3 Bumps [Sentry](https://github.com/getsentry/sentry-dotnet) from 3.8.2 to 3.8.3. - [Release notes](https://github.com/getsentry/sentry-dotnet/releases) - [Changelog](https://github.com/getsentry/sentry-dotnet/blob/main/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-dotnet/compare/3.8.2...3.8.3) --- updated-dependencies: - dependency-name: Sentry dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- 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 936fe8db94..03ae29a384 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -38,7 +38,7 @@ - + From 1e3173bf446bc16eb42bae1b8cdc64cd19c22b33 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 1 Aug 2021 20:12:35 +0300 Subject: [PATCH 0766/2442] Fix muted dim factor not considering "0 divided by 0" case --- osu.Game/Rulesets/Mods/ModMuted.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index 7fde14d6ca..c6a3a1a603 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.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.Linq; using osu.Framework.Audio; using osu.Framework.Audio.Track; @@ -89,7 +88,7 @@ namespace osu.Game.Rulesets.Mods currentCombo = scoreProcessor.Combo.GetBoundCopy(); currentCombo.BindValueChanged(combo => { - double dimFactor = Math.Min(1, (double)combo.NewValue / MuteComboCount.Value); + double dimFactor = MuteComboCount.Value == 0 ? 1 : (double)combo.NewValue / MuteComboCount.Value; if (InverseMuting.Value) dimFactor = 1 - dimFactor; From ce7987dac797f6721d9033aa503a0210cb7ccc55 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 1 Aug 2021 20:09:37 +0300 Subject: [PATCH 0767/2442] Clarify `0` final combo indicates always muted audio --- osu.Game/Rulesets/Mods/ModMuted.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index c6a3a1a603..87a2e2446e 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -7,7 +7,10 @@ using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Game.Configuration; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; @@ -40,7 +43,7 @@ namespace osu.Game.Rulesets.Mods Value = true }; - [SettingSource("Final volume at combo", "The combo count at which point the track reaches its final volume.")] + [SettingSource("Final volume at combo", "The combo count at which point the track reaches its final volume.", SettingControlType = typeof(SettingsSlider))] public BindableInt MuteComboCount { get; } = new BindableInt { Default = 100, @@ -100,4 +103,9 @@ namespace osu.Game.Rulesets.Mods public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank; } + + public class MuteComboSlider : OsuSliderBar + { + public override LocalisableString TooltipText => Current.Value == 0 ? "(always muted)" : base.TooltipText; + } } From 026c6325390fe7aa72a81b91b51a26ff836a4b7c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 1 Aug 2021 20:12:43 +0300 Subject: [PATCH 0768/2442] Add test coverage --- .../Mods/TestSceneOsuModMuted.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMuted.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMuted.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMuted.cs new file mode 100644 index 0000000000..30e234dd7a --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMuted.cs @@ -0,0 +1,28 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Testing; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; + +namespace osu.Game.Rulesets.Osu.Tests.Mods +{ + public class TestSceneOsuModMuted : OsuModTestScene + { + /// + /// Ensures that a final volume combo of 0 (i.e. "always muted" mode) constantly plays metronome and completely mutes track. + /// + [TestCase(0.0, 1.0)] + public void TestZeroFinalCombo(double expectedTrackVolume, double expectedMetronomeVolume) => CreateModTest(new ModTestData + { + Mod = new OsuModMuted + { + MuteComboCount = { Value = 0 }, + }, + PassCondition = () => Beatmap.Value.Track.AggregateVolume.Value == expectedTrackVolume && + Player.ChildrenOfType().SingleOrDefault()?.AggregateVolume.Value == expectedMetronomeVolume, + }); + } +} From a26e7b2680c7e2e53ad7fc0ba97f63ac6ea6791d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 1 Aug 2021 20:59:29 +0300 Subject: [PATCH 0769/2442] Limit combo count to minimum 1 when using inversed Avoids making the mod of no effect. --- osu.Game/Rulesets/Mods/ModMuted.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index 87a2e2446e..8991962349 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -66,6 +66,11 @@ namespace osu.Game.Rulesets.Mods Value = true }; + protected ModMuted() + { + InverseMuting.BindValueChanged(i => MuteComboCount.MinValue = i.NewValue ? 1 : 0, true); + } + public void ApplyToTrack(ITrack track) { track.AddAdjustment(AdjustableProperty.Volume, mainVolumeAdjust); From fb5ef7d2d28c0ce218bdc5a71bac8b542ce522c2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 1 Aug 2021 20:59:51 +0300 Subject: [PATCH 0770/2442] Remove brackets --- osu.Game/Rulesets/Mods/ModMuted.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index 8991962349..1d33b44812 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -111,6 +111,6 @@ namespace osu.Game.Rulesets.Mods public class MuteComboSlider : OsuSliderBar { - public override LocalisableString TooltipText => Current.Value == 0 ? "(always muted)" : base.TooltipText; + public override LocalisableString TooltipText => Current.Value == 0 ? "always muted" : base.TooltipText; } } From a75da8298686c180fe657a75899fc9838866251c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 1 Aug 2021 21:14:54 +0300 Subject: [PATCH 0771/2442] Add explaining comment --- osu.Game/Screens/Play/Player.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index db6ec1f4a9..09eaf1c543 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -297,6 +297,8 @@ namespace osu.Game.Screens.Play ScoreProcessor.HasCompleted.BindValueChanged(scoreCompletionChanged); HealthProcessor.Failed += onFail; + // Provide judgement processors to mods after they're loaded so that they're on the gameplay clock, + // this is required for mods that apply transforms to these processors. ScoreProcessor.OnLoadComplete += _ => { foreach (var mod in Mods.Value.OfType()) From df9b6182560909c892ffd8ed7a324facb2504ec4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Aug 2021 11:44:05 +0900 Subject: [PATCH 0772/2442] Add localisation license header to editorconfig --- osu.Game/.editorconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/.editorconfig b/osu.Game/.editorconfig index 46a3dafd04..4107d1bb35 100644 --- a/osu.Game/.editorconfig +++ b/osu.Game/.editorconfig @@ -1,2 +1,3 @@ [*.cs] -dotnet_diagnostic.OLOC001.prefix_namespace = osu.Game.Resources.Localisation \ No newline at end of file +dotnet_diagnostic.OLOC001.prefix_namespace = osu.Game.Resources.Localisation +dotnet_diagnostic.OLOC001.license_header = // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.\n// See the LICENCE file in the repository root for full licence text. \ No newline at end of file From 356c157d1f860e621c5fdc594b4c95050facf158 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Aug 2021 11:49:21 +0900 Subject: [PATCH 0773/2442] Add localisation options to sln's .editorconfig --- .editorconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.editorconfig b/.editorconfig index 19bd89c52f..ceaaf4e8f1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -190,3 +190,6 @@ dotnet_diagnostic.CA2225.severity = none # Banned APIs dotnet_diagnostic.RS0030.severity = error + +dotnet_diagnostic.OLOC001.prefix_namespace = osu.Game.Resources.Localisation +dotnet_diagnostic.OLOC001.license_header = // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.\n// See the LICENCE file in the repository root for full licence text. From cd0fb2f4360cc4e3aae2003e9319989f88e27765 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Aug 2021 11:52:10 +0900 Subject: [PATCH 0774/2442] Remove namespace from sln's .editorconfig --- .editorconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index ceaaf4e8f1..3c4997c88d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -191,5 +191,4 @@ dotnet_diagnostic.CA2225.severity = none # Banned APIs dotnet_diagnostic.RS0030.severity = error -dotnet_diagnostic.OLOC001.prefix_namespace = osu.Game.Resources.Localisation dotnet_diagnostic.OLOC001.license_header = // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.\n// See the LICENCE file in the repository root for full licence text. From 3e56388ba8ea4217f197cbb15ef78f216cbfb799 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Aug 2021 15:08:42 +0900 Subject: [PATCH 0775/2442] Match casing in enum value --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 2 +- osu.Game.Rulesets.Catch/UI/CatcherTrail.cs | 2 +- osu.Game.Rulesets.Catch/UI/CatcherTrailAnimation.cs | 2 +- osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 9422d6d51b..b30c3d82a4 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -118,7 +118,7 @@ namespace osu.Game.Rulesets.Catch.UI } if (!lastHyperDashState && Catcher.HyperDashing) - displayCatcherTrail(CatcherTrailAnimation.HyperDashAfterimage); + displayCatcherTrail(CatcherTrailAnimation.HyperDashAfterImage); if (Catcher.Dashing || Catcher.HyperDashing) { diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs index f3f996d2c5..6d2ac7e488 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrail.cs @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Catch.UI this.FadeTo(0.4f).FadeOut(800, Easing.OutQuint); break; - case CatcherTrailAnimation.HyperDashAfterimage: + case CatcherTrailAnimation.HyperDashAfterImage: this.MoveToOffset(new Vector2(0, -10), 1200, Easing.In); this.ScaleTo(Scale * 0.95f).ScaleTo(Scale * 1.2f, 1200, Easing.In); this.FadeOut(1200); diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailAnimation.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailAnimation.cs index 3ea99badc1..0a5281cd10 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailAnimation.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailAnimation.cs @@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Catch.UI { Dashing, HyperDashing, - HyperDashAfterimage + HyperDashAfterImage } } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs index f451f84c95..0f2530e56a 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs @@ -80,7 +80,7 @@ namespace osu.Game.Rulesets.Catch.UI hyperDashTrails.Add(drawable); break; - case CatcherTrailAnimation.HyperDashAfterimage: + case CatcherTrailAnimation.HyperDashAfterImage: hyperDashAfterImages.Add(drawable); break; } @@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Catch.UI hyperDashTrails.Remove(drawable); break; - case CatcherTrailAnimation.HyperDashAfterimage: + case CatcherTrailAnimation.HyperDashAfterImage: hyperDashAfterImages.Remove(drawable); break; } From e8338f2711ef20d3fc8ae16828d5f7b303583ad2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Jul 2021 18:59:02 +0900 Subject: [PATCH 0776/2442] Add basic class structure for match rulesets and required state --- .../Online/Multiplayer/IMultiplayerClient.cs | 13 +++++++++++++ .../Multiplayer/MatchRulesetRoomState.cs | 18 ++++++++++++++++++ .../Online/Multiplayer/MatchRulesetType.cs | 11 +++++++++++ .../Multiplayer/MatchRulesetUserState.cs | 18 ++++++++++++++++++ .../MatchRulesets/TeamVs/MultiplayerTeam.cs | 19 +++++++++++++++++++ .../TeamVs/TeamVsMatchRoomState.cs | 14 ++++++++++++++ .../TeamVs/TeamVsMatchUserState.cs | 13 +++++++++++++ .../Online/Multiplayer/MultiplayerClient.cs | 10 ++++++++++ .../Online/Multiplayer/MultiplayerRoom.cs | 3 +++ .../Multiplayer/MultiplayerRoomSettings.cs | 7 ++++++- .../Online/Multiplayer/MultiplayerRoomUser.cs | 3 +++ 11 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs create mode 100644 osu.Game/Online/Multiplayer/MatchRulesetType.cs create mode 100644 osu.Game/Online/Multiplayer/MatchRulesetUserState.cs create mode 100644 osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/MultiplayerTeam.cs create mode 100644 osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchRoomState.cs create mode 100644 osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchUserState.cs diff --git a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs index 6d7b9d24d6..cfe5af3393 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs @@ -50,6 +50,19 @@ namespace osu.Game.Online.Multiplayer /// The new state of the user. Task UserStateChanged(int userId, MultiplayerUserState state); + /// + /// Signals that the match ruleset state has changed for a user in this room. + /// + /// The ID of the user performing a state change. + /// The new state of the user. + Task MatchRulesetUserStateChanged(int userId, MatchRulesetUserState state); + + /// + /// Signals that the match ruleset state has changed for this room. + /// + /// The new state of the room. + Task MatchRulesetRoomStateChanged(MatchRulesetRoomState state); + /// /// Signals that a user in this room changed their beatmap availability state. /// diff --git a/osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs b/osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs new file mode 100644 index 0000000000..21bdec1b20 --- /dev/null +++ b/osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs @@ -0,0 +1,18 @@ +// 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 MessagePack; + +namespace osu.Game.Online.Multiplayer +{ + /// + /// Room-wide state for the current match ruleset. + /// Can be used to contain any state which should be used before or during match gameplay. + /// + [Serializable] + [MessagePackObject] + public abstract class MatchRulesetRoomState + { + } +} diff --git a/osu.Game/Online/Multiplayer/MatchRulesetType.cs b/osu.Game/Online/Multiplayer/MatchRulesetType.cs new file mode 100644 index 0000000000..b87a8eb174 --- /dev/null +++ b/osu.Game/Online/Multiplayer/MatchRulesetType.cs @@ -0,0 +1,11 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Online.Multiplayer +{ + public enum MatchRulesetType + { + HeadToHead, + TeamVs + } +} diff --git a/osu.Game/Online/Multiplayer/MatchRulesetUserState.cs b/osu.Game/Online/Multiplayer/MatchRulesetUserState.cs new file mode 100644 index 0000000000..647574b54a --- /dev/null +++ b/osu.Game/Online/Multiplayer/MatchRulesetUserState.cs @@ -0,0 +1,18 @@ +// 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 MessagePack; + +namespace osu.Game.Online.Multiplayer +{ + /// + /// User specific state for the current match ruleset. + /// Can be used to contain any state which should be used before or during match gameplay. + /// + [Serializable] + [MessagePackObject] + public abstract class MatchRulesetUserState + { + } +} diff --git a/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/MultiplayerTeam.cs b/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/MultiplayerTeam.cs new file mode 100644 index 0000000000..532111671e --- /dev/null +++ b/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/MultiplayerTeam.cs @@ -0,0 +1,19 @@ +// 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 MessagePack; + +namespace osu.Game.Online.Multiplayer.MatchRulesets.TeamVs +{ + [Serializable] + [MessagePackObject] + public class MultiplayerTeam + { + [Key(0)] + public int ID { get; set; } + + [Key(1)] + public string Name { get; set; } + } +} diff --git a/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchRoomState.cs b/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchRoomState.cs new file mode 100644 index 0000000000..6383617100 --- /dev/null +++ b/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchRoomState.cs @@ -0,0 +1,14 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using MessagePack; + +namespace osu.Game.Online.Multiplayer.MatchRulesets.TeamVs +{ + public class TeamVsMatchRoomState : MatchRulesetRoomState + { + [Key(0)] + public List Teams { get; set; } + } +} diff --git a/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchUserState.cs b/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchUserState.cs new file mode 100644 index 0000000000..b35cfd3cdd --- /dev/null +++ b/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchUserState.cs @@ -0,0 +1,13 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using MessagePack; + +namespace osu.Game.Online.Multiplayer.MatchRulesets.TeamVs +{ + public class TeamVsMatchUserState : MatchRulesetUserState + { + [Key(0)] + public int TeamID { get; set; } + } +} diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 9972d7e88d..fa0cbb1dff 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -420,6 +420,16 @@ namespace osu.Game.Online.Multiplayer return Task.CompletedTask; } + Task IMultiplayerClient.MatchRulesetUserStateChanged(int userId, MatchRulesetUserState state) + { + throw new NotImplementedException(); + } + + Task IMultiplayerClient.MatchRulesetRoomStateChanged(MatchRulesetRoomState state) + { + throw new NotImplementedException(); + } + Task IMultiplayerClient.UserBeatmapAvailabilityChanged(int userId, BeatmapAvailability beatmapAvailability) { if (Room == null) diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoom.cs b/osu.Game/Online/Multiplayer/MultiplayerRoom.cs index c5fa6253ed..19c0d3cb24 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoom.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoom.cs @@ -47,6 +47,9 @@ namespace osu.Game.Online.Multiplayer [Key(4)] public MultiplayerRoomUser? Host { get; set; } + [Key(5)] + public MatchRulesetRoomState? MatchRulesetState { get; set; } + [JsonConstructor] [SerializationConstructor] public MultiplayerRoom(long roomId) diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs index 4e94c5982f..2c49e3b251 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs @@ -39,6 +39,9 @@ namespace osu.Game.Online.Multiplayer [Key(7)] public string Password { get; set; } = string.Empty; + [Key(8)] + public MatchRulesetType MatchRulesetType { get; set; } + public bool Equals(MultiplayerRoomSettings other) => BeatmapID == other.BeatmapID && BeatmapChecksum == other.BeatmapChecksum @@ -47,7 +50,8 @@ namespace osu.Game.Online.Multiplayer && RulesetID == other.RulesetID && Password.Equals(other.Password, StringComparison.Ordinal) && Name.Equals(other.Name, StringComparison.Ordinal) - && PlaylistItemId == other.PlaylistItemId; + && PlaylistItemId == other.PlaylistItemId + && MatchRulesetType == other.MatchRulesetType; public override string ToString() => $"Name:{Name}" + $" Beatmap:{BeatmapID} ({BeatmapChecksum})" @@ -55,6 +59,7 @@ namespace osu.Game.Online.Multiplayer + $" AllowedMods:{string.Join(',', AllowedMods)}" + $" Password:{(string.IsNullOrEmpty(Password) ? "no" : "yes")}" + $" Ruleset:{RulesetID}" + + $" MatchRuleset:{MatchRulesetType}" + $" Item:{PlaylistItemId}"; } } diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs index a49a8f083c..d97d49aa8a 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs @@ -36,6 +36,9 @@ namespace osu.Game.Online.Multiplayer [Key(3)] public IEnumerable Mods { get; set; } = Enumerable.Empty(); + [Key(4)] + public MatchRulesetUserState? MatchRulesetState { get; set; } + [IgnoreMember] public User? User { get; set; } From 9d1e95caf01b548f80dfe59d11f5a2576fe40ca2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 21 Jul 2021 19:13:56 +0900 Subject: [PATCH 0777/2442] Add flow for sending match ruleset specific messages to the server --- .../Multiplayer/IMultiplayerRoomServer.cs | 6 ++++++ .../Multiplayer/MatchRulesetUserRequest.cs | 17 +++++++++++++++++ .../MatchRulesets/TeamVs/ChangeTeamRequest.cs | 13 +++++++++++++ .../Online/Multiplayer/MultiplayerClient.cs | 2 ++ .../Multiplayer/OnlineMultiplayerClient.cs | 8 ++++++++ .../Visual/Multiplayer/TestMultiplayerClient.cs | 2 ++ 6 files changed, 48 insertions(+) create mode 100644 osu.Game/Online/Multiplayer/MatchRulesetUserRequest.cs create mode 100644 osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/ChangeTeamRequest.cs diff --git a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs index 3527ce6314..20c9f9e216 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs @@ -55,6 +55,12 @@ namespace osu.Game.Online.Multiplayer /// The proposed new mods, excluding any required by the room itself. Task ChangeUserMods(IEnumerable newMods); + /// + /// Send a match ruleset specific request. + /// + /// The request to send. + Task SendMatchRulesetRequest(MatchRulesetUserRequest request); + /// /// As the host of a room, start the match. /// diff --git a/osu.Game/Online/Multiplayer/MatchRulesetUserRequest.cs b/osu.Game/Online/Multiplayer/MatchRulesetUserRequest.cs new file mode 100644 index 0000000000..3cc430a2b7 --- /dev/null +++ b/osu.Game/Online/Multiplayer/MatchRulesetUserRequest.cs @@ -0,0 +1,17 @@ +// 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 MessagePack; + +namespace osu.Game.Online.Multiplayer +{ + /// + /// A request from a user to perform an action specific to the current match ruleset. + /// + [Serializable] + [MessagePackObject] + public abstract class MatchRulesetUserRequest + { + } +} diff --git a/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/ChangeTeamRequest.cs b/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/ChangeTeamRequest.cs new file mode 100644 index 0000000000..9dd01ea87c --- /dev/null +++ b/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/ChangeTeamRequest.cs @@ -0,0 +1,13 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using MessagePack; + +namespace osu.Game.Online.Multiplayer.MatchRulesets.TeamVs +{ + public class ChangeTeamRequest : MatchRulesetUserRequest + { + [Key(0)] + public int TeamID { get; set; } + } +} diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index fa0cbb1dff..299da2e06e 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -293,6 +293,8 @@ namespace osu.Game.Online.Multiplayer public abstract Task ChangeUserMods(IEnumerable newMods); + public abstract Task SendMatchRulesetRequest(MatchRulesetUserRequest request); + public abstract Task StartMatch(); Task IMultiplayerClient.RoomStateChanged(MultiplayerRoomState state) diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs index 726e26ebe1..88d8c04c98 100644 --- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs @@ -118,6 +118,14 @@ namespace osu.Game.Online.Multiplayer return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeUserMods), newMods); } + public override Task SendMatchRulesetRequest(MatchRulesetUserRequest request) + { + if (!IsConnected.Value) + return Task.CompletedTask; + + return connection.InvokeAsync(nameof(IMultiplayerServer.SendMatchRulesetRequest), request); + } + public override Task StartMatch() { if (!IsConnected.Value) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 3349d670c8..6a8bf87ff0 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -192,6 +192,8 @@ namespace osu.Game.Tests.Visual.Multiplayer return Task.CompletedTask; } + public override Task SendMatchRulesetRequest(MatchRulesetUserRequest request) => Task.CompletedTask; + public override Task StartMatch() { Debug.Assert(Room != null); From 1c125eef121ebfda79bf7069765092433e242eff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 23 Jul 2021 18:16:53 +0900 Subject: [PATCH 0778/2442] Make `Users` an `IList` for more flexibility server-side --- osu.Game/Online/Multiplayer/MultiplayerRoom.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoom.cs b/osu.Game/Online/Multiplayer/MultiplayerRoom.cs index 19c0d3cb24..07d87fafc3 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoom.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoom.cs @@ -39,7 +39,7 @@ namespace osu.Game.Online.Multiplayer /// All users currently in this room. /// [Key(3)] - public List Users { get; set; } = new List(); + public IList Users { get; set; } = new List(); /// /// The host of this room, in control of changing room settings. From 1d645d4ca99b6c8787ab53a7ba9e2d7adc62a5c0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 23 Jul 2021 19:48:52 +0900 Subject: [PATCH 0779/2442] Mark base classes non-abstract to fix messagepack serialisation --- osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs | 2 +- osu.Game/Online/Multiplayer/MatchRulesetUserState.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs b/osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs index 21bdec1b20..d8072efa2a 100644 --- a/osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs +++ b/osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs @@ -12,7 +12,7 @@ namespace osu.Game.Online.Multiplayer /// [Serializable] [MessagePackObject] - public abstract class MatchRulesetRoomState + public class MatchRulesetRoomState { } } diff --git a/osu.Game/Online/Multiplayer/MatchRulesetUserState.cs b/osu.Game/Online/Multiplayer/MatchRulesetUserState.cs index 647574b54a..e8d9961dbd 100644 --- a/osu.Game/Online/Multiplayer/MatchRulesetUserState.cs +++ b/osu.Game/Online/Multiplayer/MatchRulesetUserState.cs @@ -12,7 +12,7 @@ namespace osu.Game.Online.Multiplayer /// [Serializable] [MessagePackObject] - public abstract class MatchRulesetUserState + public class MatchRulesetUserState { } } From d17b2b3268982af8801539dcdaf5bd6390a09c4c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Jul 2021 17:38:08 +0900 Subject: [PATCH 0780/2442] Add boilerplate for server events --- .../Online/Multiplayer/IMultiplayerClient.cs | 6 ++++++ .../Multiplayer/MatchRulesetServerEvent.cs | 17 +++++++++++++++++ .../Online/Multiplayer/MultiplayerClient.cs | 5 +++++ 3 files changed, 28 insertions(+) create mode 100644 osu.Game/Online/Multiplayer/MatchRulesetServerEvent.cs diff --git a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs index cfe5af3393..839ec9bd1f 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs @@ -63,6 +63,12 @@ namespace osu.Game.Online.Multiplayer /// The new state of the room. Task MatchRulesetRoomStateChanged(MatchRulesetRoomState state); + /// + /// Send a match ruleset specific request. + /// + /// The event to handle. + Task MatchRulesetEvent(MatchRulesetServerEvent e); + /// /// Signals that a user in this room changed their beatmap availability state. /// diff --git a/osu.Game/Online/Multiplayer/MatchRulesetServerEvent.cs b/osu.Game/Online/Multiplayer/MatchRulesetServerEvent.cs new file mode 100644 index 0000000000..c45615e914 --- /dev/null +++ b/osu.Game/Online/Multiplayer/MatchRulesetServerEvent.cs @@ -0,0 +1,17 @@ +// 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 MessagePack; + +namespace osu.Game.Online.Multiplayer +{ + /// + /// An event from the server to allow clients to update gameplay to an expected state. + /// + [Serializable] + [MessagePackObject] + public abstract class MatchRulesetServerEvent + { + } +} diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 299da2e06e..225d86ed9c 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -432,6 +432,11 @@ namespace osu.Game.Online.Multiplayer throw new NotImplementedException(); } + public Task MatchRulesetEvent(MatchRulesetServerEvent e) + { + throw new NotImplementedException(); + } + Task IMultiplayerClient.UserBeatmapAvailabilityChanged(int userId, BeatmapAvailability beatmapAvailability) { if (Room == null) From 035dfd071fb720af2245e478ea1a7d2c8baef821 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 27 Jul 2021 18:55:04 +0900 Subject: [PATCH 0781/2442] Add missing nullable specifications --- osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs | 2 ++ osu.Game/Online/Multiplayer/MatchRulesetUserState.cs | 2 ++ .../Multiplayer/MatchRulesets/TeamVs/ChangeTeamRequest.cs | 2 ++ .../Multiplayer/MatchRulesets/TeamVs/MultiplayerTeam.cs | 4 +++- .../Multiplayer/MatchRulesets/TeamVs/TeamVsMatchRoomState.cs | 4 +++- .../Multiplayer/MatchRulesets/TeamVs/TeamVsMatchUserState.cs | 2 ++ 6 files changed, 14 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs b/osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs index d8072efa2a..be8e54d341 100644 --- a/osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs +++ b/osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs @@ -4,6 +4,8 @@ using System; using MessagePack; +#nullable enable + namespace osu.Game.Online.Multiplayer { /// diff --git a/osu.Game/Online/Multiplayer/MatchRulesetUserState.cs b/osu.Game/Online/Multiplayer/MatchRulesetUserState.cs index e8d9961dbd..6fa0024c3d 100644 --- a/osu.Game/Online/Multiplayer/MatchRulesetUserState.cs +++ b/osu.Game/Online/Multiplayer/MatchRulesetUserState.cs @@ -4,6 +4,8 @@ using System; using MessagePack; +#nullable enable + namespace osu.Game.Online.Multiplayer { /// diff --git a/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/ChangeTeamRequest.cs b/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/ChangeTeamRequest.cs index 9dd01ea87c..5e1d489d39 100644 --- a/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/ChangeTeamRequest.cs +++ b/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/ChangeTeamRequest.cs @@ -3,6 +3,8 @@ using MessagePack; +#nullable enable + namespace osu.Game.Online.Multiplayer.MatchRulesets.TeamVs { public class ChangeTeamRequest : MatchRulesetUserRequest diff --git a/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/MultiplayerTeam.cs b/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/MultiplayerTeam.cs index 532111671e..f6bf893818 100644 --- a/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/MultiplayerTeam.cs +++ b/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/MultiplayerTeam.cs @@ -4,6 +4,8 @@ using System; using MessagePack; +#nullable enable + namespace osu.Game.Online.Multiplayer.MatchRulesets.TeamVs { [Serializable] @@ -14,6 +16,6 @@ namespace osu.Game.Online.Multiplayer.MatchRulesets.TeamVs public int ID { get; set; } [Key(1)] - public string Name { get; set; } + public string Name { get; set; } = string.Empty; } } diff --git a/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchRoomState.cs b/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchRoomState.cs index 6383617100..57aae6e534 100644 --- a/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchRoomState.cs +++ b/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchRoomState.cs @@ -4,11 +4,13 @@ using System.Collections.Generic; using MessagePack; +#nullable enable + namespace osu.Game.Online.Multiplayer.MatchRulesets.TeamVs { public class TeamVsMatchRoomState : MatchRulesetRoomState { [Key(0)] - public List Teams { get; set; } + public List Teams { get; set; } = new List(); } } diff --git a/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchUserState.cs b/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchUserState.cs index b35cfd3cdd..0880787a5a 100644 --- a/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchUserState.cs +++ b/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchUserState.cs @@ -3,6 +3,8 @@ using MessagePack; +#nullable enable + namespace osu.Game.Online.Multiplayer.MatchRulesets.TeamVs { public class TeamVsMatchUserState : MatchRulesetUserState From c023ce78d79a1dad4da6fc6138cdb02940237691 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Aug 2021 18:48:32 +0900 Subject: [PATCH 0782/2442] Match osu!stable taiko playfield size at 16:9 --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 46dafc3a30..0d9e08b8b7 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Taiko.UI /// /// Default height of a when inside a . /// - public const float DEFAULT_HEIGHT = 178; + public const float DEFAULT_HEIGHT = 212; private Container hitExplosionContainer; private Container kiaiExplosionContainer; From 867426441e98dbcd9a3af0cba8ec1545b1e00258 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 17:58:50 +0900 Subject: [PATCH 0783/2442] Fix weird access to room via `Client` --- .../OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index baf9570209..2a40a61257 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -99,7 +99,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match break; } - bool enableButton = Client.Room?.State == MultiplayerRoomState.Open && !operationInProgress.Value; + bool enableButton = Room?.State == MultiplayerRoomState.Open && !operationInProgress.Value; // When the local user is the host and spectating the match, the "start match" state should be enabled if any users are ready. if (localUser?.State == MultiplayerUserState.Spectating) From 359eb9c4ec81d700d53ae156a81d02272acd0415 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 18:10:38 +0900 Subject: [PATCH 0784/2442] Add new event flow for match ruleset state handling I'm totally not happy with how this is done, but don't have the energy to rewrite everything just now. --- .../Online/Multiplayer/MultiplayerClient.cs | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 225d86ed9c..deac736b80 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -424,17 +424,42 @@ namespace osu.Game.Online.Multiplayer Task IMultiplayerClient.MatchRulesetUserStateChanged(int userId, MatchRulesetUserState state) { - throw new NotImplementedException(); + if (Room == null) + return Task.CompletedTask; + + Scheduler.Add(() => + { + if (Room == null) + return; + + Room.Users.Single(u => u.UserID == userId).MatchRulesetState = state; + RoomUpdated?.Invoke(); + }, false); + + return Task.CompletedTask; } Task IMultiplayerClient.MatchRulesetRoomStateChanged(MatchRulesetRoomState state) { - throw new NotImplementedException(); + if (Room == null) + return Task.CompletedTask; + + Scheduler.Add(() => + { + if (Room == null) + return; + + Room.MatchRulesetState = state; + RoomUpdated?.Invoke(); + }); + + return Task.CompletedTask; } public Task MatchRulesetEvent(MatchRulesetServerEvent e) { - throw new NotImplementedException(); + // not used by any match rulesets just yet. + return Task.CompletedTask; } Task IMultiplayerClient.UserBeatmapAvailabilityChanged(int userId, BeatmapAvailability beatmapAvailability) From ce92a47ec689da8765d8b8d383ab8b94aef7d06c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 18:19:46 +0900 Subject: [PATCH 0785/2442] Add silly event handling hookups --- osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs index 88d8c04c98..8137430d8e 100644 --- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs @@ -56,6 +56,8 @@ namespace osu.Game.Online.Multiplayer connection.On(nameof(IMultiplayerClient.ResultsReady), ((IMultiplayerClient)this).ResultsReady); connection.On>(nameof(IMultiplayerClient.UserModsChanged), ((IMultiplayerClient)this).UserModsChanged); connection.On(nameof(IMultiplayerClient.UserBeatmapAvailabilityChanged), ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged); + connection.On(nameof(IMultiplayerClient.MatchRulesetRoomStateChanged), ((IMultiplayerClient)this).MatchRulesetRoomStateChanged); + connection.On(nameof(IMultiplayerClient.MatchRulesetUserStateChanged), ((IMultiplayerClient)this).MatchRulesetUserStateChanged); }; IsConnected.BindTo(connector.IsConnected); From 4cf2c6188d3ecfd8b4e44a601941215cc78d9f82 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 19:37:47 +0900 Subject: [PATCH 0786/2442] Add union attributes for derived class deserialisation --- osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs | 4 +++- osu.Game/Online/Multiplayer/MatchRulesetUserState.cs | 4 +++- .../Multiplayer/MatchRulesets/TeamVs/TeamVsMatchRoomState.cs | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs b/osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs index be8e54d341..773c9a55e8 100644 --- a/osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs +++ b/osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs @@ -3,6 +3,7 @@ using System; using MessagePack; +using osu.Game.Online.Multiplayer.MatchRulesets.TeamVs; #nullable enable @@ -14,7 +15,8 @@ namespace osu.Game.Online.Multiplayer /// [Serializable] [MessagePackObject] - public class MatchRulesetRoomState + [Union(0, typeof(TeamVsMatchRoomState))] + public abstract class MatchRulesetRoomState { } } diff --git a/osu.Game/Online/Multiplayer/MatchRulesetUserState.cs b/osu.Game/Online/Multiplayer/MatchRulesetUserState.cs index 6fa0024c3d..36eae2447e 100644 --- a/osu.Game/Online/Multiplayer/MatchRulesetUserState.cs +++ b/osu.Game/Online/Multiplayer/MatchRulesetUserState.cs @@ -3,6 +3,7 @@ using System; using MessagePack; +using osu.Game.Online.Multiplayer.MatchRulesets.TeamVs; #nullable enable @@ -14,7 +15,8 @@ namespace osu.Game.Online.Multiplayer /// [Serializable] [MessagePackObject] - public class MatchRulesetUserState + [Union(0, typeof(TeamVsMatchUserState))] + public abstract class MatchRulesetUserState { } } diff --git a/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchRoomState.cs b/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchRoomState.cs index 57aae6e534..338b9c965d 100644 --- a/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchRoomState.cs +++ b/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchRoomState.cs @@ -8,6 +8,7 @@ using MessagePack; namespace osu.Game.Online.Multiplayer.MatchRulesets.TeamVs { + [MessagePackObject] public class TeamVsMatchRoomState : MatchRulesetRoomState { [Key(0)] From 1cd967b35129193c8adf37646118e854c375f8b5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Aug 2021 14:37:47 +0900 Subject: [PATCH 0787/2442] Add signalr json type handling specification --- osu.Game/Online/HubClientConnector.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/HubClientConnector.cs b/osu.Game/Online/HubClientConnector.cs index 90049a6501..3876c8bbe6 100644 --- a/osu.Game/Online/HubClientConnector.cs +++ b/osu.Game/Online/HubClientConnector.cs @@ -150,7 +150,16 @@ namespace osu.Game.Online { // eventually we will precompile resolvers for messagepack, but this isn't working currently // see https://github.com/neuecc/MessagePack-CSharp/issues/780#issuecomment-768794308. - builder.AddNewtonsoftJsonProtocol(options => { options.PayloadSerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; }); + builder.AddNewtonsoftJsonProtocol(options => + { + options.PayloadSerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + options.PayloadSerializerSettings = new JsonSerializerSettings + { + // TODO: This should only be required to be `TypeNameHandling.Auto`. + // See usage in osu-server-spectator for further documentation as to why this is required. + TypeNameHandling = TypeNameHandling.All + }; + }); } var newConnection = builder.Build(); From 617ff40de763228ed652828d67a14cf462a7e000 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Aug 2021 14:44:51 +0900 Subject: [PATCH 0788/2442] Add the ability to not use MessagePack when creating a `HubConnector` --- osu.Game/Online/API/APIAccess.cs | 4 ++-- osu.Game/Online/API/DummyAPIAccess.cs | 2 +- osu.Game/Online/API/IAPIProvider.cs | 3 ++- osu.Game/Online/HubClientConnector.cs | 7 +++++-- osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs | 4 +++- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index b35dfa11cb..f7a3f4602f 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -257,8 +257,8 @@ namespace osu.Game.Online.API this.password = password; } - public IHubClientConnector GetHubConnector(string clientName, string endpoint) => - new HubClientConnector(clientName, endpoint, this, versionHash); + public IHubClientConnector GetHubConnector(string clientName, string endpoint, bool preferMessagePack) => + new HubClientConnector(clientName, endpoint, this, versionHash, preferMessagePack); public RegistrationRequest.RegistrationRequestErrors CreateAccount(string email, string username, string password) { diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs index 52f2365165..1ba31db9fa 100644 --- a/osu.Game/Online/API/DummyAPIAccess.cs +++ b/osu.Game/Online/API/DummyAPIAccess.cs @@ -89,7 +89,7 @@ namespace osu.Game.Online.API state.Value = APIState.Offline; } - public IHubClientConnector GetHubConnector(string clientName, string endpoint) => null; + public IHubClientConnector GetHubConnector(string clientName, string endpoint, bool preferMessagePack) => null; public RegistrationRequest.RegistrationRequestErrors CreateAccount(string email, string username, string password) { diff --git a/osu.Game/Online/API/IAPIProvider.cs b/osu.Game/Online/API/IAPIProvider.cs index 3a77b9cfee..686df5c57c 100644 --- a/osu.Game/Online/API/IAPIProvider.cs +++ b/osu.Game/Online/API/IAPIProvider.cs @@ -102,7 +102,8 @@ namespace osu.Game.Online.API /// /// The name of the client this connector connects for, used for logging. /// The endpoint to the hub. - IHubClientConnector? GetHubConnector(string clientName, string endpoint); + /// + IHubClientConnector? GetHubConnector(string clientName, string endpoint, bool preferMessagePack = true); /// /// Create a new user account. This is a blocking operation. diff --git a/osu.Game/Online/HubClientConnector.cs b/osu.Game/Online/HubClientConnector.cs index 3876c8bbe6..6c6a217f54 100644 --- a/osu.Game/Online/HubClientConnector.cs +++ b/osu.Game/Online/HubClientConnector.cs @@ -26,6 +26,7 @@ namespace osu.Game.Online private readonly string clientName; private readonly string endpoint; private readonly string versionHash; + private readonly bool preferMessagePack; private readonly IAPIProvider api; /// @@ -51,12 +52,14 @@ namespace osu.Game.Online /// The endpoint to the hub. /// An API provider used to react to connection state changes. /// The hash representing the current game version, used for verification purposes. - public HubClientConnector(string clientName, string endpoint, IAPIProvider api, string versionHash) + /// Whether to use MessagePack for serialisation if available on this platform. + public HubClientConnector(string clientName, string endpoint, IAPIProvider api, string versionHash, bool preferMessagePack = true) { this.clientName = clientName; this.endpoint = endpoint; this.api = api; this.versionHash = versionHash; + this.preferMessagePack = preferMessagePack; apiState.BindTo(api.State); apiState.BindValueChanged(state => @@ -144,7 +147,7 @@ namespace osu.Game.Online options.Headers.Add("OsuVersionHash", versionHash); }); - if (RuntimeInfo.SupportsJIT) + if (RuntimeInfo.SupportsJIT && preferMessagePack) builder.AddMessagePackProtocol(); else { diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs index 8137430d8e..1dfc60312b 100644 --- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs @@ -37,7 +37,9 @@ namespace osu.Game.Online.Multiplayer [BackgroundDependencyLoader] private void load(IAPIProvider api) { - connector = api.GetHubConnector(nameof(OnlineMultiplayerClient), endpoint); + // Importantly, we are intentionally not using MessagePack here to correctly support derived class serialization. + // More information on the limitations / reasoning can be found in osu-server-spectator's initialisation code. + connector = api.GetHubConnector(nameof(OnlineMultiplayerClient), endpoint, false); if (connector != null) { From c7274355a4ca9cfccbf2d2bd493efb5bc4bf4bbf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Aug 2021 17:05:05 +0900 Subject: [PATCH 0789/2442] Remove `abstract` definitions from multiplayer states for now --- osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs | 2 +- osu.Game/Online/Multiplayer/MatchRulesetUserState.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs b/osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs index 773c9a55e8..105bb1c281 100644 --- a/osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs +++ b/osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs @@ -16,7 +16,7 @@ namespace osu.Game.Online.Multiplayer [Serializable] [MessagePackObject] [Union(0, typeof(TeamVsMatchRoomState))] - public abstract class MatchRulesetRoomState + public class MatchRulesetRoomState // TODO: this will need to be abstract or interface when/if we get messagepack working. for now it isn't as it breaks json serialisation. { } } diff --git a/osu.Game/Online/Multiplayer/MatchRulesetUserState.cs b/osu.Game/Online/Multiplayer/MatchRulesetUserState.cs index 36eae2447e..aa34cdab41 100644 --- a/osu.Game/Online/Multiplayer/MatchRulesetUserState.cs +++ b/osu.Game/Online/Multiplayer/MatchRulesetUserState.cs @@ -16,7 +16,7 @@ namespace osu.Game.Online.Multiplayer [Serializable] [MessagePackObject] [Union(0, typeof(TeamVsMatchUserState))] - public abstract class MatchRulesetUserState + public class MatchRulesetUserState // TODO: this will need to be abstract or interface when/if we get messagepack working. for now it isn't as it breaks json serialisation. { } } From d93421b9b895a138870bc50bd14cd33df175ebf4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Aug 2021 17:06:55 +0900 Subject: [PATCH 0790/2442] Expose a default `TeamVs` room state so it can be consumed by tests --- .../MatchRulesets/TeamVs/TeamVsMatchRoomState.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchRoomState.cs b/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchRoomState.cs index 338b9c965d..92f67e99bd 100644 --- a/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchRoomState.cs +++ b/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchRoomState.cs @@ -13,5 +13,15 @@ namespace osu.Game.Online.Multiplayer.MatchRulesets.TeamVs { [Key(0)] public List Teams { get; set; } = new List(); + + public static TeamVsMatchRoomState CreateDefault() => + new TeamVsMatchRoomState + { + Teams = + { + new MultiplayerTeam { ID = 0, Name = "Team Red" }, + new MultiplayerTeam { ID = 1, Name = "Team Blue" }, + } + }; } } From 2af827f9133336fa39772758dff92d9349914b8b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Aug 2021 19:37:45 +0900 Subject: [PATCH 0791/2442] Increase TimeRange max value --- osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs index 6ffdad211b..f8d5a6c5a9 100644 --- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs +++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.UI.Scrolling /// /// The maximum span of time that may be visible by the length of the scrolling axes. /// - private const double time_span_max = 10000; + private const double time_span_max = 20000; /// /// The step increase/decrease of the span of time visible by the length of the scrolling axes. From 9327bc169b9449828373786f2a00afbc069a2c31 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Aug 2021 19:39:30 +0900 Subject: [PATCH 0792/2442] Make TaikoModClassic use classic-like scroll speed --- .../Mods/TaikoModClassic.cs | 22 ++++++++++++++++++- .../UI/DrawableTaikoRuleset.cs | 5 ++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs index 5a4d18be98..6520517039 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs @@ -2,10 +2,30 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.UI; +using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Taiko.Mods { - public class TaikoModClassic : ModClassic + public class TaikoModClassic : ModClassic, IApplicableToDrawableRuleset, IUpdatableByPlayfield { + private DrawableTaikoRuleset drawableTaikoRuleset; + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + drawableTaikoRuleset = (DrawableTaikoRuleset)drawableRuleset; + } + + public void Update(Playfield playfield) + { + // Classic taiko scrolls at a constant 100px per 1000ms. More notes become visible as the playfield is lengthened. + const float scroll_rate = 10; + + // Since the time range will depend on a positional value, it is referenced to the x480 pixel space. + float ratio = drawableTaikoRuleset.DrawHeight / 480; + + drawableTaikoRuleset.TimeRange.Value = (playfield.HitObjectContainer.DrawWidth / ratio) * scroll_rate; + } } } diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index ed8e6859a2..650ce1f5a3 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects.Drawables; @@ -24,12 +25,14 @@ namespace osu.Game.Rulesets.Taiko.UI { public class DrawableTaikoRuleset : DrawableScrollingRuleset { - private SkinnableDrawable scroller; + public new BindableDouble TimeRange => base.TimeRange; protected override ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Overlapping; protected override bool UserScrollSpeedAdjustment => false; + private SkinnableDrawable scroller; + public DrawableTaikoRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) : base(ruleset, beatmap, mods) { From 455666ed94af154106caad62e3a5a5fe0316ace9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Aug 2021 20:18:01 +0900 Subject: [PATCH 0793/2442] Remove taiko HD mod 4:3 scaling --- .../Mods/TaikoModHidden.cs | 30 +------------------ 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs index 0fd3625a93..8406ab8505 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs @@ -17,18 +17,6 @@ namespace osu.Game.Rulesets.Taiko.Mods public override string Description => @"Beats fade out before you hit them!"; public override double ScoreMultiplier => 1.06; - /// - /// In osu-stable, the hit position is 160, so the active playfield is essentially 160 pixels shorter - /// than the actual screen width. The normalized playfield height is 480, so on a 4:3 screen the - /// playfield ratio of the active area up to the hit position will actually be (640 - 160) / 480 = 1. - /// For custom resolutions/aspect ratios (x:y), the screen width given the normalized height becomes 480 * x / y instead, - /// and the playfield ratio becomes (480 * x / y - 160) / 480 = x / y - 1/3. - /// This constant is equal to the playfield ratio on 4:3 screens divided by the playfield ratio on 16:9 screens. - /// - private const double hd_sv_scale = (4.0 / 3.0 - 1.0 / 3.0) / (16.0 / 9.0 - 1.0 / 3.0); - - private double originalSliderMultiplier; - private ControlPointInfo controlPointInfo; protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) @@ -41,7 +29,7 @@ namespace osu.Game.Rulesets.Taiko.Mods double beatLength = controlPointInfo.TimingPointAt(position).BeatLength; double speedMultiplier = controlPointInfo.DifficultyPointAt(position).SpeedMultiplier; - return originalSliderMultiplier * speedMultiplier * TimingControlPoint.DEFAULT_BEAT_LENGTH / beatLength; + return speedMultiplier * TimingControlPoint.DEFAULT_BEAT_LENGTH / beatLength; } protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) @@ -69,22 +57,6 @@ namespace osu.Game.Rulesets.Taiko.Mods } } - public void ReadFromDifficulty(BeatmapDifficulty difficulty) - { - } - - public void ApplyToDifficulty(BeatmapDifficulty difficulty) - { - // needs to be read after all processing has been run (TaikoBeatmapConverter applies an adjustment which would otherwise be omitted). - originalSliderMultiplier = difficulty.SliderMultiplier; - - // osu-stable has an added playfield cover that essentially forces a 4:3 playfield ratio, by cutting off all objects past that size. - // This is not yet implemented; instead a playfield adjustment container is present which maintains a 16:9 ratio. - // For now, increase the slider multiplier proportionally so that the notes stay on the screen for the same amount of time as on stable. - // Note that this means that the notes will scroll faster as they have a longer distance to travel on the screen in that same amount of time. - difficulty.SliderMultiplier /= hd_sv_scale; - } - public override void ApplyToBeatmap(IBeatmap beatmap) { controlPointInfo = beatmap.ControlPointInfo; From 8d999d30f6de986ac8758a9983a6c400606fb3b6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 2 Aug 2021 20:38:46 +0900 Subject: [PATCH 0794/2442] Remove interface definition --- osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs index 8406ab8505..a6b3fe1cd9 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs @@ -12,7 +12,7 @@ using osu.Game.Rulesets.Taiko.Objects.Drawables; namespace osu.Game.Rulesets.Taiko.Mods { - public class TaikoModHidden : ModHidden, IApplicableToDifficulty + public class TaikoModHidden : ModHidden { public override string Description => @"Beats fade out before you hit them!"; public override double ScoreMultiplier => 1.06; From 2564c0c3df5855beb76ca74b45b2501bd91202f3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Aug 2021 14:46:31 +0900 Subject: [PATCH 0795/2442] Rename `GameType` to `MatchType` and make `enum` instead of `class` --- osu.Game/Online/Rooms/GameType.cs | 18 --- .../Rooms/GameTypes/GameTypePlaylists.cs | 25 --- .../Online/Rooms/GameTypes/GameTypeTag.cs | 28 ---- .../Online/Rooms/GameTypes/GameTypeTagTeam.cs | 45 ------ .../Rooms/GameTypes/GameTypeTeamVersus.cs | 32 ---- .../Online/Rooms/GameTypes/GameTypeVersus.cs | 22 --- osu.Game/Online/Rooms/GameTypes/VersusRow.cs | 55 ------- osu.Game/Online/Rooms/MatchType.cs | 18 +++ osu.Game/Online/Rooms/Room.cs | 3 +- .../OnlinePlay/Components/DrawableGameType.cs | 142 +++++++++++++++++- .../Match/Components/GameTypePicker.cs | 16 +- .../Match/MultiplayerMatchSettingsOverlay.cs | 3 +- .../Screens/OnlinePlay/OnlinePlayComposite.cs | 2 +- 13 files changed, 165 insertions(+), 244 deletions(-) delete mode 100644 osu.Game/Online/Rooms/GameType.cs delete mode 100644 osu.Game/Online/Rooms/GameTypes/GameTypePlaylists.cs delete mode 100644 osu.Game/Online/Rooms/GameTypes/GameTypeTag.cs delete mode 100644 osu.Game/Online/Rooms/GameTypes/GameTypeTagTeam.cs delete mode 100644 osu.Game/Online/Rooms/GameTypes/GameTypeTeamVersus.cs delete mode 100644 osu.Game/Online/Rooms/GameTypes/GameTypeVersus.cs delete mode 100644 osu.Game/Online/Rooms/GameTypes/VersusRow.cs create mode 100644 osu.Game/Online/Rooms/MatchType.cs diff --git a/osu.Game/Online/Rooms/GameType.cs b/osu.Game/Online/Rooms/GameType.cs deleted file mode 100644 index caa352d812..0000000000 --- a/osu.Game/Online/Rooms/GameType.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Game.Graphics; - -namespace osu.Game.Online.Rooms -{ - public abstract class GameType - { - public abstract string Name { get; } - - public abstract Drawable GetIcon(OsuColour colours, float size); - - public override int GetHashCode() => GetType().GetHashCode(); - public override bool Equals(object obj) => GetType() == obj?.GetType(); - } -} diff --git a/osu.Game/Online/Rooms/GameTypes/GameTypePlaylists.cs b/osu.Game/Online/Rooms/GameTypes/GameTypePlaylists.cs deleted file mode 100644 index 3425c6c5cd..0000000000 --- a/osu.Game/Online/Rooms/GameTypes/GameTypePlaylists.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics; -using osuTK; - -namespace osu.Game.Online.Rooms.GameTypes -{ - public class GameTypePlaylists : GameType - { - public override string Name => "Playlists"; - - public override Drawable GetIcon(OsuColour colours, float size) => new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Icon = FontAwesome.Regular.Clock, - Size = new Vector2(size), - Colour = colours.Blue, - Shadow = false - }; - } -} diff --git a/osu.Game/Online/Rooms/GameTypes/GameTypeTag.cs b/osu.Game/Online/Rooms/GameTypes/GameTypeTag.cs deleted file mode 100644 index e468612738..0000000000 --- a/osu.Game/Online/Rooms/GameTypes/GameTypeTag.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics; -using osuTK; - -namespace osu.Game.Online.Rooms.GameTypes -{ - public class GameTypeTag : GameType - { - public override string Name => "Tag"; - - public override Drawable GetIcon(OsuColour colours, float size) - { - return new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Icon = FontAwesome.Solid.Sync, - Size = new Vector2(size), - Colour = colours.Blue, - Shadow = false, - }; - } - } -} diff --git a/osu.Game/Online/Rooms/GameTypes/GameTypeTagTeam.cs b/osu.Game/Online/Rooms/GameTypes/GameTypeTagTeam.cs deleted file mode 100644 index b82f203fac..0000000000 --- a/osu.Game/Online/Rooms/GameTypes/GameTypeTagTeam.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics; -using osuTK; - -namespace osu.Game.Online.Rooms.GameTypes -{ - public class GameTypeTagTeam : GameType - { - public override string Name => "Tag Team"; - - public override Drawable GetIcon(OsuColour colours, float size) - { - return new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(2f), - Children = new[] - { - new SpriteIcon - { - Icon = FontAwesome.Solid.Sync, - Size = new Vector2(size * 0.75f), - Colour = colours.Blue, - Shadow = false, - }, - new SpriteIcon - { - Icon = FontAwesome.Solid.Sync, - Size = new Vector2(size * 0.75f), - Colour = colours.Pink, - Shadow = false, - }, - }, - }; - } - } -} diff --git a/osu.Game/Online/Rooms/GameTypes/GameTypeTeamVersus.cs b/osu.Game/Online/Rooms/GameTypes/GameTypeTeamVersus.cs deleted file mode 100644 index 5ad4033dc9..0000000000 --- a/osu.Game/Online/Rooms/GameTypes/GameTypeTeamVersus.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osuTK; - -namespace osu.Game.Online.Rooms.GameTypes -{ - public class GameTypeTeamVersus : GameType - { - public override string Name => "Team Versus"; - - public override Drawable GetIcon(OsuColour colours, float size) - { - return new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(2f), - Children = new[] - { - new VersusRow(colours.Blue, colours.Pink, size * 0.5f), - new VersusRow(colours.Blue, colours.Pink, size * 0.5f), - }, - }; - } - } -} diff --git a/osu.Game/Online/Rooms/GameTypes/GameTypeVersus.cs b/osu.Game/Online/Rooms/GameTypes/GameTypeVersus.cs deleted file mode 100644 index 3783cc67b0..0000000000 --- a/osu.Game/Online/Rooms/GameTypes/GameTypeVersus.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Game.Graphics; - -namespace osu.Game.Online.Rooms.GameTypes -{ - public class GameTypeVersus : GameType - { - public override string Name => "Versus"; - - public override Drawable GetIcon(OsuColour colours, float size) - { - return new VersusRow(colours.Blue, colours.Blue, size * 0.6f) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }; - } - } -} diff --git a/osu.Game/Online/Rooms/GameTypes/VersusRow.cs b/osu.Game/Online/Rooms/GameTypes/VersusRow.cs deleted file mode 100644 index 0bd09a23ac..0000000000 --- a/osu.Game/Online/Rooms/GameTypes/VersusRow.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osuTK; -using osuTK.Graphics; - -namespace osu.Game.Online.Rooms.GameTypes -{ - public class VersusRow : FillFlowContainer - { - public VersusRow(Color4 first, Color4 second, float size) - { - var triangleSize = new Vector2(size); - AutoSizeAxes = Axes.Both; - Spacing = new Vector2(2f, 0f); - - Children = new[] - { - new Container - { - Size = triangleSize, - Colour = first, - Children = new[] - { - new EquilateralTriangle - { - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.Both, - Rotation = 90, - EdgeSmoothness = new Vector2(1f), - }, - }, - }, - new Container - { - Size = triangleSize, - Colour = second, - Children = new[] - { - new EquilateralTriangle - { - Anchor = Anchor.BottomLeft, - RelativeSizeAxes = Axes.Both, - Rotation = -90, - EdgeSmoothness = new Vector2(1f), - }, - }, - }, - }; - } - } -} diff --git a/osu.Game/Online/Rooms/MatchType.cs b/osu.Game/Online/Rooms/MatchType.cs new file mode 100644 index 0000000000..cafa147a61 --- /dev/null +++ b/osu.Game/Online/Rooms/MatchType.cs @@ -0,0 +1,18 @@ +// 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 MatchType + { + Playlists, + + [Description("Head to head")] + HeadToHead, + + [Description("Team VS")] + TeamVersus, + } +} diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index 4c506e26a8..fe7455d964 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -7,7 +7,6 @@ using Newtonsoft.Json; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.IO.Serialization.Converters; -using osu.Game.Online.Rooms.GameTypes; using osu.Game.Online.Rooms.RoomStatuses; using osu.Game.Users; using osu.Game.Utils; @@ -63,7 +62,7 @@ namespace osu.Game.Online.Rooms [Cached] [JsonIgnore] - public readonly Bindable Type = new Bindable(new GameTypePlaylists()); + public readonly Bindable Type = new Bindable(); [Cached] [JsonIgnore] diff --git a/osu.Game/Screens/OnlinePlay/Components/DrawableGameType.cs b/osu.Game/Screens/OnlinePlay/Components/DrawableGameType.cs index ae1ca1b967..613f16563c 100644 --- a/osu.Game/Screens/OnlinePlay/Components/DrawableGameType.cs +++ b/osu.Game/Screens/OnlinePlay/Components/DrawableGameType.cs @@ -2,24 +2,28 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Online.Rooms; +using osuTK; +using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay.Components { public class DrawableGameType : CircularContainer, IHasTooltip { - private readonly GameType type; + private readonly MatchType type; - public LocalisableString TooltipText => type.Name; + public LocalisableString TooltipText => type.GetLocalisableDescription(); - public DrawableGameType(GameType type) + public DrawableGameType(MatchType type) { this.type = type; Masking = true; @@ -34,10 +38,138 @@ namespace osu.Game.Screens.OnlinePlay.Components }; } + [Resolved] + private OsuColour colours { get; set; } + [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load() { - Add(type.GetIcon(colours, Height / 2)); + Add(getIconFor(type)); + } + + private Drawable getIconFor(MatchType matchType) + { + float size = Height / 2; + + switch (matchType) + { + default: + case MatchType.Playlists: + return new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(size), + Icon = FontAwesome.Regular.Clock, + Colour = colours.Blue, + Shadow = false + }; + + case MatchType.HeadToHead: + return new VersusRow(colours.Blue, colours.Blue, size * 0.6f) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + + case MatchType.TeamVersus: + return new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(2f), + Children = new[] + { + new VersusRow(colours.Blue, colours.Pink, size * 0.5f), + new VersusRow(colours.Blue, colours.Pink, size * 0.5f), + }, + }; + + // case MatchType.TagCoop: + // return new SpriteIcon + // { + // Anchor = Anchor.Centre, + // Origin = Anchor.Centre, + // Size = new Vector2(size), + // Icon = FontAwesome.Solid.Sync, + // Colour = colours.Blue, + // + // Shadow = false + // }; + + // case MatchType.TagTeamCoop: + // return new FillFlowContainer + // { + // Anchor = Anchor.Centre, + // Origin = Anchor.Centre, + // AutoSizeAxes = Axes.Both, + // Direction = FillDirection.Horizontal, + // Spacing = new Vector2(2f), + // Children = new[] + // { + // new SpriteIcon + // { + // Icon = FontAwesome.Solid.Sync, + // Size = new Vector2(size * 0.75f), + // Colour = colours.Blue, + // Shadow = false, + // }, + // new SpriteIcon + // { + // Icon = FontAwesome.Solid.Sync, + // Size = new Vector2(size * 0.75f), + // Colour = colours.Pink, + // Shadow = false, + // }, + // }, + // }; + } + } + + private class VersusRow : FillFlowContainer + { + public VersusRow(Color4 first, Color4 second, float size) + { + var triangleSize = new Vector2(size); + AutoSizeAxes = Axes.Both; + Spacing = new Vector2(2f, 0f); + + Children = new[] + { + new Container + { + Size = triangleSize, + Colour = first, + Children = new[] + { + new EquilateralTriangle + { + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Both, + Rotation = 90, + EdgeSmoothness = new Vector2(1f), + }, + }, + }, + new Container + { + Size = triangleSize, + Colour = second, + Children = new[] + { + new EquilateralTriangle + { + Anchor = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Both, + Rotation = -90, + EdgeSmoothness = new Vector2(1f), + }, + }, + }, + }; + } } } } diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/GameTypePicker.cs b/osu.Game/Screens/OnlinePlay/Match/Components/GameTypePicker.cs index cca1f84bbb..864a9bc4d7 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/GameTypePicker.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/GameTypePicker.cs @@ -9,31 +9,27 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Online.Rooms; -using osu.Game.Online.Rooms.GameTypes; using osu.Game.Screens.OnlinePlay.Components; using osuTK; namespace osu.Game.Screens.OnlinePlay.Match.Components { - public class GameTypePicker : DisableableTabControl + public class GameTypePicker : DisableableTabControl { private const float height = 40; private const float selection_width = 3; - protected override TabItem CreateTabItem(GameType value) => new GameTypePickerItem(value); + protected override TabItem CreateTabItem(MatchType value) => new GameTypePickerItem(value); - protected override Dropdown CreateDropdown() => null; + protected override Dropdown CreateDropdown() => null; public GameTypePicker() { Height = height + selection_width * 2; TabContainer.Spacing = new Vector2(10 - selection_width * 2); - AddItem(new GameTypeTag()); - AddItem(new GameTypeVersus()); - AddItem(new GameTypeTagTeam()); - AddItem(new GameTypeTeamVersus()); - AddItem(new GameTypePlaylists()); + AddItem(MatchType.HeadToHead); + AddItem(MatchType.TeamVersus); } private class GameTypePickerItem : DisableableTabItem @@ -42,7 +38,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components private readonly CircularContainer hover, selection; - public GameTypePickerItem(GameType value) + public GameTypePickerItem(MatchType value) : base(value) { AutoSizeAxes = Axes.Both; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 338d2c9e84..3dfbeac411 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.ExceptionExtensions; using osu.Framework.Graphics; @@ -265,7 +266,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match loadingLayer = new LoadingLayer(true) }; - TypePicker.Current.BindValueChanged(type => typeLabel.Text = type.NewValue?.Name ?? string.Empty, true); + TypePicker.Current.BindValueChanged(type => typeLabel.Text = type.NewValue.GetLocalisableDescription(), true); RoomName.BindValueChanged(name => NameField.Text = name.NewValue, true); Availability.BindValueChanged(availability => AvailabilityPicker.Current.Value = availability.NewValue, true); Type.BindValueChanged(type => TypePicker.Current.Value = type.NewValue, true); diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index 0b28bc1a7e..49524660db 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -30,7 +30,7 @@ namespace osu.Game.Screens.OnlinePlay protected Bindable Status { get; private set; } [Resolved(typeof(Room))] - protected Bindable Type { get; private set; } + protected Bindable Type { get; private set; } [Resolved(typeof(Room))] protected BindableList Playlist { get; private set; } From 556962a3d881fe7a4f50ef8092432af19303521b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Aug 2021 14:50:56 +0900 Subject: [PATCH 0796/2442] Add missing xmldoc comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Online/API/IAPIProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/IAPIProvider.cs b/osu.Game/Online/API/IAPIProvider.cs index 686df5c57c..5ad5367924 100644 --- a/osu.Game/Online/API/IAPIProvider.cs +++ b/osu.Game/Online/API/IAPIProvider.cs @@ -102,7 +102,7 @@ namespace osu.Game.Online.API /// /// The name of the client this connector connects for, used for logging. /// The endpoint to the hub. - /// + /// Whether to use MessagePack for serialisation if available on this platform. IHubClientConnector? GetHubConnector(string clientName, string endpoint, bool preferMessagePack = true); /// From 5ac3abac99befc092b6e92d5489b7b8dfc4e0759 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Aug 2021 14:53:04 +0900 Subject: [PATCH 0797/2442] Add missing `forceScheduled` parameter --- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index deac736b80..5220db16b5 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -451,7 +451,7 @@ namespace osu.Game.Online.Multiplayer Room.MatchRulesetState = state; RoomUpdated?.Invoke(); - }); + }, false); return Task.CompletedTask; } From ee102e3755b0a15d7fd6af3be1e9de2b3beb09a6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Aug 2021 14:55:20 +0900 Subject: [PATCH 0798/2442] Fix incorrectly overwritten `ReferenceLoopHandling` setting --- osu.Game/Online/HubClientConnector.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/HubClientConnector.cs b/osu.Game/Online/HubClientConnector.cs index 6c6a217f54..d2dba8a402 100644 --- a/osu.Game/Online/HubClientConnector.cs +++ b/osu.Game/Online/HubClientConnector.cs @@ -156,12 +156,9 @@ namespace osu.Game.Online builder.AddNewtonsoftJsonProtocol(options => { options.PayloadSerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; - options.PayloadSerializerSettings = new JsonSerializerSettings - { - // TODO: This should only be required to be `TypeNameHandling.Auto`. - // See usage in osu-server-spectator for further documentation as to why this is required. - TypeNameHandling = TypeNameHandling.All - }; + // TODO: This should only be required to be `TypeNameHandling.Auto`. + // See usage in osu-server-spectator for further documentation as to why this is required. + options.PayloadSerializerSettings.TypeNameHandling = TypeNameHandling.All; }); } From 66427127f0f48bcbce56a4b8fb4eee40e2775128 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Aug 2021 15:09:03 +0900 Subject: [PATCH 0799/2442] Update naming in line with discussion --- osu.Game/Online/Multiplayer/MatchRulesetType.cs | 11 ----------- .../Online/Multiplayer/MultiplayerRoomSettings.cs | 3 ++- osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs | 6 +++--- 3 files changed, 5 insertions(+), 15 deletions(-) delete mode 100644 osu.Game/Online/Multiplayer/MatchRulesetType.cs diff --git a/osu.Game/Online/Multiplayer/MatchRulesetType.cs b/osu.Game/Online/Multiplayer/MatchRulesetType.cs deleted file mode 100644 index b87a8eb174..0000000000 --- a/osu.Game/Online/Multiplayer/MatchRulesetType.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Game.Online.Multiplayer -{ - public enum MatchRulesetType - { - HeadToHead, - TeamVs - } -} diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs index 2c49e3b251..f76ab5da89 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Linq; using MessagePack; using osu.Game.Online.API; +using osu.Game.Online.Rooms; namespace osu.Game.Online.Multiplayer { @@ -40,7 +41,7 @@ namespace osu.Game.Online.Multiplayer public string Password { get; set; } = string.Empty; [Key(8)] - public MatchRulesetType MatchRulesetType { get; set; } + public MatchType MatchRulesetType { get; set; } public bool Equals(MultiplayerRoomSettings other) => BeatmapID == other.BeatmapID diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs index d97d49aa8a..96e9a7fd4a 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs @@ -24,6 +24,9 @@ namespace osu.Game.Online.Multiplayer [Key(1)] public MultiplayerUserState State { get; set; } = MultiplayerUserState.Idle; + [Key(4)] + public MatchRulesetUserState? MatchRulesetState { get; set; } + /// /// The availability state of the current beatmap. /// @@ -36,9 +39,6 @@ namespace osu.Game.Online.Multiplayer [Key(3)] public IEnumerable Mods { get; set; } = Enumerable.Empty(); - [Key(4)] - public MatchRulesetUserState? MatchRulesetState { get; set; } - [IgnoreMember] public User? User { get; set; } From 70da58323a3781cd83fb8f462351ce8026523d6c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Aug 2021 15:43:04 +0900 Subject: [PATCH 0800/2442] Drop `MatchRuleset` terminology completely --- osu.Game/Online/Multiplayer/IMultiplayerClient.cs | 12 ++++++------ .../Online/Multiplayer/IMultiplayerRoomServer.cs | 4 ++-- ...{MatchRulesetRoomState.cs => MatchRoomState.cs} | 9 +++++---- ...chRulesetServerEvent.cs => MatchServerEvent.cs} | 2 +- .../TeamVersus}/ChangeTeamRequest.cs | 4 ++-- .../TeamVersus}/MultiplayerTeam.cs | 2 +- .../TeamVersus/TeamVersusRoomState.cs} | 8 ++++---- .../TeamVersus/TeamVersusUserState.cs} | 4 ++-- ...chRulesetUserRequest.cs => MatchUserRequest.cs} | 4 ++-- ...{MatchRulesetUserState.cs => MatchUserState.cs} | 9 +++++---- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 14 +++++++------- osu.Game/Online/Multiplayer/MultiplayerRoom.cs | 2 +- .../Online/Multiplayer/MultiplayerRoomSettings.cs | 6 +++--- osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs | 2 +- .../Online/Multiplayer/OnlineMultiplayerClient.cs | 8 ++++---- .../Visual/Multiplayer/TestMultiplayerClient.cs | 2 +- 16 files changed, 47 insertions(+), 45 deletions(-) rename osu.Game/Online/Multiplayer/{MatchRulesetRoomState.cs => MatchRoomState.cs} (56%) rename osu.Game/Online/Multiplayer/{MatchRulesetServerEvent.cs => MatchServerEvent.cs} (89%) rename osu.Game/Online/Multiplayer/{MatchRulesets/TeamVs => MatchTypes/TeamVersus}/ChangeTeamRequest.cs (68%) rename osu.Game/Online/Multiplayer/{MatchRulesets/TeamVs => MatchTypes/TeamVersus}/MultiplayerTeam.cs (87%) rename osu.Game/Online/Multiplayer/{MatchRulesets/TeamVs/TeamVsMatchRoomState.cs => MatchTypes/TeamVersus/TeamVersusRoomState.cs} (73%) rename osu.Game/Online/Multiplayer/{MatchRulesets/TeamVs/TeamVsMatchUserState.cs => MatchTypes/TeamVersus/TeamVersusUserState.cs} (68%) rename osu.Game/Online/Multiplayer/{MatchRulesetUserRequest.cs => MatchUserRequest.cs} (83%) rename osu.Game/Online/Multiplayer/{MatchRulesetUserState.cs => MatchUserState.cs} (56%) diff --git a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs index 839ec9bd1f..064065ab00 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs @@ -51,23 +51,23 @@ namespace osu.Game.Online.Multiplayer Task UserStateChanged(int userId, MultiplayerUserState state); /// - /// Signals that the match ruleset state has changed for a user in this room. + /// Signals that the match type state has changed for a user in this room. /// /// The ID of the user performing a state change. /// The new state of the user. - Task MatchRulesetUserStateChanged(int userId, MatchRulesetUserState state); + Task MatchUserStateChanged(int userId, MatchUserState state); /// - /// Signals that the match ruleset state has changed for this room. + /// Signals that the match type state has changed for this room. /// /// The new state of the room. - Task MatchRulesetRoomStateChanged(MatchRulesetRoomState state); + Task MatchRoomStateChanged(MatchRoomState state); /// - /// Send a match ruleset specific request. + /// Send a match type specific request. /// /// The event to handle. - Task MatchRulesetEvent(MatchRulesetServerEvent e); + Task MatchEvent(MatchServerEvent e); /// /// Signals that a user in this room changed their beatmap availability state. diff --git a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs index 20c9f9e216..b26c4d8201 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs @@ -56,10 +56,10 @@ namespace osu.Game.Online.Multiplayer Task ChangeUserMods(IEnumerable newMods); /// - /// Send a match ruleset specific request. + /// Send a match type specific request. /// /// The request to send. - Task SendMatchRulesetRequest(MatchRulesetUserRequest request); + Task SendMatchRequest(MatchUserRequest request); /// /// As the host of a room, start the match. diff --git a/osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs b/osu.Game/Online/Multiplayer/MatchRoomState.cs similarity index 56% rename from osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs rename to osu.Game/Online/Multiplayer/MatchRoomState.cs index 105bb1c281..5b662af100 100644 --- a/osu.Game/Online/Multiplayer/MatchRulesetRoomState.cs +++ b/osu.Game/Online/Multiplayer/MatchRoomState.cs @@ -3,20 +3,21 @@ using System; using MessagePack; -using osu.Game.Online.Multiplayer.MatchRulesets.TeamVs; +using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; #nullable enable namespace osu.Game.Online.Multiplayer { /// - /// Room-wide state for the current match ruleset. + /// Room-wide state for the current match type. /// Can be used to contain any state which should be used before or during match gameplay. /// [Serializable] [MessagePackObject] - [Union(0, typeof(TeamVsMatchRoomState))] - public class MatchRulesetRoomState // TODO: this will need to be abstract or interface when/if we get messagepack working. for now it isn't as it breaks json serialisation. + [Union(0, typeof(TeamVersusRoomState))] + // TODO: this will need to be abstract or interface when/if we get messagepack working. for now it isn't as it breaks json serialisation. + public class MatchRoomState { } } diff --git a/osu.Game/Online/Multiplayer/MatchRulesetServerEvent.cs b/osu.Game/Online/Multiplayer/MatchServerEvent.cs similarity index 89% rename from osu.Game/Online/Multiplayer/MatchRulesetServerEvent.cs rename to osu.Game/Online/Multiplayer/MatchServerEvent.cs index c45615e914..891fb2cc3b 100644 --- a/osu.Game/Online/Multiplayer/MatchRulesetServerEvent.cs +++ b/osu.Game/Online/Multiplayer/MatchServerEvent.cs @@ -11,7 +11,7 @@ namespace osu.Game.Online.Multiplayer /// [Serializable] [MessagePackObject] - public abstract class MatchRulesetServerEvent + public abstract class MatchServerEvent { } } diff --git a/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/ChangeTeamRequest.cs b/osu.Game/Online/Multiplayer/MatchTypes/TeamVersus/ChangeTeamRequest.cs similarity index 68% rename from osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/ChangeTeamRequest.cs rename to osu.Game/Online/Multiplayer/MatchTypes/TeamVersus/ChangeTeamRequest.cs index 5e1d489d39..9c3b07049c 100644 --- a/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/ChangeTeamRequest.cs +++ b/osu.Game/Online/Multiplayer/MatchTypes/TeamVersus/ChangeTeamRequest.cs @@ -5,9 +5,9 @@ using MessagePack; #nullable enable -namespace osu.Game.Online.Multiplayer.MatchRulesets.TeamVs +namespace osu.Game.Online.Multiplayer.MatchTypes.TeamVersus { - public class ChangeTeamRequest : MatchRulesetUserRequest + public class ChangeTeamRequest : MatchUserRequest { [Key(0)] public int TeamID { get; set; } diff --git a/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/MultiplayerTeam.cs b/osu.Game/Online/Multiplayer/MatchTypes/TeamVersus/MultiplayerTeam.cs similarity index 87% rename from osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/MultiplayerTeam.cs rename to osu.Game/Online/Multiplayer/MatchTypes/TeamVersus/MultiplayerTeam.cs index f6bf893818..f952dbc1b5 100644 --- a/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/MultiplayerTeam.cs +++ b/osu.Game/Online/Multiplayer/MatchTypes/TeamVersus/MultiplayerTeam.cs @@ -6,7 +6,7 @@ using MessagePack; #nullable enable -namespace osu.Game.Online.Multiplayer.MatchRulesets.TeamVs +namespace osu.Game.Online.Multiplayer.MatchTypes.TeamVersus { [Serializable] [MessagePackObject] diff --git a/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchRoomState.cs b/osu.Game/Online/Multiplayer/MatchTypes/TeamVersus/TeamVersusRoomState.cs similarity index 73% rename from osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchRoomState.cs rename to osu.Game/Online/Multiplayer/MatchTypes/TeamVersus/TeamVersusRoomState.cs index 92f67e99bd..91d1aa43d4 100644 --- a/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchRoomState.cs +++ b/osu.Game/Online/Multiplayer/MatchTypes/TeamVersus/TeamVersusRoomState.cs @@ -6,16 +6,16 @@ using MessagePack; #nullable enable -namespace osu.Game.Online.Multiplayer.MatchRulesets.TeamVs +namespace osu.Game.Online.Multiplayer.MatchTypes.TeamVersus { [MessagePackObject] - public class TeamVsMatchRoomState : MatchRulesetRoomState + public class TeamVersusRoomState : MatchRoomState { [Key(0)] public List Teams { get; set; } = new List(); - public static TeamVsMatchRoomState CreateDefault() => - new TeamVsMatchRoomState + public static TeamVersusRoomState CreateDefault() => + new TeamVersusRoomState { Teams = { diff --git a/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchUserState.cs b/osu.Game/Online/Multiplayer/MatchTypes/TeamVersus/TeamVersusUserState.cs similarity index 68% rename from osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchUserState.cs rename to osu.Game/Online/Multiplayer/MatchTypes/TeamVersus/TeamVersusUserState.cs index 0880787a5a..96a4e2ea99 100644 --- a/osu.Game/Online/Multiplayer/MatchRulesets/TeamVs/TeamVsMatchUserState.cs +++ b/osu.Game/Online/Multiplayer/MatchTypes/TeamVersus/TeamVersusUserState.cs @@ -5,9 +5,9 @@ using MessagePack; #nullable enable -namespace osu.Game.Online.Multiplayer.MatchRulesets.TeamVs +namespace osu.Game.Online.Multiplayer.MatchTypes.TeamVersus { - public class TeamVsMatchUserState : MatchRulesetUserState + public class TeamVersusUserState : MatchUserState { [Key(0)] public int TeamID { get; set; } diff --git a/osu.Game/Online/Multiplayer/MatchRulesetUserRequest.cs b/osu.Game/Online/Multiplayer/MatchUserRequest.cs similarity index 83% rename from osu.Game/Online/Multiplayer/MatchRulesetUserRequest.cs rename to osu.Game/Online/Multiplayer/MatchUserRequest.cs index 3cc430a2b7..15c3ad0776 100644 --- a/osu.Game/Online/Multiplayer/MatchRulesetUserRequest.cs +++ b/osu.Game/Online/Multiplayer/MatchUserRequest.cs @@ -7,11 +7,11 @@ using MessagePack; namespace osu.Game.Online.Multiplayer { /// - /// A request from a user to perform an action specific to the current match ruleset. + /// A request from a user to perform an action specific to the current match type. /// [Serializable] [MessagePackObject] - public abstract class MatchRulesetUserRequest + public abstract class MatchUserRequest { } } diff --git a/osu.Game/Online/Multiplayer/MatchRulesetUserState.cs b/osu.Game/Online/Multiplayer/MatchUserState.cs similarity index 56% rename from osu.Game/Online/Multiplayer/MatchRulesetUserState.cs rename to osu.Game/Online/Multiplayer/MatchUserState.cs index aa34cdab41..f457191bb5 100644 --- a/osu.Game/Online/Multiplayer/MatchRulesetUserState.cs +++ b/osu.Game/Online/Multiplayer/MatchUserState.cs @@ -3,20 +3,21 @@ using System; using MessagePack; -using osu.Game.Online.Multiplayer.MatchRulesets.TeamVs; +using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; #nullable enable namespace osu.Game.Online.Multiplayer { /// - /// User specific state for the current match ruleset. + /// User specific state for the current match type. /// Can be used to contain any state which should be used before or during match gameplay. /// [Serializable] [MessagePackObject] - [Union(0, typeof(TeamVsMatchUserState))] - public class MatchRulesetUserState // TODO: this will need to be abstract or interface when/if we get messagepack working. for now it isn't as it breaks json serialisation. + [Union(0, typeof(TeamVersusUserState))] + // TODO: this will need to be abstract or interface when/if we get messagepack working. for now it isn't as it breaks json serialisation. + public class MatchUserState { } } diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 5220db16b5..873be7f49c 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -293,7 +293,7 @@ namespace osu.Game.Online.Multiplayer public abstract Task ChangeUserMods(IEnumerable newMods); - public abstract Task SendMatchRulesetRequest(MatchRulesetUserRequest request); + public abstract Task SendMatchRequest(MatchUserRequest request); public abstract Task StartMatch(); @@ -422,7 +422,7 @@ namespace osu.Game.Online.Multiplayer return Task.CompletedTask; } - Task IMultiplayerClient.MatchRulesetUserStateChanged(int userId, MatchRulesetUserState state) + Task IMultiplayerClient.MatchUserStateChanged(int userId, MatchUserState state) { if (Room == null) return Task.CompletedTask; @@ -432,14 +432,14 @@ namespace osu.Game.Online.Multiplayer if (Room == null) return; - Room.Users.Single(u => u.UserID == userId).MatchRulesetState = state; + Room.Users.Single(u => u.UserID == userId).MatchState = state; RoomUpdated?.Invoke(); }, false); return Task.CompletedTask; } - Task IMultiplayerClient.MatchRulesetRoomStateChanged(MatchRulesetRoomState state) + Task IMultiplayerClient.MatchRoomStateChanged(MatchRoomState state) { if (Room == null) return Task.CompletedTask; @@ -449,16 +449,16 @@ namespace osu.Game.Online.Multiplayer if (Room == null) return; - Room.MatchRulesetState = state; + Room.MatchState = state; RoomUpdated?.Invoke(); }, false); return Task.CompletedTask; } - public Task MatchRulesetEvent(MatchRulesetServerEvent e) + public Task MatchEvent(MatchServerEvent e) { - // not used by any match rulesets just yet. + // not used by any match types just yet. return Task.CompletedTask; } diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoom.cs b/osu.Game/Online/Multiplayer/MultiplayerRoom.cs index 07d87fafc3..175c0e0e27 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoom.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoom.cs @@ -48,7 +48,7 @@ namespace osu.Game.Online.Multiplayer public MultiplayerRoomUser? Host { get; set; } [Key(5)] - public MatchRulesetRoomState? MatchRulesetState { get; set; } + public MatchRoomState? MatchState { get; set; } [JsonConstructor] [SerializationConstructor] diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs index f76ab5da89..706bc750d3 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs @@ -41,7 +41,7 @@ namespace osu.Game.Online.Multiplayer public string Password { get; set; } = string.Empty; [Key(8)] - public MatchType MatchRulesetType { get; set; } + public MatchType MatchType { get; set; } public bool Equals(MultiplayerRoomSettings other) => BeatmapID == other.BeatmapID @@ -52,7 +52,7 @@ namespace osu.Game.Online.Multiplayer && Password.Equals(other.Password, StringComparison.Ordinal) && Name.Equals(other.Name, StringComparison.Ordinal) && PlaylistItemId == other.PlaylistItemId - && MatchRulesetType == other.MatchRulesetType; + && MatchType == other.MatchType; public override string ToString() => $"Name:{Name}" + $" Beatmap:{BeatmapID} ({BeatmapChecksum})" @@ -60,7 +60,7 @@ namespace osu.Game.Online.Multiplayer + $" AllowedMods:{string.Join(',', AllowedMods)}" + $" Password:{(string.IsNullOrEmpty(Password) ? "no" : "yes")}" + $" Ruleset:{RulesetID}" - + $" MatchRuleset:{MatchRulesetType}" + + $" Type:{MatchType}" + $" Item:{PlaylistItemId}"; } } diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs index 96e9a7fd4a..5d11e2921a 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs @@ -25,7 +25,7 @@ namespace osu.Game.Online.Multiplayer public MultiplayerUserState State { get; set; } = MultiplayerUserState.Idle; [Key(4)] - public MatchRulesetUserState? MatchRulesetState { get; set; } + public MatchUserState? MatchState { get; set; } /// /// The availability state of the current beatmap. diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs index 1dfc60312b..00f30463a5 100644 --- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs @@ -58,8 +58,8 @@ namespace osu.Game.Online.Multiplayer connection.On(nameof(IMultiplayerClient.ResultsReady), ((IMultiplayerClient)this).ResultsReady); connection.On>(nameof(IMultiplayerClient.UserModsChanged), ((IMultiplayerClient)this).UserModsChanged); connection.On(nameof(IMultiplayerClient.UserBeatmapAvailabilityChanged), ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged); - connection.On(nameof(IMultiplayerClient.MatchRulesetRoomStateChanged), ((IMultiplayerClient)this).MatchRulesetRoomStateChanged); - connection.On(nameof(IMultiplayerClient.MatchRulesetUserStateChanged), ((IMultiplayerClient)this).MatchRulesetUserStateChanged); + connection.On(nameof(IMultiplayerClient.MatchRoomStateChanged), ((IMultiplayerClient)this).MatchRoomStateChanged); + connection.On(nameof(IMultiplayerClient.MatchUserStateChanged), ((IMultiplayerClient)this).MatchUserStateChanged); }; IsConnected.BindTo(connector.IsConnected); @@ -122,12 +122,12 @@ namespace osu.Game.Online.Multiplayer return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeUserMods), newMods); } - public override Task SendMatchRulesetRequest(MatchRulesetUserRequest request) + public override Task SendMatchRequest(MatchUserRequest request) { if (!IsConnected.Value) return Task.CompletedTask; - return connection.InvokeAsync(nameof(IMultiplayerServer.SendMatchRulesetRequest), request); + return connection.InvokeAsync(nameof(IMultiplayerServer.SendMatchRequest), request); } public override Task StartMatch() diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 6a8bf87ff0..7deecdfa28 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -192,7 +192,7 @@ namespace osu.Game.Tests.Visual.Multiplayer return Task.CompletedTask; } - public override Task SendMatchRulesetRequest(MatchRulesetUserRequest request) => Task.CompletedTask; + public override Task SendMatchRequest(MatchUserRequest request) => Task.CompletedTask; public override Task StartMatch() { From 8d1586261d47f3738e91adc37dc287746edf4887 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Aug 2021 15:59:13 +0900 Subject: [PATCH 0801/2442] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index c1075cfb17..b01210a7b1 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7e32f1e9fd..b3eeaa4be7 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 7b7d5f80fe..7ad27b222c 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From 404faabbbc5d77f5497628fa5968ebb0081131e2 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Tue, 3 Aug 2021 15:34:21 +0800 Subject: [PATCH 0802/2442] Use direct reference instead --- .../NamedOverlayComponentStrings.cs | 22 ------------------- .../BeatmapListing/BeatmapListingHeader.cs | 3 ++- .../Overlays/BeatmapSet/BeatmapSetHeader.cs | 4 ++-- .../Overlays/Changelog/ChangelogHeader.cs | 2 +- .../Dashboard/DashboardOverlayHeader.cs | 2 +- osu.Game/Overlays/News/NewsHeader.cs | 2 +- .../Rankings/RankingsOverlayHeader.cs | 3 ++- osu.Game/Overlays/Wiki/WikiHeader.cs | 2 +- 8 files changed, 10 insertions(+), 30 deletions(-) diff --git a/osu.Game/Localisation/NamedOverlayComponentStrings.cs b/osu.Game/Localisation/NamedOverlayComponentStrings.cs index f160ee57c7..475bea2a4a 100644 --- a/osu.Game/Localisation/NamedOverlayComponentStrings.cs +++ b/osu.Game/Localisation/NamedOverlayComponentStrings.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Localisation; -using osu.Game.Resources.Localisation.Web; namespace osu.Game.Localisation { @@ -10,52 +9,31 @@ namespace osu.Game.Localisation { private const string prefix = @"osu.Game.Resources.Localisation.NamedOverlayComponent"; - /// - public static LocalisableString BeatmapListingTitle => PageTitleStrings.MainBeatmapsetsControllerIndex; - /// /// "browse for new beatmaps" /// public static LocalisableString BeatmapListingDescription => new TranslatableString(getKey(@"beatmap_listing_description"), @"browse for new beatmaps"); - /// - public static LocalisableString BeatmapSetTitle => PageTitleStrings.MainBeatmapsetsControllerShow; - - /// - public static LocalisableString ChangelogTitle => PageTitleStrings.MainChangelogControllerDefault; - /// /// "track recent dev updates in the osu! ecosystem" /// public static LocalisableString ChangelogDescription => new TranslatableString(getKey(@"changelog_description"), @"track recent dev updates in the osu! ecosystem"); - /// - public static LocalisableString DashboardTitle => PageTitleStrings.MainHomeControllerIndex; - /// /// "view your friends and other information" /// public static LocalisableString DashboardDescription => new TranslatableString(getKey(@"dashboard_description"), @"view your friends and other information"); - /// - public static LocalisableString RankingsTitle => PageTitleStrings.MainRankingControllerDefault; - /// /// "find out who's the best right now" /// public static LocalisableString RankingsDescription => new TranslatableString(getKey(@"rankings_description"), @"find out who's the best right now"); - /// - public static LocalisableString NewsTitle => PageTitleStrings.MainNewsControllerDefault; - /// /// "get up-to-date on community happenings" /// public static LocalisableString NewsDescription => new TranslatableString(getKey(@"news_description"), @"get up-to-date on community happenings"); - /// - public static LocalisableString WikiTitle => PageTitleStrings.MainWikiControllerDefault; - /// /// "knowledge base" /// diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs index fa2ee0d735..3568fe9e4f 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapListing { @@ -13,7 +14,7 @@ namespace osu.Game.Overlays.BeatmapListing { public BeatmapListingTitle() { - Title = NamedOverlayComponentStrings.BeatmapListingTitle; + Title = PageTitleStrings.MainBeatmapsetsControllerIndex; Description = NamedOverlayComponentStrings.BeatmapListingDescription; IconTexture = "Icons/Hexacons/beatmap"; } diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs index 4fd24d9819..4a0c0e9f75 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs @@ -7,7 +7,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Effects; using osu.Game.Beatmaps; -using osu.Game.Localisation; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osuTK; using osuTK.Graphics; @@ -55,7 +55,7 @@ namespace osu.Game.Overlays.BeatmapSet { public BeatmapHeaderTitle() { - Title = NamedOverlayComponentStrings.BeatmapSetTitle; + Title = PageTitleStrings.MainBeatmapsetsControllerShow; IconTexture = "Icons/Hexacons/beatmap"; } } diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index 8707883ddd..52dea63ab7 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -117,7 +117,7 @@ namespace osu.Game.Overlays.Changelog { public ChangelogHeaderTitle() { - Title = NamedOverlayComponentStrings.ChangelogTitle; + Title = PageTitleStrings.MainChangelogControllerDefault; Description = NamedOverlayComponentStrings.ChangelogDescription; IconTexture = "Icons/Hexacons/devtools"; } diff --git a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs index 05c9f30ff3..70991d47b7 100644 --- a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs +++ b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Dashboard { public DashboardTitle() { - Title = NamedOverlayComponentStrings.DashboardTitle; + Title = PageTitleStrings.MainHomeControllerIndex; Description = NamedOverlayComponentStrings.DashboardDescription; IconTexture = "Icons/Hexacons/social"; } diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs index bf739e1e8e..35e3c7755d 100644 --- a/osu.Game/Overlays/News/NewsHeader.cs +++ b/osu.Game/Overlays/News/NewsHeader.cs @@ -64,7 +64,7 @@ namespace osu.Game.Overlays.News { public NewsHeaderTitle() { - Title = NamedOverlayComponentStrings.NewsTitle; + Title = PageTitleStrings.MainNewsControllerDefault; Description = NamedOverlayComponentStrings.NewsDescription; IconTexture = "Icons/Hexacons/news"; } diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs index c0f77049de..4916b8b327 100644 --- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs @@ -4,6 +4,7 @@ using osu.Framework.Graphics; using osu.Framework.Bindables; using osu.Game.Localisation; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osu.Game.Users; @@ -30,7 +31,7 @@ namespace osu.Game.Overlays.Rankings { public RankingsTitle() { - Title = NamedOverlayComponentStrings.RankingsTitle; + Title = PageTitleStrings.MainRankingControllerDefault; Description = NamedOverlayComponentStrings.RankingsDescription; IconTexture = "Icons/Hexacons/rankings"; } diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index b57bffb3d8..3e81d2cffe 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -77,7 +77,7 @@ namespace osu.Game.Overlays.Wiki { public WikiHeaderTitle() { - Title = NamedOverlayComponentStrings.WikiTitle; + Title = PageTitleStrings.MainWikiControllerDefault; Description = NamedOverlayComponentStrings.WikiDescription; IconTexture = "Icons/Hexacons/wiki"; } From 01f15bd6fc47df0bfc754d8cc3e100a7597a5d78 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Aug 2021 17:47:53 +0900 Subject: [PATCH 0803/2442] Rename picker class to match new naming --- .../Components/{GameTypePicker.cs => MatchTypePicker.cs} | 4 ++-- .../Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename osu.Game/Screens/OnlinePlay/Match/Components/{GameTypePicker.cs => MatchTypePicker.cs} (97%) diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/GameTypePicker.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.cs similarity index 97% rename from osu.Game/Screens/OnlinePlay/Match/Components/GameTypePicker.cs rename to osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.cs index 864a9bc4d7..c6f9b0f207 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/GameTypePicker.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.cs @@ -14,7 +14,7 @@ using osuTK; namespace osu.Game.Screens.OnlinePlay.Match.Components { - public class GameTypePicker : DisableableTabControl + public class MatchTypePicker : DisableableTabControl { private const float height = 40; private const float selection_width = 3; @@ -23,7 +23,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components protected override Dropdown CreateDropdown() => null; - public GameTypePicker() + public MatchTypePicker() { Height = height + selection_width * 2; TabContainer.Spacing = new Vector2(10 - selection_width * 2); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 3dfbeac411..425252f3cf 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -44,7 +44,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match public OsuTextBox NameField, MaxParticipantsField; public RoomAvailabilityPicker AvailabilityPicker; - public GameTypePicker TypePicker; + public MatchTypePicker TypePicker; public OsuTextBox PasswordTextBox; public TriangleButton ApplyButton; @@ -158,7 +158,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match Spacing = new Vector2(7), Children = new Drawable[] { - TypePicker = new GameTypePicker + TypePicker = new MatchTypePicker { RelativeSizeAxes = Axes.X, Enabled = { Value = false } From feadfbcca765797fe62d278c725f696fa1b86595 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Aug 2021 17:48:20 +0900 Subject: [PATCH 0804/2442] Add playlist type to picker temporarily --- osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.cs index c6f9b0f207..c72fa24b67 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.cs @@ -30,6 +30,8 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components AddItem(MatchType.HeadToHead); AddItem(MatchType.TeamVersus); + // TODO: remove after osu-web is updated to set the correct default type. + AddItem(MatchType.Playlists); } private class GameTypePickerItem : DisableableTabItem From 062207fcd97a079fd88919758f12c5e45835657b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 3 Aug 2021 18:16:58 +0900 Subject: [PATCH 0805/2442] Fix TestSceneCurrentlyPlayingDisplay reusing cached spectator client --- .../TestSceneCurrentlyPlayingDisplay.cs | 38 ++++++++----------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs index 30785fd163..2f11fec6d1 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs @@ -1,13 +1,12 @@ // 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.Linq; using System.Threading; using System.Threading.Tasks; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Database; using osu.Game.Online.Spectator; @@ -21,51 +20,44 @@ namespace osu.Game.Tests.Visual.Online { private readonly User streamingUser = new User { Id = 2, Username = "Test user" }; - [Cached(typeof(SpectatorClient))] - private TestSpectatorClient testSpectatorClient = new TestSpectatorClient(); - + private TestSpectatorClient spectatorClient; private CurrentlyPlayingDisplay currentlyPlaying; - [Cached(typeof(UserLookupCache))] - private UserLookupCache lookupCache = new TestUserLookupCache(); - - private Container nestedContainer; - [SetUpSteps] public void SetUpSteps() { AddStep("add streaming client", () => { - nestedContainer?.Remove(testSpectatorClient); - Remove(lookupCache); + spectatorClient = new TestSpectatorClient(); + var lookupCache = new TestUserLookupCache(); Children = new Drawable[] { lookupCache, - nestedContainer = new Container + spectatorClient, + new DependencyProvidingContainer { RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + CachedDependencies = new (Type, object)[] { - testSpectatorClient, - currentlyPlaying = new CurrentlyPlayingDisplay - { - RelativeSizeAxes = Axes.Both, - } + (typeof(SpectatorClient), spectatorClient), + (typeof(UserLookupCache), lookupCache) + }, + Child = currentlyPlaying = new CurrentlyPlayingDisplay + { + RelativeSizeAxes = Axes.Both, } }, }; }); - - AddStep("Reset players", () => testSpectatorClient.EndPlay(streamingUser.Id)); } [Test] public void TestBasicDisplay() { - AddStep("Add playing user", () => testSpectatorClient.StartPlay(streamingUser.Id, 0)); + AddStep("Add playing user", () => spectatorClient.StartPlay(streamingUser.Id, 0)); AddUntilStep("Panel loaded", () => currentlyPlaying.ChildrenOfType()?.FirstOrDefault()?.User.Id == 2); - AddStep("Remove playing user", () => testSpectatorClient.EndPlay(streamingUser.Id)); + AddStep("Remove playing user", () => spectatorClient.EndPlay(streamingUser.Id)); AddUntilStep("Panel no longer present", () => !currentlyPlaying.ChildrenOfType().Any()); } From 11b9ba86cbed430a2cb0c02dcb0087d168aa81e4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 3 Aug 2021 18:28:08 +0900 Subject: [PATCH 0806/2442] Fix TestSceneSpectator reusing cached spectator client --- .../Visual/Gameplay/TestSceneSpectator.cs | 57 ++++++++++++------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index 7584d67afe..21c5d89aca 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -13,6 +13,7 @@ using osu.Game.Online.Spectator; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.UI; +using osu.Game.Screens; using osu.Game.Screens.Play; using osu.Game.Tests.Beatmaps.IO; using osu.Game.Tests.Visual.Multiplayer; @@ -25,41 +26,43 @@ namespace osu.Game.Tests.Visual.Gameplay { private readonly User streamingUser = new User { Id = MultiplayerTestScene.PLAYER_1_ID, Username = "Test user" }; - [Cached(typeof(SpectatorClient))] - private TestSpectatorClient testSpectatorClient = new TestSpectatorClient(); - [Cached(typeof(UserLookupCache))] private UserLookupCache lookupCache = new TestUserLookupCache(); // used just to show beatmap card for the time being. protected override bool UseOnlineAPI => true; - private SoloSpectator spectatorScreen; - [Resolved] private OsuGameBase game { get; set; } - private BeatmapSetInfo importedBeatmap; + private TestSpectatorClient spectatorClient; + private SoloSpectator spectatorScreen; + private BeatmapSetInfo importedBeatmap; private int importedBeatmapId; - public override void SetUpSteps() + [SetUpSteps] + public void SetupSteps() { - base.SetUpSteps(); + DependenciesScreen dependenciesScreen = null; + + AddStep("load dependencies", () => + { + spectatorClient = new TestSpectatorClient(); + + // The screen gets suspended so it stops receiving updates. + Child = spectatorClient; + + LoadScreen(dependenciesScreen = new DependenciesScreen(spectatorClient)); + }); + + AddUntilStep("wait for dependencies to load", () => dependenciesScreen.IsLoaded); AddStep("import beatmap", () => { importedBeatmap = ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).Result; importedBeatmapId = importedBeatmap.Beatmaps.First(b => b.RulesetID == 0).OnlineBeatmapID ?? -1; }); - - AddStep("add streaming client", () => - { - Remove(testSpectatorClient); - Add(testSpectatorClient); - }); - - finish(); } [Test] @@ -206,22 +209,36 @@ namespace osu.Game.Tests.Visual.Gameplay private void waitForPlayer() => AddUntilStep("wait for player", () => (Stack.CurrentScreen as Player)?.IsLoaded == true); - private void start(int? beatmapId = null) => AddStep("start play", () => testSpectatorClient.StartPlay(streamingUser.Id, beatmapId ?? importedBeatmapId)); + private void start(int? beatmapId = null) => AddStep("start play", () => spectatorClient.StartPlay(streamingUser.Id, beatmapId ?? importedBeatmapId)); - private void finish() => AddStep("end play", () => testSpectatorClient.EndPlay(streamingUser.Id)); + private void finish() => AddStep("end play", () => spectatorClient.EndPlay(streamingUser.Id)); private void checkPaused(bool state) => AddUntilStep($"game is {(state ? "paused" : "playing")}", () => player.ChildrenOfType().First().IsPaused.Value == state); private void sendFrames(int count = 10) { - AddStep("send frames", () => testSpectatorClient.SendFrames(streamingUser.Id, count)); + AddStep("send frames", () => spectatorClient.SendFrames(streamingUser.Id, count)); } private void loadSpectatingScreen() { - AddStep("load screen", () => LoadScreen(spectatorScreen = new SoloSpectator(streamingUser))); + AddStep("load spectator", () => LoadScreen(spectatorScreen = new SoloSpectator(streamingUser))); AddUntilStep("wait for screen load", () => spectatorScreen.LoadState == LoadState.Loaded); } + + /// + /// Used for the sole purpose of adding as a resolvable dependency. + /// + private class DependenciesScreen : OsuScreen + { + [Cached(typeof(SpectatorClient))] + public readonly TestSpectatorClient Client; + + public DependenciesScreen(TestSpectatorClient client) + { + Client = client; + } + } } } From 6b2ea1b08f638cc953956ee776e8fd3bcd69f60b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Aug 2021 18:30:04 +0900 Subject: [PATCH 0807/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index b01210a7b1..1866acd248 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b3eeaa4be7..4b0edf990e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 7ad27b222c..e4992e1132 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 1152e15282b6497be0a6a904c61617f0f7e2efc1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Aug 2021 18:29:02 +0900 Subject: [PATCH 0808/2442] Update new usages of `LocalisableEnum` --- .../Rankings/RankingsOverlayHeader.cs | 2 +- osu.Game/Overlays/Rankings/RankingsScope.cs | 33 ++++--------------- .../Rankings/RankingsSortTabControl.cs | 23 ++----------- 3 files changed, 11 insertions(+), 47 deletions(-) diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs index 4916b8b327..417b33ddf6 100644 --- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs @@ -1,10 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Graphics; using osu.Framework.Bindables; using osu.Game.Localisation; using osu.Game.Resources.Localisation.Web; +using osu.Framework.Graphics; using osu.Game.Rulesets; using osu.Game.Users; diff --git a/osu.Game/Overlays/Rankings/RankingsScope.cs b/osu.Game/Overlays/Rankings/RankingsScope.cs index 684408d3a2..e660c2898a 100644 --- a/osu.Game/Overlays/Rankings/RankingsScope.cs +++ b/osu.Game/Overlays/Rankings/RankingsScope.cs @@ -1,42 +1,23 @@ // 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 osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Rankings { - [LocalisableEnum(typeof(RankingsScopeEnumLocalisationMapper))] public enum RankingsScope { + [LocalisableDescription(typeof(RankingsStrings), nameof(RankingsStrings.TypePerformance))] Performance, + + [LocalisableDescription(typeof(RankingsStrings), nameof(RankingsStrings.TypeCharts))] Spotlights, + + [LocalisableDescription(typeof(RankingsStrings), nameof(RankingsStrings.TypeScore))] Score, + + [LocalisableDescription(typeof(RankingsStrings), nameof(RankingsStrings.TypeCountry))] Country } - - public class RankingsScopeEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(RankingsScope value) - { - switch (value) - { - case RankingsScope.Performance: - return RankingsStrings.TypePerformance; - - case RankingsScope.Spotlights: - return RankingsStrings.TypeCharts; - - case RankingsScope.Score: - return RankingsStrings.TypeScore; - - case RankingsScope.Country: - return RankingsStrings.TypeCountry; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } diff --git a/osu.Game/Overlays/Rankings/RankingsSortTabControl.cs b/osu.Game/Overlays/Rankings/RankingsSortTabControl.cs index c04eb5bdd1..f05795b2a2 100644 --- a/osu.Game/Overlays/Rankings/RankingsSortTabControl.cs +++ b/osu.Game/Overlays/Rankings/RankingsSortTabControl.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 osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; @@ -16,28 +15,12 @@ namespace osu.Game.Overlays.Rankings } } - [LocalisableEnum(typeof(RankingsSortCriteriaEnumLocalisationMapper))] public enum RankingsSortCriteria { + [LocalisableDescription(typeof(SortStrings), nameof(SortStrings.All))] All, + + [LocalisableDescription(typeof(SortStrings), nameof(SortStrings.Friends))] Friends } - - public class RankingsSortCriteriaEnumLocalisationMapper : EnumLocalisationMapper - { - public override LocalisableString Map(RankingsSortCriteria value) - { - switch (value) - { - case RankingsSortCriteria.All: - return SortStrings.All; - - case RankingsSortCriteria.Friends: - return SortStrings.Friends; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - } } From ea02571da9e72b38f7b328434fece3a55af48c3f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 3 Aug 2021 12:49:37 +0300 Subject: [PATCH 0809/2442] Inline parameterised test case --- osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMuted.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMuted.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMuted.cs index 30e234dd7a..acb5357c38 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMuted.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMuted.cs @@ -14,15 +14,15 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods /// /// Ensures that a final volume combo of 0 (i.e. "always muted" mode) constantly plays metronome and completely mutes track. /// - [TestCase(0.0, 1.0)] - public void TestZeroFinalCombo(double expectedTrackVolume, double expectedMetronomeVolume) => CreateModTest(new ModTestData + [Test] + public void TestZeroFinalCombo() => CreateModTest(new ModTestData { Mod = new OsuModMuted { MuteComboCount = { Value = 0 }, }, - PassCondition = () => Beatmap.Value.Track.AggregateVolume.Value == expectedTrackVolume && - Player.ChildrenOfType().SingleOrDefault()?.AggregateVolume.Value == expectedMetronomeVolume, + PassCondition = () => Beatmap.Value.Track.AggregateVolume.Value == 0.0 && + Player.ChildrenOfType().SingleOrDefault()?.AggregateVolume.Value == 1.0, }); } } From 0d22b9223b97131ed36f2469fd4f89aa6aabe267 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 3 Aug 2021 12:50:54 +0300 Subject: [PATCH 0810/2442] Add unit test for inverse muting transferal --- .../Mods/TestSceneOsuModMuted.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMuted.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMuted.cs index acb5357c38..c14dc78f38 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMuted.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMuted.cs @@ -24,5 +24,29 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods PassCondition = () => Beatmap.Value.Track.AggregateVolume.Value == 0.0 && Player.ChildrenOfType().SingleOrDefault()?.AggregateVolume.Value == 1.0, }); + + /// + /// Ensures that copying from a normal mod with 0 final combo while originally inversed does not yield incorrect results. + /// + [Test] + public void TestModCopy() + { + OsuModMuted muted = null; + + AddStep("create inversed mod", () => muted = new OsuModMuted + { + MuteComboCount = { Value = 100 }, + InverseMuting = { Value = true }, + }); + + AddStep("copy from normal", () => muted.CopyFrom(new OsuModMuted + { + MuteComboCount = { Value = 0 }, + InverseMuting = { Value = false }, + })); + + AddAssert("mute combo count = 0", () => muted.MuteComboCount.Value == 0); + AddAssert("inverse muting = false", () => muted.InverseMuting.Value == false); + } } } From 118e13227a9b979ee4e5c8822759eb0ca1c8fbd6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Aug 2021 18:37:01 +0900 Subject: [PATCH 0811/2442] Update existing test to make use of `ManualInputManager.Keys` --- .../Visual/Online/TestSceneChatOverlay.cs | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 5e234bdacf..7cfca31167 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -330,22 +330,11 @@ namespace osu.Game.Tests.Visual.Online InputManager.ReleaseKey(Key.AltLeft); } - private void pressCloseDocumentKeys() => pressKeysFor(PlatformAction.DocumentClose); + private void pressCloseDocumentKeys() => InputManager.Keys(PlatformAction.DocumentClose); - private void pressNewTabKeys() => pressKeysFor(PlatformAction.TabNew); + private void pressNewTabKeys() => InputManager.Keys(PlatformAction.TabNew); - private void pressRestoreTabKeys() => pressKeysFor(PlatformAction.TabRestore); - - private void pressKeysFor(PlatformAction type) - { - var binding = host.PlatformKeyBindings.First(b => (PlatformAction)b.Action == type); - - foreach (var k in binding.KeyCombination.Keys) - InputManager.PressKey((Key)k); - - foreach (var k in binding.KeyCombination.Keys) - InputManager.ReleaseKey((Key)k); - } + private void pressRestoreTabKeys() => InputManager.Keys(PlatformAction.TabRestore); private void clickDrawable(Drawable d) { From 063868713e7d518b79eadac5750a3133a02a7f12 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Aug 2021 18:05:43 +0900 Subject: [PATCH 0812/2442] Add ability to create a room using only keyboard input --- .../Match/Components/CreateRoomButton.cs | 39 +++++++++++++++++++ .../CreateMultiplayerMatchButton.cs | 4 +- .../Match/BeatmapSelectionControl.cs | 24 +++++++++++- .../Match/MultiplayerMatchSettingsOverlay.cs | 23 ++++++++++- .../Playlists/CreatePlaylistsRoomButton.cs | 4 +- 5 files changed, 86 insertions(+), 8 deletions(-) create mode 100644 osu.Game/Screens/OnlinePlay/Match/Components/CreateRoomButton.cs diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/CreateRoomButton.cs b/osu.Game/Screens/OnlinePlay/Match/Components/CreateRoomButton.cs new file mode 100644 index 0000000000..711eaa0ca1 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Match/Components/CreateRoomButton.cs @@ -0,0 +1,39 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Input; +using osu.Framework.Input.Bindings; + +namespace osu.Game.Screens.OnlinePlay.Match.Components +{ + public abstract class CreateRoomButton : PurpleTriangleButton, IKeyBindingHandler + { + [BackgroundDependencyLoader] + private void load() + { + Triangles.TriangleScale = 1.5f; + } + + public bool OnPressed(PlatformAction action) + { + if (!Enabled.Value) + return false; + + switch (action) + { + case PlatformAction.DocumentNew: + // might as well also handle new tab. it's a bit of an undefined flow on this screen. + case PlatformAction.TabNew: + Click(); + return true; + } + + return false; + } + + public void OnReleased(PlatformAction action) + { + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs index cc51b5b691..e80923ed47 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs @@ -8,7 +8,7 @@ using osu.Game.Screens.OnlinePlay.Match.Components; namespace osu.Game.Screens.OnlinePlay.Multiplayer { - public class CreateMultiplayerMatchButton : PurpleTriangleButton + public class CreateMultiplayerMatchButton : CreateRoomButton { private IBindable isConnected; private IBindable operationInProgress; @@ -22,8 +22,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [BackgroundDependencyLoader] private void load() { - Triangles.TriangleScale = 1.5f; - Text = "Create room"; isConnected = multiplayerClient.IsConnected.GetBoundCopy(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs index ebe63e26d6..6293751ac0 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs @@ -5,13 +5,15 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Bindings; using osu.Framework.Screens; +using osu.Game.Input.Bindings; using osu.Game.Online.API; using osu.Game.Screens.OnlinePlay.Match.Components; namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { - public class BeatmapSelectionControl : RoomSubScreenComposite + public class BeatmapSelectionControl : RoomSubScreenComposite, IKeyBindingHandler { [Resolved] private MultiplayerMatchSubScreen matchSubScreen { get; set; } @@ -75,5 +77,25 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match else beatmapPanelContainer.Child = new DrawableRoomPlaylistItem(SelectedItem.Value, false, false); } + + public bool OnPressed(GlobalAction action) + { + // only handle keyboard input if there is no current selection. + if (SelectedItem.Value != null) + return false; + + switch (action) + { + case GlobalAction.Select: + selectButton.Click(); + return true; + } + + return false; + } + + public void OnReleased(GlobalAction action) + { + } } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 338d2c9e84..b0e8e73732 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -11,11 +11,13 @@ using osu.Framework.Extensions.ExceptionExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Bindings; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Input.Bindings; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Overlays; @@ -352,7 +354,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match } } - public class CreateOrUpdateButton : TriangleButton + public class CreateOrUpdateButton : TriangleButton, IKeyBindingHandler { [Resolved(typeof(Room), nameof(Room.RoomID))] private Bindable roomId { get; set; } @@ -370,6 +372,25 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match Triangles.ColourLight = colours.YellowLight; Triangles.ColourDark = colours.YellowDark; } + + public bool OnPressed(GlobalAction action) + { + if (!Enabled.Value) + return false; + + switch (action) + { + case GlobalAction.Select: + Click(); + return true; + } + + return false; + } + + public void OnReleased(GlobalAction action) + { + } } } } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/CreatePlaylistsRoomButton.cs b/osu.Game/Screens/OnlinePlay/Playlists/CreatePlaylistsRoomButton.cs index fcb773f8be..a9826a72eb 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/CreatePlaylistsRoomButton.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/CreatePlaylistsRoomButton.cs @@ -6,13 +6,11 @@ using osu.Game.Screens.OnlinePlay.Match.Components; namespace osu.Game.Screens.OnlinePlay.Playlists { - public class CreatePlaylistsRoomButton : PurpleTriangleButton + public class CreatePlaylistsRoomButton : CreateRoomButton { [BackgroundDependencyLoader] private void load() { - Triangles.TriangleScale = 1.5f; - Text = "Create playlist"; } } From 3c7a49f4311f0fb2e7105d85436b0c3d371022c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Aug 2021 18:37:09 +0900 Subject: [PATCH 0813/2442] Add test coverage of keyboard room creation flow --- .../Multiplayer/TestSceneMultiplayer.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 7c151ffac3..1474e0f712 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -8,6 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Testing; @@ -88,6 +89,28 @@ namespace osu.Game.Tests.Visual.Multiplayer // used to test the flow of multiplayer from visual tests. } + [Test] + public void TestCreateRoomViaKeyboard() + { + // create room dialog + AddStep("Press new document", () => InputManager.Keys(PlatformAction.DocumentNew)); + AddUntilStep("wait for settings", () => InputManager.ChildrenOfType().FirstOrDefault() != null); + + // edit playlist item + AddStep("Press select", () => InputManager.Key(Key.Enter)); + AddUntilStep("wait for song select", () => InputManager.ChildrenOfType().FirstOrDefault() != null); + + // select beatmap + AddStep("Press select", () => InputManager.Key(Key.Enter)); + AddUntilStep("wait for return to screen", () => InputManager.ChildrenOfType().FirstOrDefault() == null); + + // create room + AddStep("Press select", () => InputManager.Key(Key.Enter)); + + AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); + AddUntilStep("wait for join", () => client.Room != null); + } + [Test] public void TestCreateRoomWithoutPassword() { From 8b3feaabfcab105dc49ba0861e6f1ccbf528dd4c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 3 Aug 2021 20:07:42 +0900 Subject: [PATCH 0814/2442] Fix more compile errors --- osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs | 2 +- osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 6681703311..893d99fb9d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -401,7 +401,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void createRoom(Func room) { AddUntilStep("wait for lounge", () => multiplayerScreen.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); - AddStep("open room", () => multiplayerScreen.ChildrenOfType().Single().OpenNewRoom(room())); + AddStep("open room", () => multiplayerScreen.ChildrenOfType().Single().Open(room())); AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); AddWaitStep("wait for transition", 2); diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index fa2b2b363f..3c65f46c79 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -318,7 +318,7 @@ namespace osu.Game.Tests.Visual.Navigation PushAndConfirm(() => multiplayer = new TestMultiplayer()); AddUntilStep("wait for lounge", () => multiplayer.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); - AddStep("open room", () => multiplayer.ChildrenOfType().Single().OpenNewRoom()); + AddStep("open room", () => multiplayer.ChildrenOfType().Single().Open()); AddStep("press back button", () => Game.ChildrenOfType().First().Action()); AddWaitStep("wait two frames", 2); } From 56ded4fde640d5589af877e701f1b31bd2deb761 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 3 Aug 2021 20:32:51 +0900 Subject: [PATCH 0815/2442] Fix DrawableRoom test scene --- .../Multiplayer/TestSceneDrawableRoom.cs | 131 ++++++++++-------- 1 file changed, 70 insertions(+), 61 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index c69890ba33..faabed0e17 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -4,6 +4,8 @@ using System; using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; @@ -19,49 +21,78 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneDrawableRoom : OsuTestScene { - public TestSceneDrawableRoom() + [Cached] + private readonly Bindable selectedRoom = new Bindable(); + + [Test] + public void TestMultipleStatuses() { - Child = new FillFlowContainer + AddStep("create rooms", () => { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.9f), - Spacing = new Vector2(10), - Children = new Drawable[] + Child = new FillFlowContainer { - createDrawableRoom(new Room + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.9f), + Spacing = new Vector2(10), + Children = new Drawable[] { - Name = { Value = "Room 1" }, - Status = { Value = new RoomStatusOpen() }, - EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, - }), - createDrawableRoom(new Room - { - Name = { Value = "Room 2" }, - Status = { Value = new RoomStatusPlaying() }, - EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, - }), - createDrawableRoom(new Room - { - Name = { Value = "Room 3" }, - Status = { Value = new RoomStatusEnded() }, - EndDate = { Value = DateTimeOffset.Now }, - }), - createDrawableRoom(new Room - { - Name = { Value = "Room 4 (realtime)" }, - Status = { Value = new RoomStatusOpen() }, - Category = { Value = RoomCategory.Realtime }, - }), - createDrawableRoom(new Room - { - Name = { Value = "Room 4 (spotlight)" }, - Status = { Value = new RoomStatusOpen() }, - Category = { Value = RoomCategory.Spotlight }, - }), - } - }; + createDrawableRoom(new Room + { + Name = { Value = "Room 1" }, + Status = { Value = new RoomStatusOpen() }, + EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, + }), + createDrawableRoom(new Room + { + Name = { Value = "Room 2" }, + Status = { Value = new RoomStatusPlaying() }, + EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, + }), + createDrawableRoom(new Room + { + Name = { Value = "Room 3" }, + Status = { Value = new RoomStatusEnded() }, + EndDate = { Value = DateTimeOffset.Now }, + }), + createDrawableRoom(new Room + { + Name = { Value = "Room 4 (realtime)" }, + Status = { Value = new RoomStatusOpen() }, + Category = { Value = RoomCategory.Realtime }, + }), + createDrawableRoom(new Room + { + Name = { Value = "Room 4 (spotlight)" }, + Status = { Value = new RoomStatusOpen() }, + Category = { Value = RoomCategory.Spotlight }, + }), + } + }; + }); + } + + [Test] + public void TestEnableAndDisablePassword() + { + DrawableRoom drawableRoom = null; + Room room = null; + + AddStep("create room", () => Child = drawableRoom = createDrawableRoom(room = new Room + { + Name = { Value = "Room with password" }, + Status = { Value = new RoomStatusOpen() }, + Category = { Value = RoomCategory.Realtime }, + })); + + AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType().Single().Alpha)); + + AddStep("set password", () => room.Password.Value = "password"); + AddAssert("password icon visible", () => Precision.AlmostEquals(1, drawableRoom.ChildrenOfType().Single().Alpha)); + + AddStep("unset password", () => room.Password.Value = string.Empty); + AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType().Single().Alpha)); } private DrawableRoom createDrawableRoom(Room room) @@ -82,27 +113,5 @@ namespace osu.Game.Tests.Visual.Multiplayer return drawableRoom; } - - [Test] - public void TestEnableAndDisablePassword() - { - DrawableRoom drawableRoom = null; - Room room = null; - - AddStep("create room", () => Child = drawableRoom = new DrawableRoom(room = new Room - { - Name = { Value = "Room with password" }, - Status = { Value = new RoomStatusOpen() }, - Category = { Value = RoomCategory.Realtime }, - }) { MatchingFilter = true }); - - AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType().Single().Alpha)); - - AddStep("set password", () => room.Password.Value = "password"); - AddAssert("password icon visible", () => Precision.AlmostEquals(1, drawableRoom.ChildrenOfType().Single().Alpha)); - - AddStep("unset password", () => room.Password.Value = string.Empty); - AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType().Single().Alpha)); - } } } From 8dd72a9dc6651eda4b536024442e5dc0ff4db96f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 12 Jul 2021 19:34:14 +0300 Subject: [PATCH 0816/2442] Add new difficulty rating colour method --- osu.Game/Graphics/OsuColour.cs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index c0bc8fdb76..7610f84eda 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -1,11 +1,13 @@ // 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 osu.Framework.Extensions.Color4Extensions; using osu.Game.Beatmaps; using osu.Game.Overlays; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; +using osu.Game.Utils; using osuTK.Graphics; namespace osu.Game.Graphics @@ -40,6 +42,33 @@ namespace osu.Game.Graphics } } + public Color4 ForDifficultyRatingNew(DifficultyRating difficulty) + { + switch (difficulty) + { + case DifficultyRating.Easy: + return Color4Extensions.FromHex("4ebfff"); + + case DifficultyRating.Normal: + return Color4Extensions.FromHex("66ff91"); + + case DifficultyRating.Hard: + return Color4Extensions.FromHex("f7e75d"); + + case DifficultyRating.Insane: + return Color4Extensions.FromHex("ff7e68"); + + case DifficultyRating.Expert: + return Color4Extensions.FromHex("fe3c71"); + + case DifficultyRating.ExpertPlus: + return Color4Extensions.FromHex("6662dd"); + + default: + throw new ArgumentOutOfRangeException(nameof(difficulty)); + } + } + /// /// Retrieves the colour for a . /// From d9686332a175c282dfe3633a7df8edb7c00b06b2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 12 Jul 2021 19:35:03 +0300 Subject: [PATCH 0817/2442] Implement new difficulty rating colour spectrum sampling --- osu.Game/Graphics/OsuColour.cs | 20 ++++++++++++++++++ osu.Game/Utils/ColourUtils.cs | 37 ++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 osu.Game/Utils/ColourUtils.cs diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index 7610f84eda..6aac78b765 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -69,6 +69,26 @@ namespace osu.Game.Graphics } } + public Color4 ForStarDifficulty(double starDifficulty) + { + var spectrumPoint = (float)(starDifficulty / 8.00); + + if (spectrumPoint > 1f) + return Color4.Black; + + return ColourUtils.SampleFromLinearGradient(new[] + { + (0.2159f, Color4Extensions.FromHex("4fc0ff")), + (0.2693f, Color4Extensions.FromHex("4fffd5")), + (0.3217f, Color4Extensions.FromHex("7cff4f")), + (0.4111f, Color4Extensions.FromHex("f6f05c")), + (0.5767f, Color4Extensions.FromHex("ff8068")), + (0.7307f, Color4Extensions.FromHex("ff3c71")), + (0.8667f, Color4Extensions.FromHex("6563de")), + (0.9996f, Color4Extensions.FromHex("18158e")), + }, spectrumPoint); + } + /// /// Retrieves the colour for a . /// diff --git a/osu.Game/Utils/ColourUtils.cs b/osu.Game/Utils/ColourUtils.cs new file mode 100644 index 0000000000..515963971d --- /dev/null +++ b/osu.Game/Utils/ColourUtils.cs @@ -0,0 +1,37 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Framework.Utils; +using osuTK.Graphics; + +namespace osu.Game.Utils +{ + public static class ColourUtils + { + /// + /// Samples from a given linear gradient at a certain specified point. + /// + /// The gradient, defining the colour stops and their positions (in [0-1] range) in the gradient. + /// The point to sample the colour at. + /// A sampled from the linear gradient. + public static Color4 SampleFromLinearGradient(IReadOnlyList<(float position, Color4 colour)> gradient, float point) + { + if (point < gradient[0].position) + return gradient[0].colour; + + for (int i = 0; i < gradient.Count - 1; i++) + { + var startStop = gradient[i]; + var endStop = gradient[i + 1]; + + if (point >= endStop.position) + continue; + + return Interpolation.ValueAt(point, startStop.colour, endStop.colour, startStop.position, endStop.position); + } + + return gradient[^1].colour; + } + } +} From 50f6632051de4d0a0d609eb90e5c4ae9b93107dd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 3 Aug 2021 20:38:50 +0900 Subject: [PATCH 0818/2442] Fix duplicate RoomManager --- osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 5ec9202759..fee612c0a5 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -140,7 +140,7 @@ namespace osu.Game.Screens.OnlinePlay } }, new Header(ScreenTitle, screenStack), - RoomManager = CreateRoomManager(), + RoomManager, ongoingOperationTracker } }; From 692e320ea57fc1cf52419448f3fce01aed5eecf7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 14 Jul 2021 01:04:01 +0300 Subject: [PATCH 0819/2442] Add visual test scene --- .../Colours/TestSceneStarDifficultyColours.cs | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 osu.Game.Tests/Visual/Colours/TestSceneStarDifficultyColours.cs diff --git a/osu.Game.Tests/Visual/Colours/TestSceneStarDifficultyColours.cs b/osu.Game.Tests/Visual/Colours/TestSceneStarDifficultyColours.cs new file mode 100644 index 0000000000..c345320e28 --- /dev/null +++ b/osu.Game.Tests/Visual/Colours/TestSceneStarDifficultyColours.cs @@ -0,0 +1,90 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osuTK; + +namespace osu.Game.Tests.Visual.Colours +{ + public class TestSceneStarDifficultyColours : OsuTestScene + { + [Resolved] + private OsuColour colours { get; set; } + + [Test] + public void TestColours() + { + AddStep("load colour displays", () => + { + Child = new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(5f), + ChildrenEnumerable = Enumerable.Range(0, 10).Select(i => new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(10f), + ChildrenEnumerable = Enumerable.Range(0, 10).Select(j => + { + var colour = colours.ForStarDifficulty(1f * i + 0.1f * j); + + return new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0f, 10f), + Children = new Drawable[] + { + new CircularContainer + { + Masking = true, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Size = new Vector2(75f, 25f), + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colour, + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = OsuColour.ForegroundTextColourFor(colour), + Text = colour.ToHex(), + }, + } + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = $"*{(1f * i + 0.1f * j):0.00}", + } + } + }; + }) + }) + }; + }); + } + } +} From 6fd97d67eb9297dbd75467009e511dded79903bd Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 15 Jul 2021 23:57:48 +0300 Subject: [PATCH 0820/2442] Update colour spectrum inline with latest version From https://github.com/ppy/osu-web/pull/7855#issuecomment-880959644, less arbitrary. --- osu.Game/Graphics/OsuColour.cs | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index 6aac78b765..4eb9aa7c94 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -69,25 +69,18 @@ namespace osu.Game.Graphics } } - public Color4 ForStarDifficulty(double starDifficulty) + public Color4 ForStarDifficulty(double starDifficulty) => ColourUtils.SampleFromLinearGradient(new[] { - var spectrumPoint = (float)(starDifficulty / 8.00); - - if (spectrumPoint > 1f) - return Color4.Black; - - return ColourUtils.SampleFromLinearGradient(new[] - { - (0.2159f, Color4Extensions.FromHex("4fc0ff")), - (0.2693f, Color4Extensions.FromHex("4fffd5")), - (0.3217f, Color4Extensions.FromHex("7cff4f")), - (0.4111f, Color4Extensions.FromHex("f6f05c")), - (0.5767f, Color4Extensions.FromHex("ff8068")), - (0.7307f, Color4Extensions.FromHex("ff3c71")), - (0.8667f, Color4Extensions.FromHex("6563de")), - (0.9996f, Color4Extensions.FromHex("18158e")), - }, spectrumPoint); - } + (1.5f, Color4Extensions.FromHex("4fc0ff")), + (2.0f, Color4Extensions.FromHex("4fffd5")), + (2.5f, Color4Extensions.FromHex("7cff4f")), + (3.25f, Color4Extensions.FromHex("f6f05c")), + (4.5f, Color4Extensions.FromHex("ff8068")), + (6.0f, Color4Extensions.FromHex("ff3c71")), + (7.0f, Color4Extensions.FromHex("6563de")), + (8.0f, Color4Extensions.FromHex("18158e")), + (8.0f, Color4.Black), + }, (float)starDifficulty); /// /// Retrieves the colour for a . From 9a5e052dc08c4eae30de3b2021f97cfdc19837b9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 3 Aug 2021 14:17:02 +0300 Subject: [PATCH 0821/2442] Use star difficulty colour spectrum game-wide --- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 6 ++--- osu.Game/Graphics/OsuColour.cs | 25 ------------------- .../Components/StarRatingRangeDisplay.cs | 4 +-- .../Ranking/Expanded/StarRatingDisplay.cs | 4 +-- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 2 +- 5 files changed, 7 insertions(+), 34 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index c62b803d1a..9a3c75dcc6 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -106,7 +106,7 @@ namespace osu.Game.Beatmaps.Drawables Child = background = new Box { RelativeSizeAxes = Axes.Both, - Colour = colours.ForDifficultyRating(beatmap.DifficultyRating) // Default value that will be re-populated once difficulty calculation completes + Colour = colours.ForStarDifficulty(beatmap.StarDifficulty) // Default value that will be re-populated once difficulty calculation completes }, }, new ConstrainedIconContainer @@ -124,7 +124,7 @@ namespace osu.Game.Beatmaps.Drawables else difficultyBindable.Value = new StarDifficulty(beatmap.StarDifficulty, 0); - difficultyBindable.BindValueChanged(difficulty => background.Colour = colours.ForDifficultyRating(difficulty.NewValue.DifficultyRating)); + difficultyBindable.BindValueChanged(difficulty => background.Colour = colours.ForStarDifficulty(difficulty.NewValue.Stars)); } public ITooltip GetCustomTooltip() => new DifficultyIconTooltip(); @@ -271,7 +271,7 @@ namespace osu.Game.Beatmaps.Drawables starDifficulty.BindValueChanged(difficulty => { starRating.Text = $"{difficulty.NewValue.Stars:0.##}"; - difficultyFlow.Colour = colours.ForDifficultyRating(difficulty.NewValue.DifficultyRating, true); + difficultyFlow.Colour = colours.ForStarDifficulty(difficulty.NewValue.Stars); }, true); return true; diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index 4eb9aa7c94..222300d018 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -18,31 +18,6 @@ namespace osu.Game.Graphics public static Color4 Gray(byte amt) => new Color4(amt, amt, amt, 255); public Color4 ForDifficultyRating(DifficultyRating difficulty, bool useLighterColour = false) - { - switch (difficulty) - { - case DifficultyRating.Easy: - return Green; - - default: - case DifficultyRating.Normal: - return Blue; - - case DifficultyRating.Hard: - return Yellow; - - case DifficultyRating.Insane: - return Pink; - - case DifficultyRating.Expert: - return PurpleLight; - - case DifficultyRating.ExpertPlus: - return useLighterColour ? Gray9 : Color4Extensions.FromHex("#121415"); - } - } - - public Color4 ForDifficultyRatingNew(DifficultyRating difficulty) { switch (difficulty) { diff --git a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs index b2e35d7020..8c1b10e3bd 100644 --- a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs @@ -86,8 +86,8 @@ namespace osu.Game.Screens.OnlinePlay.Components minDisplay.Current.Value = minDifficulty; maxDisplay.Current.Value = maxDifficulty; - minBackground.Colour = colours.ForDifficultyRating(minDifficulty.DifficultyRating, true); - maxBackground.Colour = colours.ForDifficultyRating(maxDifficulty.DifficultyRating, true); + minBackground.Colour = colours.ForStarDifficulty(minDifficulty.Stars); + maxBackground.Colour = colours.ForStarDifficulty(maxDifficulty.Stars); } } } diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index e59a0de316..9d325768f3 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -107,9 +107,7 @@ namespace osu.Game.Screens.Ranking.Expanded string fractionPart = starRatingParts[1]; string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; - var rating = Current.Value.DifficultyRating; - - background.Colour = colours.ForDifficultyRating(rating, true); + background.Colour = colours.ForStarDifficulty(Current.Value.Stars); textFlow.Clear(); textFlow.AddText($"{wholePart}", s => diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 763c27bcbb..3779523094 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -503,7 +503,7 @@ namespace osu.Game.Screens.Select { const float full_opacity_ratio = 0.7f; - var difficultyColour = colours.ForDifficultyRating(difficulty.DifficultyRating); + var difficultyColour = colours.ForStarDifficulty(difficulty.Stars); Children = new Drawable[] { From e11b815b82171be2eb98bdac91564154c7f0f13f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Aug 2021 23:08:11 +0900 Subject: [PATCH 0822/2442] Serialise `type` as snake_case --- osu.Game/Online/Rooms/MatchType.cs | 2 ++ osu.Game/Online/Rooms/Room.cs | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/osu.Game/Online/Rooms/MatchType.cs b/osu.Game/Online/Rooms/MatchType.cs index cafa147a61..36f0dc0c81 100644 --- a/osu.Game/Online/Rooms/MatchType.cs +++ b/osu.Game/Online/Rooms/MatchType.cs @@ -7,6 +7,8 @@ namespace osu.Game.Online.Rooms { public enum MatchType { + // used for osu-web deserialization so names shouldn't be changed. + Playlists, [Description("Head to head")] diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index fe7455d964..4bd5b1a788 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -64,6 +64,15 @@ namespace osu.Game.Online.Rooms [JsonIgnore] public readonly Bindable Type = new Bindable(); + // Todo: osu-framework bug (https://github.com/ppy/osu-framework/issues/4106) + [JsonConverter(typeof(SnakeCaseStringEnumConverter))] + [JsonProperty("type")] + private MatchType type + { + get => Type.Value; + set => Type.Value = value; + } + [Cached] [JsonIgnore] public readonly Bindable MaxParticipants = new Bindable(); From b956d325876226a49d508530d39c044c3c672505 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Jul 2021 15:30:57 +0900 Subject: [PATCH 0823/2442] Add the ability to change multiplayer game type --- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 4 +++- osu.Game/Online/Rooms/Room.cs | 2 +- .../Screens/OnlinePlay/Components/DisableableTabControl.cs | 2 +- .../Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs | 4 +--- osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs | 3 ++- osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs | 6 +++++- 6 files changed, 13 insertions(+), 8 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 873be7f49c..fed4564500 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -192,8 +192,9 @@ namespace osu.Game.Online.Multiplayer /// /// The new room name, if any. /// The new password, if any. + /// The type of the match, if any. /// The new room playlist item, if any. - public Task ChangeSettings(Optional name = default, Optional password = default, Optional item = default) + public Task ChangeSettings(Optional name = default, Optional password = default, Optional matchType = default, Optional item = default) { if (Room == null) throw new InvalidOperationException("Must be joined to a match to change settings."); @@ -219,6 +220,7 @@ namespace osu.Game.Online.Multiplayer BeatmapID = item.GetOr(existingPlaylistItem).BeatmapID, BeatmapChecksum = item.GetOr(existingPlaylistItem).Beatmap.Value.MD5Hash, RulesetID = item.GetOr(existingPlaylistItem).RulesetID, + MatchType = matchType.GetOr(Room.Settings.MatchType), RequiredMods = item.HasValue ? item.Value.AsNonNull().RequiredMods.Select(m => new APIMod(m)).ToList() : Room.Settings.RequiredMods, AllowedMods = item.HasValue ? item.Value.AsNonNull().AllowedMods.Select(m => new APIMod(m)).ToList() : Room.Settings.AllowedMods, }); diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index 4bd5b1a788..de90907552 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -61,7 +61,7 @@ namespace osu.Game.Online.Rooms public readonly Bindable Availability = new Bindable(); [Cached] - [JsonIgnore] + [JsonProperty("type")] public readonly Bindable Type = new Bindable(); // Todo: osu-framework bug (https://github.com/ppy/osu-framework/issues/4106) diff --git a/osu.Game/Screens/OnlinePlay/Components/DisableableTabControl.cs b/osu.Game/Screens/OnlinePlay/Components/DisableableTabControl.cs index bbc407e926..2b596da361 100644 --- a/osu.Game/Screens/OnlinePlay/Components/DisableableTabControl.cs +++ b/osu.Game/Screens/OnlinePlay/Components/DisableableTabControl.cs @@ -9,7 +9,7 @@ namespace osu.Game.Screens.OnlinePlay.Components { public abstract class DisableableTabControl : TabControl { - public readonly BindableBool Enabled = new BindableBool(); + public readonly BindableBool Enabled = new BindableBool(true); protected override void AddTabItem(TabItem tab, bool addToDropdown = true) { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 425252f3cf..65ff02f2cc 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -149,7 +149,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match }, new Section("Game type") { - Alpha = disabled_alpha, Child = new FillFlowContainer { AutoSizeAxes = Axes.Y, @@ -161,7 +160,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match TypePicker = new MatchTypePicker { RelativeSizeAxes = Axes.X, - Enabled = { Value = false } }, typeLabel = new OsuSpriteText { @@ -305,7 +303,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match // Otherwise, update the room directly in preparation for it to be submitted to the API on match creation. if (client.Room != null) { - client.ChangeSettings(name: NameField.Text, password: PasswordTextBox.Text).ContinueWith(t => Schedule(() => + client.ChangeSettings(name: NameField.Text, password: PasswordTextBox.Text, matchType: TypePicker.Current.Value).ContinueWith(t => Schedule(() => { if (t.IsCompletedSuccessfully) onSuccess(currentRoom.Value); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs index dbac826954..d906cc8110 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs @@ -58,7 +58,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer new Room { Name = { Value = $"{API.LocalUser}'s awesome room" }, - Category = { Value = RoomCategory.Realtime } + Category = { Value = RoomCategory.Realtime }, + Type = { Value = MatchType.HeadToHead }, }; protected override string ScreenTitle => "Multiplayer"; diff --git a/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs b/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs index 5b132c97fd..4f02651f02 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs @@ -48,7 +48,11 @@ namespace osu.Game.Screens.OnlinePlay.Playlists protected override Room CreateNewRoom() { - return new Room { Name = { Value = $"{API.LocalUser}'s awesome playlist" } }; + return new Room + { + Name = { Value = $"{API.LocalUser}'s awesome playlist" }, + Type = { Value = MatchType.Playlists } + }; } protected override string ScreenTitle => "Playlists"; From 5e59b1325c8ecb10cbd063acaf0d4184f2d3cf7c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 30 Jul 2021 18:11:11 +0900 Subject: [PATCH 0824/2442] Add team display to participant list --- .../Participants/ParticipantPanel.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs index f4a334e9d3..eca3ae0763 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs @@ -17,6 +17,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.Multiplayer; +using osu.Game.Online.Multiplayer.MatchRulesets.TeamVs; using osu.Game.Rulesets; using osu.Game.Screens.Play.HUD; using osu.Game.Users; @@ -37,6 +38,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants private RulesetStore rulesets { get; set; } private SpriteIcon crown; + private Box teamDisplay; + private OsuSpriteText userRankText; private ModDisplay userModsDisplay; private StateDisplay userStateDisplay; @@ -67,6 +70,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants Colour = Color4Extensions.FromHex("#F7E65D"), Alpha = 0 }, + teamDisplay = new Box + { + Colour = Color4.Black, + RelativeSizeAxes = Axes.Y, + Width = 15, + Alpha = 0, + }, new Container { RelativeSizeAxes = Axes.Both, @@ -174,11 +184,36 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants else crown.FadeOut(fade_time); + if (User.MatchRulesetState is TeamVsMatchUserState teamState) + { + teamDisplay.Show(); + teamDisplay.FadeColour(getColourForTeam(teamState.TeamID), 100, Easing.OutQuint); + } + else + { + teamDisplay.Hide(); + } + // If the mods are updated at the end of the frame, the flow container will skip a reflow cycle: https://github.com/ppy/osu-framework/issues/4187 // This looks particularly jarring here, so re-schedule the update to that start of our frame as a fix. Schedule(() => userModsDisplay.Current.Value = User.Mods.Select(m => m.ToMod(ruleset)).ToList()); } + [Resolved] + private OsuColour colours { get; set; } + + private ColourInfo getColourForTeam(int id) + { + switch (id) + { + default: + return colours.Red; + + case 1: + return colours.Blue; + } + } + public MenuItem[] ContextMenuItems { get From 9bfb0f1294e3aa47f99e23e0db999dff570dc66a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Aug 2021 17:06:01 +0900 Subject: [PATCH 0825/2442] Add basic team vs handling to `TestMultiplayerClient` Not sure this is the best place to do so... I can foresee this class getting much larger than we want it to. --- .../Multiplayer/TestMultiplayerClient.cs | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 7deecdfa28..9826d24d01 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -14,6 +14,7 @@ using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.Multiplayer; +using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; using osu.Game.Online.Rooms; using osu.Game.Rulesets.Mods; using osu.Game.Users; @@ -132,6 +133,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Settings = { Name = apiRoom.Name.Value, + MatchType = apiRoom.Type.Value, BeatmapID = apiRoom.Playlist.Last().BeatmapID, RulesetID = apiRoom.Playlist.Last().RulesetID, BeatmapChecksum = apiRoom.Playlist.Last().Beatmap.Value.MD5Hash, @@ -163,6 +165,23 @@ namespace osu.Game.Tests.Visual.Multiplayer foreach (var user in Room.Users.Where(u => u.State == MultiplayerUserState.Ready)) ChangeUserState(user.UserID, MultiplayerUserState.Idle); + + switch (settings.MatchType) + { + case MatchType.HeadToHead: + await ((IMultiplayerClient)this).MatchRoomStateChanged(null).ConfigureAwait(false); + + foreach (var user in Room.Users) + await ((IMultiplayerClient)this).MatchUserStateChanged(user.UserID, null).ConfigureAwait(false); + break; + + case MatchType.TeamVersus: + await ((IMultiplayerClient)this).MatchRoomStateChanged(TeamVersusRoomState.CreateDefault()).ConfigureAwait(false); + + foreach (var user in Room.Users) + await ((IMultiplayerClient)this).MatchUserStateChanged(user.UserID, new TeamVersusUserState()).ConfigureAwait(false); + break; + } } public override Task ChangeState(MultiplayerUserState newState) @@ -192,7 +211,30 @@ namespace osu.Game.Tests.Visual.Multiplayer return Task.CompletedTask; } - public override Task SendMatchRequest(MatchUserRequest request) => Task.CompletedTask; + public override async Task SendMatchRequest(MatchUserRequest request) + { + Debug.Assert(Room != null); + Debug.Assert(LocalUser != null); + + switch (request) + { + case ChangeTeamRequest changeTeam: + + TeamVersusRoomState roomState = (TeamVersusRoomState)Room.MatchState!; + TeamVersusUserState userState = (TeamVersusUserState)LocalUser.MatchState!; + + var targetTeam = roomState.Teams.FirstOrDefault(t => t.ID == changeTeam.TeamID); + + if (targetTeam != null) + { + userState.TeamID = targetTeam.ID; + + await ((IMultiplayerClient)this).MatchUserStateChanged(LocalUser.UserID, userState).ConfigureAwait(false); + } + + break; + } + } public override Task StartMatch() { From b8e878ccc93d9c121e9dc501bda255f5886c718b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Aug 2021 17:07:08 +0900 Subject: [PATCH 0826/2442] Add the ability to change team by clicking current team colour Definitely not the final UX, but it's what people are used to and easy to implement, so it'll do for now. --- .../Participants/ParticipantPanel.cs | 201 ++++++++---------- .../Multiplayer/Participants/TeamDisplay.cs | 130 +++++++++++ 2 files changed, 218 insertions(+), 113 deletions(-) create mode 100644 osu.Game/Screens/OnlinePlay/Multiplayer/Participants/TeamDisplay.cs diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs index eca3ae0763..b2de9763ee 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs @@ -17,7 +17,6 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.Multiplayer; -using osu.Game.Online.Multiplayer.MatchRulesets.TeamVs; using osu.Game.Rulesets; using osu.Game.Screens.Play.HUD; using osu.Game.Users; @@ -38,7 +37,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants private RulesetStore rulesets { get; set; } private SpriteIcon crown; - private Box teamDisplay; private OsuSpriteText userRankText; private ModDisplay userModsDisplay; @@ -59,106 +57,108 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants var backgroundColour = Color4Extensions.FromHex("#33413C"); - InternalChildren = new Drawable[] + InternalChild = new GridContainer { - crown = new SpriteIcon + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Icon = FontAwesome.Solid.Crown, - Size = new Vector2(14), - Colour = Color4Extensions.FromHex("#F7E65D"), - Alpha = 0 + new Dimension(GridSizeMode.Absolute, 14), + new Dimension(GridSizeMode.AutoSize), + new Dimension() }, - teamDisplay = new Box + Content = new[] { - Colour = Color4.Black, - RelativeSizeAxes = Axes.Y, - Width = 15, - Alpha = 0, - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = 24 }, - Child = new Container + new Drawable[] { - RelativeSizeAxes = Axes.Both, - Masking = true, - CornerRadius = 5, - Children = new Drawable[] + crown = new SpriteIcon { - new Box + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Icon = FontAwesome.Solid.Crown, + Size = new Vector2(14), + Colour = Color4Extensions.FromHex("#F7E65D"), + Alpha = 0 + }, + new TeamDisplay(user), + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 5, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Colour = backgroundColour - }, - new UserCoverBackground - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - RelativeSizeAxes = Axes.Both, - Width = 0.75f, - User = user, - Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(0), Color4.White.Opacity(0.25f)) - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Spacing = new Vector2(10), - Direction = FillDirection.Horizontal, - Children = new Drawable[] + new Box { - new UpdateableAvatar + RelativeSizeAxes = Axes.Both, + Colour = backgroundColour + }, + new UserCoverBackground + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.Both, + Width = 0.75f, + User = user, + Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(0), Color4.White.Opacity(0.25f)) + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Spacing = new Vector2(10), + Direction = FillDirection.Horizontal, + Children = new Drawable[] { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit, - User = user - }, - new UpdateableFlag - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Size = new Vector2(30, 20), - Country = user?.Country - }, - new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 18), - Text = user?.Username - }, - userRankText = new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Font = OsuFont.GetFont(size: 14), + new UpdateableAvatar + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + User = user + }, + new UpdateableFlag + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(30, 20), + Country = user?.Country + }, + new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 18), + Text = user?.Username + }, + userRankText = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Font = OsuFont.GetFont(size: 14), + } } - } - }, - new Container - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Right = 70 }, - Child = userModsDisplay = new ModDisplay + }, + new Container { - Scale = new Vector2(0.5f), - ExpansionMode = ExpansionMode.AlwaysContracted, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Right = 70 }, + Child = userModsDisplay = new ModDisplay + { + Scale = new Vector2(0.5f), + ExpansionMode = ExpansionMode.AlwaysContracted, + } + }, + userStateDisplay = new StateDisplay + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Margin = new MarginPadding { Right = 10 }, } - }, - userStateDisplay = new StateDisplay - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Margin = new MarginPadding { Right = 10 }, } } - } + }, } }; } @@ -184,36 +184,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants else crown.FadeOut(fade_time); - if (User.MatchRulesetState is TeamVsMatchUserState teamState) - { - teamDisplay.Show(); - teamDisplay.FadeColour(getColourForTeam(teamState.TeamID), 100, Easing.OutQuint); - } - else - { - teamDisplay.Hide(); - } - // If the mods are updated at the end of the frame, the flow container will skip a reflow cycle: https://github.com/ppy/osu-framework/issues/4187 // This looks particularly jarring here, so re-schedule the update to that start of our frame as a fix. Schedule(() => userModsDisplay.Current.Value = User.Mods.Select(m => m.ToMod(ruleset)).ToList()); } - [Resolved] - private OsuColour colours { get; set; } - - private ColourInfo getColourForTeam(int id) - { - switch (id) - { - default: - return colours.Red; - - case 1: - return colours.Blue; - } - } - public MenuItem[] ContextMenuItems { get diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/TeamDisplay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/TeamDisplay.cs new file mode 100644 index 0000000000..fd4ce404f1 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/TeamDisplay.cs @@ -0,0 +1,130 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Online.Multiplayer; +using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; +using osu.Game.Users; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants +{ + internal class TeamDisplay : MultiplayerRoomComposite + { + private readonly User user; + private Drawable box; + + [Resolved] + private OsuColour colours { get; set; } + + [Resolved] + private MultiplayerClient client { get; set; } + + public TeamDisplay(User user) + { + this.user = user; + + RelativeSizeAxes = Axes.Y; + Width = 15; + + Margin = new MarginPadding { Horizontal = 3 }; + } + + [BackgroundDependencyLoader] + private void load() + { + box = new Container + { + RelativeSizeAxes = Axes.Both, + CornerRadius = 5, + Masking = true, + Alpha = 0, + Scale = new Vector2(0, 1), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Child = new Box + { + Colour = Color4.White, + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + }; + + if (user.Id == client.LocalUser?.UserID) + { + InternalChild = new OsuClickableContainer + { + RelativeSizeAxes = Axes.Both, + TooltipText = "Change team", + Action = changeTeam, + Child = box + }; + } + else + { + InternalChild = box; + } + } + + private void changeTeam() + { + client.SendMatchRequest(new ChangeTeamRequest + { + TeamID = ((client.LocalUser?.MatchState as TeamVersusUserState)?.TeamID + 1) % 2 ?? 0, + }); + } + + private int? displayedTeam; + + protected override void OnRoomUpdated() + { + base.OnRoomUpdated(); + + // we don't have a way of knowing when an individual user's state has updated, so just handle on RoomUpdated for now. + + var userRoomState = Room?.Users.FirstOrDefault(u => u.UserID == user.Id)?.MatchState; + + const double duration = 400; + + int? newTeam = (userRoomState as TeamVersusUserState)?.TeamID; + + if (newTeam == displayedTeam) + return; + + displayedTeam = newTeam; + + if (displayedTeam != null) + { + box.FadeIn(duration); + box.FadeColour(getColourForTeam(displayedTeam.Value), duration, Easing.OutQuint); + box.ScaleTo(new Vector2(box.Scale.X < 0 ? 1 : -1, 1), duration, Easing.OutQuint); + } + else + { + box.ScaleTo(new Vector2(0, 1), duration, Easing.OutQuint); + box.FadeOut(duration); + } + } + + private ColourInfo getColourForTeam(int id) + { + switch (id) + { + default: + return colours.Red; + + case 1: + return colours.Blue; + } + } + } +} From a0119f8cd6b8ca3ec2a67ed916cfa67cacd5162b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Aug 2021 18:31:56 +0900 Subject: [PATCH 0827/2442] Add basic test coverage --- .../Visual/Multiplayer/TestSceneTeamVersus.cs | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs new file mode 100644 index 0000000000..bbb1ea5fa0 --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs @@ -0,0 +1,138 @@ +// 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.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Platform; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Database; +using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; +using osu.Game.Screens; +using osu.Game.Screens.OnlinePlay.Components; +using osu.Game.Screens.OnlinePlay.Multiplayer; +using osu.Game.Screens.OnlinePlay.Multiplayer.Match; +using osu.Game.Tests.Resources; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneTeamVersus : ScreenTestScene + { + private BeatmapManager beatmaps; + private RulesetStore rulesets; + private BeatmapSetInfo importedSet; + + private DependenciesScreen dependenciesScreen; + private TestMultiplayer multiplayerScreen; + private TestMultiplayerClient client; + + [Cached(typeof(UserLookupCache))] + private UserLookupCache lookupCache = new TestUserLookupCache(); + + [BackgroundDependencyLoader] + private void load(GameHost host, AudioManager audio) + { + Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); + Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); + } + + public override void SetUpSteps() + { + base.SetUpSteps(); + + AddStep("import beatmap", () => + { + beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); + importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); + }); + + AddStep("create multiplayer screen", () => multiplayerScreen = new TestMultiplayer()); + + AddStep("load dependencies", () => + { + client = new TestMultiplayerClient(multiplayerScreen.RoomManager); + + // The screen gets suspended so it stops receiving updates. + Child = client; + + LoadScreen(dependenciesScreen = new DependenciesScreen(client)); + }); + + AddUntilStep("wait for dependencies to load", () => dependenciesScreen.IsLoaded); + + AddStep("load multiplayer", () => LoadScreen(multiplayerScreen)); + AddUntilStep("wait for multiplayer to load", () => multiplayerScreen.IsLoaded); + AddUntilStep("wait for lounge to load", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); + } + + [Test] + public void TestChangeTypeViaMatchSettings() + { + createRoom(() => new Room + { + Name = { Value = "Test Room" }, + Playlist = + { + new PlaylistItem + { + Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + } + } + }); + + AddAssert("room type is head to head", () => client.Room?.Settings.MatchType == MatchType.HeadToHead); + + AddStep("change to team vs", () => client.ChangeSettings(matchType: MatchType.TeamVersus)); + + AddAssert("room type is team vs", () => client.Room?.Settings.MatchType == MatchType.TeamVersus); + } + + private void createRoom(Func room) + { + AddStep("open room", () => + { + multiplayerScreen.OpenNewRoom(room()); + }); + + AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); + AddWaitStep("wait for transition", 2); + + AddStep("create room", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().Single()); + InputManager.Click(MouseButton.Left); + }); + + AddUntilStep("wait for join", () => client.Room != null); + } + + /// + /// Used for the sole purpose of adding as a resolvable dependency. + /// + private class DependenciesScreen : OsuScreen + { + [Cached(typeof(MultiplayerClient))] + public readonly TestMultiplayerClient Client; + + public DependenciesScreen(TestMultiplayerClient client) + { + Client = client; + } + } + + private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer + { + public new TestRequestHandlingMultiplayerRoomManager RoomManager { get; private set; } + + protected override RoomManager CreateRoomManager() => RoomManager = new TestRequestHandlingMultiplayerRoomManager(); + } + } +} From 75426f84f19ac34a6d52a0704957843d053f35a2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Aug 2021 17:08:19 +0900 Subject: [PATCH 0828/2442] Fire initial match user states in `TestMultiplayerClient` --- .../Visual/Multiplayer/TestSceneTeamVersus.cs | 22 +++++++++ .../Online/Multiplayer/MultiplayerClient.cs | 9 ++++ .../Multiplayer/TestMultiplayerClient.cs | 47 ++++++++++++------- 3 files changed, 62 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs index bbb1ea5fa0..7d9d35b896 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs @@ -11,6 +11,7 @@ using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Online.Multiplayer; +using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; using osu.Game.Online.Rooms; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; @@ -72,6 +73,27 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for lounge to load", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); } + [Test] + public void TestCreateWithType() + { + createRoom(() => new Room + { + Name = { Value = "Test Room" }, + Type = { Value = MatchType.TeamVersus }, + Playlist = + { + new PlaylistItem + { + Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + } + } + }); + + AddAssert("room type is team vs", () => client.Room?.Settings.MatchType == MatchType.TeamVersus); + AddAssert("user state arrived", () => client.Room?.Users.FirstOrDefault()?.MatchState is TeamVersusUserState); + } + [Test] public void TestChangeTypeViaMatchSettings() { diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index fed4564500..bffb2d341a 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -142,6 +142,8 @@ namespace osu.Game.Online.Multiplayer APIRoom = room; foreach (var user in joinedRoom.Users) updateUserPlayingState(user.UserID, user.State); + + OnRoomJoined(); }, cancellationSource.Token).ConfigureAwait(false); // Update room settings. @@ -149,6 +151,13 @@ namespace osu.Game.Online.Multiplayer }, cancellationSource.Token).ConfigureAwait(false); } + /// + /// Fired when the room join sequence is complete + /// + protected virtual void OnRoomJoined() + { + } + /// /// Joins the with a given ID. /// diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 9826d24d01..43aadf5acb 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -153,6 +153,14 @@ namespace osu.Game.Tests.Visual.Multiplayer return Task.FromResult(room); } + protected override void OnRoomJoined() + { + Debug.Assert(Room != null); + + // emulate the server sending this after the join room. scheduler required to make sure the join room event is fired first (in Join). + changeMatchType(Room.Settings.MatchType).Wait(); + } + protected override Task LeaveRoomInternal() => Task.CompletedTask; public override Task TransferHost(int userId) => ((IMultiplayerClient)this).HostChanged(userId); @@ -166,22 +174,7 @@ namespace osu.Game.Tests.Visual.Multiplayer foreach (var user in Room.Users.Where(u => u.State == MultiplayerUserState.Ready)) ChangeUserState(user.UserID, MultiplayerUserState.Idle); - switch (settings.MatchType) - { - case MatchType.HeadToHead: - await ((IMultiplayerClient)this).MatchRoomStateChanged(null).ConfigureAwait(false); - - foreach (var user in Room.Users) - await ((IMultiplayerClient)this).MatchUserStateChanged(user.UserID, null).ConfigureAwait(false); - break; - - case MatchType.TeamVersus: - await ((IMultiplayerClient)this).MatchRoomStateChanged(TeamVersusRoomState.CreateDefault()).ConfigureAwait(false); - - foreach (var user in Room.Users) - await ((IMultiplayerClient)this).MatchUserStateChanged(user.UserID, new TeamVersusUserState()).ConfigureAwait(false); - break; - } + await changeMatchType(settings.MatchType).ConfigureAwait(false); } public override Task ChangeState(MultiplayerUserState newState) @@ -260,5 +253,27 @@ namespace osu.Game.Tests.Visual.Multiplayer return Task.FromResult(set); } + + private async Task changeMatchType(MatchType type) + { + Debug.Assert(Room != null); + + switch (type) + { + case MatchType.HeadToHead: + await ((IMultiplayerClient)this).MatchRoomStateChanged(null).ConfigureAwait(false); + + foreach (var user in Room.Users) + await ((IMultiplayerClient)this).MatchUserStateChanged(user.UserID, null).ConfigureAwait(false); + break; + + case MatchType.TeamVersus: + await ((IMultiplayerClient)this).MatchRoomStateChanged(TeamVersusRoomState.CreateDefault()).ConfigureAwait(false); + + foreach (var user in Room.Users) + await ((IMultiplayerClient)this).MatchUserStateChanged(user.UserID, new TeamVersusUserState()).ConfigureAwait(false); + break; + } + } } } From 69e6c08cc26594947b039a5a4cfc05b489a808d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Aug 2021 17:26:10 +0900 Subject: [PATCH 0829/2442] Add test coverage of changing teams via clicking --- .../Visual/Multiplayer/TestSceneTeamVersus.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs index 7d9d35b896..e19665497d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs @@ -19,6 +19,7 @@ using osu.Game.Screens; using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; +using osu.Game.Screens.OnlinePlay.Multiplayer.Participants; using osu.Game.Tests.Resources; using osuTK.Input; @@ -94,6 +95,36 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("user state arrived", () => client.Room?.Users.FirstOrDefault()?.MatchState is TeamVersusUserState); } + [Test] + public void TestChangeTeamsViaButton() + { + createRoom(() => new Room + { + Name = { Value = "Test Room" }, + Type = { Value = MatchType.TeamVersus }, + Playlist = + { + new PlaylistItem + { + Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + } + } + }); + + AddAssert("user on team 0", () => (client.Room?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 0); + + AddStep("press button", () => + { + InputManager.MoveMouseTo(multiplayerScreen.ChildrenOfType().First()); + InputManager.Click(MouseButton.Left); + }); + AddAssert("user on team 1", () => (client.Room?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 1); + + AddStep("press button", () => InputManager.Click(MouseButton.Left)); + AddAssert("user on team 0", () => (client.Room?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 0); + } + [Test] public void TestChangeTypeViaMatchSettings() { From aa320c70a71254e00e267495467f6bc7687f08a5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Aug 2021 23:13:12 +0900 Subject: [PATCH 0830/2442] Improve show/hide animation and add more padding around the crown --- .../Multiplayer/Participants/ParticipantPanel.cs | 2 +- .../Multiplayer/Participants/TeamDisplay.cs | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs index b2de9763ee..89431445d3 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs @@ -62,7 +62,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants RelativeSizeAxes = Axes.Both, ColumnDimensions = new[] { - new Dimension(GridSizeMode.Absolute, 14), + new Dimension(GridSizeMode.Absolute, 18), new Dimension(GridSizeMode.AutoSize), new Dimension() }, diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/TeamDisplay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/TeamDisplay.cs index fd4ce404f1..5a7073f9de 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/TeamDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/TeamDisplay.cs @@ -36,6 +36,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants Width = 15; Margin = new MarginPadding { Horizontal = 3 }; + + Alpha = 0; + Scale = new Vector2(0, 1); } [BackgroundDependencyLoader] @@ -46,7 +49,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants RelativeSizeAxes = Axes.Both, CornerRadius = 5, Masking = true, - Alpha = 0, Scale = new Vector2(0, 1), Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -104,14 +106,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants if (displayedTeam != null) { - box.FadeIn(duration); box.FadeColour(getColourForTeam(displayedTeam.Value), duration, Easing.OutQuint); box.ScaleTo(new Vector2(box.Scale.X < 0 ? 1 : -1, 1), duration, Easing.OutQuint); + + this.ScaleTo(Vector2.One, duration, Easing.OutQuint); + this.FadeIn(duration); } else { - box.ScaleTo(new Vector2(0, 1), duration, Easing.OutQuint); - box.FadeOut(duration); + this.ScaleTo(new Vector2(0, 1), duration, Easing.OutQuint); + this.FadeOut(duration); } } From cb72667aa844e19d053074686fed562b6f2a45c3 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 3 Aug 2021 22:10:33 +0700 Subject: [PATCH 0831/2442] add typeface inter in osu font --- osu.Game/Graphics/OsuFont.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/OsuFont.cs b/osu.Game/Graphics/OsuFont.cs index 7c78141b4d..b6090d0e1a 100644 --- a/osu.Game/Graphics/OsuFont.cs +++ b/osu.Game/Graphics/OsuFont.cs @@ -21,6 +21,8 @@ namespace osu.Game.Graphics public static FontUsage Torus => GetFont(Typeface.Torus, weight: FontWeight.Regular); + public static FontUsage Inter => GetFont(Typeface.Inter, weight: FontWeight.Regular); + /// /// Retrieves a . /// @@ -54,6 +56,9 @@ namespace osu.Game.Graphics case Typeface.Torus: return "Torus"; + + case Typeface.Inter: + return "Inter"; } return null; @@ -107,7 +112,8 @@ namespace osu.Game.Graphics public enum Typeface { Venera, - Torus + Torus, + Inter, } public enum FontWeight From ed94266a5d9a8d8845592372c2b6037a1bd8e778 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 3 Aug 2021 22:14:44 +0700 Subject: [PATCH 0832/2442] change markdown container font --- osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs | 2 +- osu.Game/Graphics/Containers/Markdown/OsuMarkdownHeading.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs index 81f30bd406..296c600771 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs @@ -48,7 +48,7 @@ namespace osu.Game.Graphics.Containers.Markdown public override SpriteText CreateSpriteText() => new OsuSpriteText { - Font = OsuFont.GetFont(size: 14), + Font = OsuFont.GetFont(Typeface.Inter, size: 14, weight: FontWeight.Regular), }; public override MarkdownTextFlowContainer CreateTextFlow() => new OsuMarkdownTextFlowContainer(); diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownHeading.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownHeading.cs index a3a86df678..e4685a2935 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownHeading.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownHeading.cs @@ -70,7 +70,7 @@ namespace osu.Game.Graphics.Containers.Markdown public FontWeight FontWeight; protected override SpriteText CreateSpriteText() - => base.CreateSpriteText().With(t => t.Font = t.Font.With(size: FontSize, weight: FontWeight)); + => base.CreateSpriteText().With(t => t.Font = t.Font.With(Typeface.Torus, size: FontSize, weight: FontWeight)); } } } From d22f2ececbf007d27bc05dee4b8acb560d752460 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 3 Aug 2021 22:17:45 +0700 Subject: [PATCH 0833/2442] adjust wiki main page font --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 2 +- osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index c4c0b83ef4..3fb0aa450e 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -59,7 +59,7 @@ namespace osu.Game.Overlays.Wiki Child = new OsuSpriteText { Text = blurbNode.InnerText, - Font = OsuFont.GetFont(size: 12), + Font = OsuFont.GetFont(Typeface.Inter, size: 12, weight: FontWeight.Light), Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, } diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index e1c00a955b..7e7e005586 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs @@ -89,7 +89,7 @@ namespace osu.Game.Overlays.Wiki DocumentMargin = new MarginPadding(0); } - public override SpriteText CreateSpriteText() => base.CreateSpriteText().With(t => t.Font = t.Font.With(weight: FontWeight.Bold)); + public override SpriteText CreateSpriteText() => base.CreateSpriteText().With(t => t.Font = t.Font.With(Typeface.Torus, weight: FontWeight.Bold)); public override MarkdownTextFlowContainer CreateTextFlow() => base.CreateTextFlow().With(f => f.TextAnchor = Anchor.TopCentre); From b5970d5cdcc18a35603b0f788cfe433ff2a8511c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 4 Aug 2021 01:40:39 +0300 Subject: [PATCH 0834/2442] Handle pitch black background case --- .../Screens/Ranking/Expanded/StarRatingDisplay.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index 9d325768f3..2b86100be8 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -23,6 +23,7 @@ namespace osu.Game.Screens.Ranking.Expanded public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue { private Box background; + private FillFlowContainer content; private OsuTextFlowContainer textFlow; [Resolved] @@ -64,7 +65,7 @@ namespace osu.Game.Screens.Ranking.Expanded }, } }, - new FillFlowContainer + content = new FillFlowContainer { AutoSizeAxes = Axes.Both, Padding = new MarginPadding { Horizontal = 8, Vertical = 4 }, @@ -78,7 +79,6 @@ namespace osu.Game.Screens.Ranking.Expanded Origin = Anchor.CentreLeft, Size = new Vector2(7), Icon = FontAwesome.Solid.Star, - Colour = Color4.Black }, textFlow = new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) { @@ -107,19 +107,20 @@ namespace osu.Game.Screens.Ranking.Expanded string fractionPart = starRatingParts[1]; string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; - background.Colour = colours.ForStarDifficulty(Current.Value.Stars); + var stars = Current.Value.Stars; + + background.Colour = colours.ForStarDifficulty(stars); + content.Colour = stars >= 6.5 ? colours.Orange1 : Color4.Black; textFlow.Clear(); textFlow.AddText($"{wholePart}", s => { - s.Colour = Color4.Black; s.Font = s.Font.With(size: 14); s.UseFullGlyphHeight = false; }); textFlow.AddText($"{separator}{fractionPart}", s => { - s.Colour = Color4.Black; s.Font = s.Font.With(size: 7); s.UseFullGlyphHeight = false; }); From 4e303b2aa1b96fa20b2b97d1666393d129efb1f5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 4 Aug 2021 01:52:26 +0300 Subject: [PATCH 0835/2442] Add xmldoc and source link --- osu.Game/Graphics/OsuColour.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index 222300d018..98629a538e 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -17,6 +17,12 @@ namespace osu.Game.Graphics public static Color4 Gray(float amt) => new Color4(amt, amt, amt, 1f); public static Color4 Gray(byte amt) => new Color4(amt, amt, amt, 255); + /// + /// Retrieves the colour for a . + /// + /// + /// Sourced from the @diff-{rating} variables in https://github.com/ppy/osu-web/blob/71fbab8936d79a7929d13854f5e854b4f383b236/resources/assets/less/variables.less. + /// public Color4 ForDifficultyRating(DifficultyRating difficulty, bool useLighterColour = false) { switch (difficulty) From 65db9d664e19ecc115a11b1689ed6b146650b9f8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 4 Aug 2021 01:52:34 +0300 Subject: [PATCH 0836/2442] Match osu-web colour for `DifficultyRating.Hard` --- osu.Game/Graphics/OsuColour.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index 98629a538e..d6c5bc8754 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -34,7 +34,7 @@ namespace osu.Game.Graphics return Color4Extensions.FromHex("66ff91"); case DifficultyRating.Hard: - return Color4Extensions.FromHex("f7e75d"); + return Color4Extensions.FromHex("f7e85d"); case DifficultyRating.Insane: return Color4Extensions.FromHex("ff7e68"); From db1f43f6eb887c2d4c38ad796f00dd075821fb25 Mon Sep 17 00:00:00 2001 From: 02Naitsirk <57512128+02Naitsirk@users.noreply.github.com> Date: Tue, 3 Aug 2021 18:57:33 -0400 Subject: [PATCH 0837/2442] Multiply star rating by a constant --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index b75bf81071..da879cb02e 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty double baseAimPerformance = Math.Pow(5 * Math.Max(1, aimRating / 0.0675) - 4, 3) / 100000; double baseSpeedPerformance = Math.Pow(5 * Math.Max(1, speedRating / 0.0675) - 4, 3) / 100000; double basePerformance = Math.Pow(Math.Pow(baseAimPerformance, 1.1) + Math.Pow(baseSpeedPerformance, 1.1), 1 / 1.1); - double starRating = basePerformance > 0.00001 ? 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0; + double starRating = basePerformance > 0.00001 ? Math.Cbrt(1.12) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0; HitWindows hitWindows = new OsuHitWindows(); hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty); From bec0f379a7cd050692d8f221160eeb345b6b5b5c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 4 Aug 2021 02:13:19 +0300 Subject: [PATCH 0838/2442] Round star difficulty to two fractional digits during sampling --- osu.Game/Graphics/OsuColour.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index d6c5bc8754..1f87c06dd2 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -61,7 +61,7 @@ namespace osu.Game.Graphics (7.0f, Color4Extensions.FromHex("6563de")), (8.0f, Color4Extensions.FromHex("18158e")), (8.0f, Color4.Black), - }, (float)starDifficulty); + }, (float)Math.Round(starDifficulty, 2, MidpointRounding.AwayFromZero)); /// /// Retrieves the colour for a . From 401835a3d892ec1287183863ce42370a2afcda9b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Aug 2021 13:13:47 +0900 Subject: [PATCH 0839/2442] Add missing event glue --- osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs index 00f30463a5..8b8d10ce4f 100644 --- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs @@ -60,6 +60,7 @@ namespace osu.Game.Online.Multiplayer connection.On(nameof(IMultiplayerClient.UserBeatmapAvailabilityChanged), ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged); connection.On(nameof(IMultiplayerClient.MatchRoomStateChanged), ((IMultiplayerClient)this).MatchRoomStateChanged); connection.On(nameof(IMultiplayerClient.MatchUserStateChanged), ((IMultiplayerClient)this).MatchUserStateChanged); + connection.On(nameof(IMultiplayerClient.MatchEvent), ((IMultiplayerClient)this).MatchEvent); }; IsConnected.BindTo(connector.IsConnected); From c06fffb56ac73219ba0f796fac0f5f39ac77ba93 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 4 Aug 2021 13:49:13 +0900 Subject: [PATCH 0840/2442] Increase background image resolution --- osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index c2955cec94..3112b2cc30 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -190,7 +190,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components RelativeSizeAxes = Axes.Both, Colour = Color4Extensions.FromHex(@"#27302E"), }, - new OnlinePlayBackgroundSprite(BeatmapSetCoverType.List) + new OnlinePlayBackgroundSprite(BeatmapSetCoverType.Cover) { RelativeSizeAxes = Axes.Both }, From c76edd888716fbe6c0a33c00d97d4897258dffd9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Aug 2021 17:24:13 +0900 Subject: [PATCH 0841/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 1866acd248..591db6e2c2 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 4b0edf990e..0c26bce8c9 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index e4992e1132..b7f252820c 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 9b9dacf3fe1d2cf5f8d89c9976140323449200b1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Aug 2021 17:27:44 +0900 Subject: [PATCH 0842/2442] Update usages of `Drawable.Click()` --- .../Visual/Editing/TestSceneHitObjectComposer.cs | 2 +- .../Visual/Gameplay/TestSceneGameplayMenuOverlay.cs | 2 +- .../Multiplayer/TestSceneLoungeRoomsContainer.cs | 2 +- .../Visual/Multiplayer/TestSceneMultiplayer.cs | 6 +++--- .../Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs | 2 +- .../Visual/Online/TestSceneAccountCreationOverlay.cs | 2 +- osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs | 2 +- .../Visual/Online/TestSceneShowMoreButton.cs | 6 +++--- osu.Game.Tests/Visual/Online/TestSceneVotePill.cs | 6 +++--- .../Playlists/TestScenePlaylistsLoungeSubScreen.cs | 2 +- .../Playlists/TestScenePlaylistsRoomSubScreen.cs | 2 +- .../Visual/Settings/TestSceneKeyBindingPanel.cs | 4 ++-- .../Visual/UserInterface/TestSceneModSelectOverlay.cs | 4 ++-- .../Visual/UserInterface/TestSceneModSettings.cs | 2 +- .../Screens/TestSceneGameplayScreen.cs | 2 +- osu.Game/Graphics/UserInterface/BackButton.cs | 2 +- osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs | 2 +- osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs | 2 +- osu.Game/Overlays/Comments/CommentEditor.cs | 2 +- osu.Game/Overlays/Dialog/PopupDialog.cs | 6 +++--- osu.Game/Overlays/DialogOverlay.cs | 2 +- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 4 ++-- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 2 +- osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs | 2 +- osu.Game/Screens/Menu/ButtonSystem.cs | 10 +++++----- .../OnlinePlay/Lounge/Components/DrawableRoom.cs | 2 +- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 4 ++-- osu.Game/Screens/Play/PauseOverlay.cs | 2 +- osu.Game/Screens/Play/SkipOverlay.cs | 2 +- osu.Game/Screens/Select/FooterButton.cs | 2 +- osu.Game/Screens/Select/FooterButtonRandom.cs | 2 +- .../Screens/Select/Options/BeatmapOptionsOverlay.cs | 2 +- 32 files changed, 48 insertions(+), 48 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs index 550896270a..c758bd1707 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs @@ -75,7 +75,7 @@ namespace osu.Game.Tests.Visual.Editing AddAssert("Hitcircle button not clickable", () => !hitObjectComposer.ChildrenOfType().First(d => d.Button.Label == "HitCircle").Enabled.Value); AddStep("Add timing point", () => editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint())); AddAssert("Hitcircle button is clickable", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Label == "HitCircle").Enabled.Value); - AddStep("Change to hitcircle", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Label == "HitCircle").Click()); + AddStep("Change to hitcircle", () => hitObjectComposer.ChildrenOfType().First(d => d.Button.Label == "HitCircle").TriggerClick()); AddAssert("Tool changed", () => hitObjectComposer.ChildrenOfType().First().CurrentTool is HitCircleCompositionTool); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs index ed40a83831..477ac70501 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs @@ -228,7 +228,7 @@ namespace osu.Game.Tests.Visual.Gameplay var lastAction = pauseOverlay.OnRetry; pauseOverlay.OnRetry = () => triggered = true; - getButton(1).Click(); + getButton(1).TriggerClick(); pauseOverlay.OnRetry = lastAction; }); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs index 0a23550f5d..bcbdcd2a4f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("has 2 rooms", () => container.Rooms.Count == 2); AddAssert("first room removed", () => container.Rooms.All(r => r.Room.RoomID.Value != 0)); - AddStep("select first room", () => container.Rooms.First().Click()); + AddStep("select first room", () => container.Rooms.First().TriggerClick()); AddAssert("first room selected", () => checkRoomSelected(RoomManager.Rooms.First())); } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 7c151ffac3..a535f8c3c3 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -209,7 +209,7 @@ namespace osu.Game.Tests.Visual.Multiplayer DrawableRoom.PasswordEntryPopover passwordEntryPopover = null; AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null); AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password"); - AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().Click()); + AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().TriggerClick()); AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); AddUntilStep("wait for join", () => client.Room != null); @@ -373,7 +373,7 @@ namespace osu.Game.Tests.Visual.Multiplayer } }); - AddStep("open mod overlay", () => this.ChildrenOfType().ElementAt(2).Click()); + AddStep("open mod overlay", () => this.ChildrenOfType().ElementAt(2).TriggerClick()); AddStep("invoke on back button", () => multiplayerScreen.OnBackButton()); @@ -381,7 +381,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("dialog overlay is hidden", () => DialogOverlay.State.Value == Visibility.Hidden); - testLeave("lounge tab item", () => this.ChildrenOfType.BreadcrumbTabItem>().First().Click()); + testLeave("lounge tab item", () => this.ChildrenOfType.BreadcrumbTabItem>().First().TriggerClick()); testLeave("back button", () => multiplayerScreen.OnBackButton()); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs index 4ea635fd3e..c66d5429d6 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs @@ -74,7 +74,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("attempt join room", () => InputManager.Key(Key.Enter)); AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null); AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password"); - AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().Click()); + AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().TriggerClick()); AddAssert("room join requested", () => lastJoinedRoom == RoomManager.Rooms.First()); AddAssert("room join password correct", () => lastJoinedPassword == "password"); diff --git a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs index 437c5b07c9..06cc613c17 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneAccountCreationOverlay.cs @@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("show manually", () => accountCreation.Show()); AddUntilStep("overlay is visible", () => accountCreation.State.Value == Visibility.Visible); - AddStep("click button", () => accountCreation.ChildrenOfType().Single().Click()); + AddStep("click button", () => accountCreation.ChildrenOfType().Single().TriggerClick()); AddUntilStep("warning screen is present", () => accountCreation.ChildrenOfType().SingleOrDefault()?.IsPresent == true); AddStep("log back in", () => API.Login("dummy", "password")); diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs index 93a5b6fc59..f94c018b27 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs @@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("Show", () => overlay.Show()); AddUntilStep("Show More button is visible", () => showMoreButton?.Alpha == 1); setUpNewsResponse(responseWithNoCursor, "Set up no cursor response"); - AddStep("Click Show More", () => showMoreButton?.Click()); + AddStep("Click Show More", () => showMoreButton?.TriggerClick()); AddUntilStep("Show More button is hidden", () => showMoreButton?.Alpha == 0); } diff --git a/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs b/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs index 18ac415126..d7fa5a1f6d 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs @@ -32,19 +32,19 @@ namespace osu.Game.Tests.Visual.Online } }); - AddStep("click button", () => button.Click()); + AddStep("click button", () => button.TriggerClick()); AddAssert("action fired once", () => fireCount == 1); AddAssert("is in loading state", () => button.IsLoading); - AddStep("click button", () => button.Click()); + AddStep("click button", () => button.TriggerClick()); AddAssert("action not fired", () => fireCount == 1); AddAssert("is in loading state", () => button.IsLoading); AddUntilStep("wait for loaded", () => !button.IsLoading); - AddStep("click button", () => button.Click()); + AddStep("click button", () => button.TriggerClick()); AddAssert("action fired twice", () => fireCount == 2); AddAssert("is in loading state", () => button.IsLoading); diff --git a/osu.Game.Tests/Visual/Online/TestSceneVotePill.cs b/osu.Game.Tests/Visual/Online/TestSceneVotePill.cs index e9e826e62f..a9fed7b302 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneVotePill.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneVotePill.cs @@ -45,7 +45,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("Log in", logIn); AddStep("User comment", () => addVotePill(getUserComment())); AddAssert("Background is transparent", () => votePill.Background.Alpha == 0); - AddStep("Click", () => votePill.Click()); + AddStep("Click", () => votePill.TriggerClick()); AddAssert("Not loading", () => !votePill.IsLoading); } @@ -56,7 +56,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("Log in", logIn); AddStep("Random comment", () => addVotePill(getRandomComment())); AddAssert("Background is visible", () => votePill.Background.Alpha == 1); - AddStep("Click", () => votePill.Click()); + AddStep("Click", () => votePill.TriggerClick()); AddAssert("Loading", () => votePill.IsLoading); } @@ -66,7 +66,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("Hide login overlay", () => login.Hide()); AddStep("Log out", API.Logout); AddStep("Random comment", () => addVotePill(getRandomComment())); - AddStep("Click", () => votePill.Click()); + AddStep("Click", () => votePill.TriggerClick()); AddAssert("Login overlay is visible", () => login.State.Value == Visibility.Visible); } diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs index 79ba6d9660..ecdb046203 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs @@ -56,7 +56,7 @@ namespace osu.Game.Tests.Visual.Playlists AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms[0])); - AddStep("select last room", () => roomsContainer.Rooms[^1].Click()); + AddStep("select last room", () => roomsContainer.Rooms[^1].TriggerClick()); AddUntilStep("first room is masked", () => !checkRoomVisible(roomsContainer.Rooms[0])); AddUntilStep("last room is not masked", () => checkRoomVisible(roomsContainer.Rooms[^1])); diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs index f2bfb80beb..9fc29049ef 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs @@ -76,7 +76,7 @@ namespace osu.Game.Tests.Visual.Playlists }); }); - AddStep("start match", () => match.ChildrenOfType().First().Click()); + AddStep("start match", () => match.ChildrenOfType().First().TriggerClick()); AddUntilStep("player loader loaded", () => Stack.CurrentScreen is PlayerLoader); } diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index 54293485cb..fa2c9ecdea 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -160,7 +160,7 @@ namespace osu.Game.Tests.Visual.Settings { var resetButton = settingsKeyBindingRow.ChildrenOfType>().First(); - resetButton.Click(); + resetButton.TriggerClick(); }); AddUntilStep("restore button hidden", () => settingsKeyBindingRow.ChildrenOfType>().First().Alpha == 0); @@ -189,7 +189,7 @@ namespace osu.Game.Tests.Visual.Settings { var resetButton = panel.ChildrenOfType().First(); - resetButton.Click(); + resetButton.TriggerClick(); }); AddUntilStep("restore button hidden", () => settingsKeyBindingRow.ChildrenOfType>().First().Alpha == 0); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 1e76c33fca..32c1d262d5 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -94,10 +94,10 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("selected mod matches", () => (SelectedMods.Value.Single() as OsuModDoubleTime)?.SpeedChange.Value == 1.2); - AddStep("deselect", () => modSelect.DeselectAllButton.Click()); + AddStep("deselect", () => modSelect.DeselectAllButton.TriggerClick()); AddAssert("selected mods empty", () => SelectedMods.Value.Count == 0); - AddStep("reselect", () => modSelect.GetModButton(osuModDoubleTime).Click()); + AddStep("reselect", () => modSelect.GetModButton(osuModDoubleTime).TriggerClick()); AddAssert("selected mod has default value", () => (SelectedMods.Value.Single() as OsuModDoubleTime)?.SpeedChange.IsDefault == true); } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index 65db2e9644..84e2ebb6d8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("wait for button load", () => modSelect.ButtonsLoaded); AddStep("select mod", () => modSelect.SelectMod(testCustomisableMod)); AddAssert("button enabled", () => modSelect.CustomiseButton.Enabled.Value); - AddStep("open Customisation", () => modSelect.CustomiseButton.Click()); + AddStep("open Customisation", () => modSelect.CustomiseButton.TriggerClick()); AddStep("deselect mod", () => modSelect.SelectMod(testCustomisableMod)); AddAssert("controls hidden", () => modSelect.ModSettingsContainer.State.Value == Visibility.Hidden); } diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs index 522567584d..2e34c39370 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs @@ -40,6 +40,6 @@ namespace osu.Game.Tournament.Tests.Screens () => this.ChildrenOfType().All(score => score.Alpha == (visible ? 1 : 0))); private void toggleWarmup() - => AddStep("toggle warmup", () => this.ChildrenOfType().First().Click()); + => AddStep("toggle warmup", () => this.ChildrenOfType().First().TriggerClick()); } } diff --git a/osu.Game/Graphics/UserInterface/BackButton.cs b/osu.Game/Graphics/UserInterface/BackButton.cs index b8196c6360..1607762908 100644 --- a/osu.Game/Graphics/UserInterface/BackButton.cs +++ b/osu.Game/Graphics/UserInterface/BackButton.cs @@ -35,7 +35,7 @@ namespace osu.Game.Graphics.UserInterface Add(receptor = new Receptor()); } - receptor.OnBackPressed = () => button.Click(); + receptor.OnBackPressed = () => button.TriggerClick(); } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs index a5e5f664c9..5b3c142a66 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs @@ -63,7 +63,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons }, }; - Action = () => playButton.Click(); + Action = () => playButton.TriggerClick(); Playing.ValueChanged += playing => progress.FadeTo(playing.NewValue ? 1 : 0, 100); } diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs index 58b402c164..9d2cd8a21d 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs @@ -153,7 +153,7 @@ namespace osu.Game.Overlays.Chat.Tabs switch (e.Button) { case MouseButton.Middle: - CloseButton.Click(); + CloseButton.TriggerClick(); break; } } diff --git a/osu.Game/Overlays/Comments/CommentEditor.cs b/osu.Game/Overlays/Comments/CommentEditor.cs index 7b4bf882dc..20a8ab64f7 100644 --- a/osu.Game/Overlays/Comments/CommentEditor.cs +++ b/osu.Game/Overlays/Comments/CommentEditor.cs @@ -120,7 +120,7 @@ namespace osu.Game.Overlays.Comments if (commitButton.IsBlocked.Value) return; - commitButton.Click(); + commitButton.TriggerClick(); }; } diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index cd02900e88..78ef2ec795 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -218,7 +218,7 @@ namespace osu.Game.Overlays.Dialog /// /// Programmatically clicks the first . /// - public void PerformOkAction() => Buttons.OfType().First().Click(); + public void PerformOkAction() => Buttons.OfType().First().TriggerClick(); protected override bool OnKeyDown(KeyDownEvent e) { @@ -265,7 +265,7 @@ namespace osu.Game.Overlays.Dialog if (!actionInvoked && content.IsPresent) // In the case a user did not choose an action before a hide was triggered, press the last button. // This is presumed to always be a sane default "cancel" action. - buttonsContainer.Last().Click(); + buttonsContainer.Last().TriggerClick(); content.FadeOut(EXIT_DURATION, Easing.InSine); } @@ -273,7 +273,7 @@ namespace osu.Game.Overlays.Dialog private void pressButtonAtIndex(int index) { if (index < Buttons.Count()) - Buttons.Skip(index).First().Click(); + Buttons.Skip(index).First().TriggerClick(); } } } diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index bc3b0e6c9a..43ef42a809 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -88,7 +88,7 @@ namespace osu.Game.Overlays switch (action) { case GlobalAction.Select: - CurrentDialog?.Buttons.OfType().FirstOrDefault()?.Click(); + CurrentDialog?.Buttons.OfType().FirstOrDefault()?.TriggerClick(); return true; } diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 793bb79318..96eba7808f 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -404,11 +404,11 @@ namespace osu.Game.Overlays.Mods switch (e.Key) { case Key.Number1: - DeselectAllButton.Click(); + DeselectAllButton.TriggerClick(); return true; case Key.Number2: - CloseButton.Click(); + CloseButton.TriggerClick(); return true; } diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index 4a33f9e296..6da41b2b5f 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -188,7 +188,7 @@ namespace osu.Game.Overlays.Toolbar { if (action == Hotkey) { - Click(); + TriggerClick(); return true; } diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs index 564fd65719..a70a0d8a71 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs @@ -62,7 +62,7 @@ namespace osu.Game.Overlays.Toolbar protected override bool OnClick(ClickEvent e) { - Parent.Click(); + Parent.TriggerClick(); return base.OnClick(e); } } diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index bdb0157746..6c712e9d5b 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -210,7 +210,7 @@ namespace osu.Game.Screens.Menu { if (buttonsTopLevel.Any(b => e.Key == b.TriggerKey)) { - logo?.Click(); + logo?.TriggerClick(); return true; } } @@ -226,7 +226,7 @@ namespace osu.Game.Screens.Menu return goBack(); case GlobalAction.Select: - logo?.Click(); + logo?.TriggerClick(); return true; default: @@ -248,7 +248,7 @@ namespace osu.Game.Screens.Menu return true; case ButtonSystemState.Play: - backButton.Click(); + backButton.TriggerClick(); return true; default: @@ -268,11 +268,11 @@ namespace osu.Game.Screens.Menu return true; case ButtonSystemState.TopLevel: - buttonsTopLevel.First().Click(); + buttonsTopLevel.First().TriggerClick(); return false; case ButtonSystemState.Play: - buttonsPlay.First().Click(); + buttonsPlay.First().TriggerClick(); return false; } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index adff1cc33e..c455cb0c50 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -262,7 +262,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components switch (action) { case GlobalAction.Select: - Click(); + TriggerClick(); return true; } diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index 2608c93fa1..ce1f223403 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -42,12 +42,12 @@ namespace osu.Game.Screens.Play /// /// Action that is invoked when is triggered. /// - protected virtual Action BackAction => () => InternalButtons.Children.LastOrDefault()?.Click(); + protected virtual Action BackAction => () => InternalButtons.Children.LastOrDefault()?.TriggerClick(); /// /// Action that is invoked when is triggered. /// - protected virtual Action SelectAction => () => InternalButtons.Selected?.Click(); + protected virtual Action SelectAction => () => InternalButtons.Selected?.TriggerClick(); public abstract string Header { get; } diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs index 8778cff535..e0b7e5c941 100644 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -24,7 +24,7 @@ namespace osu.Game.Screens.Play private SkinnableSound pauseLoop; - protected override Action BackAction => () => InternalButtons.Children.First().Click(); + protected override Action BackAction => () => InternalButtons.Children.First().TriggerClick(); [BackgroundDependencyLoader] private void load(OsuColour colours) diff --git a/osu.Game/Screens/Play/SkipOverlay.cs b/osu.Game/Screens/Play/SkipOverlay.cs index ed49fc40b2..4a74fa1d4f 100644 --- a/osu.Game/Screens/Play/SkipOverlay.cs +++ b/osu.Game/Screens/Play/SkipOverlay.cs @@ -148,7 +148,7 @@ namespace osu.Game.Screens.Play if (!button.Enabled.Value) return false; - button.Click(); + button.TriggerClick(); return true; } diff --git a/osu.Game/Screens/Select/FooterButton.cs b/osu.Game/Screens/Select/FooterButton.cs index c3fbd767ff..9c0a68133c 100644 --- a/osu.Game/Screens/Select/FooterButton.cs +++ b/osu.Game/Screens/Select/FooterButton.cs @@ -176,7 +176,7 @@ namespace osu.Game.Screens.Select { if (action == Hotkey) { - Click(); + TriggerClick(); return true; } diff --git a/osu.Game/Screens/Select/FooterButtonRandom.cs b/osu.Game/Screens/Select/FooterButtonRandom.cs index 2d14111137..1eaf2c591e 100644 --- a/osu.Game/Screens/Select/FooterButtonRandom.cs +++ b/osu.Game/Screens/Select/FooterButtonRandom.cs @@ -67,7 +67,7 @@ namespace osu.Game.Screens.Select return false; } - Click(); + TriggerClick(); return true; } diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs index 2676635764..b5fdbd225f 100644 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs @@ -122,7 +122,7 @@ namespace osu.Game.Screens.Select.Options if (found != null) { - found.Click(); + found.TriggerClick(); return true; } } From 3b6771ca65514060935b5b34bab8da8d3b31e25e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 4 Aug 2021 22:09:03 +0900 Subject: [PATCH 0843/2442] Remove todo --- osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 0b19e2df1c..06c95dcb7d 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -359,7 +359,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components roomCategory.BindTo(Room.Category); roomCategory.BindValueChanged(c => { - // Todo: Tournament category... if (c.NewValue == RoomCategory.Spotlight) specialCategoryPill.Show(); else From a01402664f341be9bc04ffbb9811ee6d40cbbdae Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 13 Jul 2021 05:22:11 +0300 Subject: [PATCH 0844/2442] Add redesigned star rating display Matching the same design as the one in the latest figma designs. --- .../Beatmaps/Drawables/StarRatingDisplayV2.cs | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 osu.Game/Beatmaps/Drawables/StarRatingDisplayV2.cs diff --git a/osu.Game/Beatmaps/Drawables/StarRatingDisplayV2.cs b/osu.Game/Beatmaps/Drawables/StarRatingDisplayV2.cs new file mode 100644 index 0000000000..aa411e74bd --- /dev/null +++ b/osu.Game/Beatmaps/Drawables/StarRatingDisplayV2.cs @@ -0,0 +1,99 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Beatmaps.Drawables +{ + public class StarRatingDisplayV2 : CompositeDrawable, IHasCurrentValue + { + private readonly Box background; + private readonly SpriteIcon starIcon; + private readonly OsuSpriteText starsText; + + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + public Bindable Current + { + get => current.Current; + set => current.Current = value; + } + + [Resolved] + private OsuColour colours { get; set; } + + [Resolved(canBeNull: true)] + private OverlayColourProvider colourProvider { get; set; } + + /// + /// Creates a new using an already computed . + /// + /// The already computed to display the star difficulty of. + public StarRatingDisplayV2(StarDifficulty starDifficulty) + { + Size = new Vector2(52f, 20f); + + Current.Value = starDifficulty; + + InternalChild = new CircularContainer + { + Masking = true, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + }, + starIcon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Margin = new MarginPadding { Right = 30f }, + Icon = FontAwesome.Solid.Star, + Size = new Vector2(8f), + }, + starsText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Margin = new MarginPadding { Left = 10f, Bottom = 1f }, + Font = OsuFont.Torus.With(size: 12f, weight: FontWeight.Bold), + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Current.BindValueChanged(c => + { + starsText.Text = c.NewValue.Stars.ToString("0.00"); + + background.Colour = colours.ForStarDifficulty(c.NewValue.Stars); + + starIcon.Colour = c.NewValue.Stars >= 7.5 + ? colourProvider?.Content1 ?? Color4.White + : colourProvider?.Background5 ?? Color4Extensions.FromHex("303d47"); + + starsText.Colour = c.NewValue.Stars >= 7.5 + ? colourProvider?.Content1 ?? Color4.White.Opacity(0.75f) + : colourProvider?.Background5 ?? Color4.Black.Opacity(0.75f); + }, true); + } + } +} From 6c9ed16b0caa5e29f436d58c388ada3ab34e31b1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 13 Jul 2021 05:29:16 +0300 Subject: [PATCH 0845/2442] Add visual test scene --- .../TestSceneStarRatingDisplayV2.cs | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplayV2.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplayV2.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplayV2.cs new file mode 100644 index 0000000000..1b85e25e92 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplayV2.cs @@ -0,0 +1,69 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Utils; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; +using osuTK; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneStarRatingDisplayV2 : OsuTestScene + { + [Test] + public void TestDisplay() + { + AddStep("load displays", () => + { + Child = new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(2f), + Direction = FillDirection.Horizontal, + ChildrenEnumerable = Enumerable.Range(0, 10).Select(i => new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(2f), + Direction = FillDirection.Vertical, + ChildrenEnumerable = Enumerable.Range(0, 10).Select(j => new StarRatingDisplayV2(new StarDifficulty(i + j * 0.1f, 0)) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }) + }) + }; + }); + } + + [Test] + public void TestChangingStarRatingDisplay() + { + StarRatingDisplayV2 starRating = null; + + AddStep("load display", () => Child = starRating = new StarRatingDisplayV2(new StarDifficulty(5.55, 1)) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }); + + AddRepeatStep("set random value", () => + { + starRating.Current.Value = new StarDifficulty(RNG.NextDouble(0.0, 11.0), 1); + }, 10); + + AddSliderStep("set exact stars", 0.0, 11.0, 5.55, d => + { + if (starRating != null) + starRating.Current.Value = new StarDifficulty(d, 1); + }); + } + } +} From 19d54ee7510d0a4d594b0897e22fd39e45c3b9e5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 16 Jul 2021 01:59:32 +0300 Subject: [PATCH 0846/2442] Update light background handling to `Color4.Yellow` instead Confirmed to be the way forward in https://github.com/ppy/osu-web/pull/7855#issuecomment-880959644. --- osu.Game/Beatmaps/Drawables/StarRatingDisplayV2.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/StarRatingDisplayV2.cs b/osu.Game/Beatmaps/Drawables/StarRatingDisplayV2.cs index aa411e74bd..6154c8e811 100644 --- a/osu.Game/Beatmaps/Drawables/StarRatingDisplayV2.cs +++ b/osu.Game/Beatmaps/Drawables/StarRatingDisplayV2.cs @@ -86,13 +86,8 @@ namespace osu.Game.Beatmaps.Drawables background.Colour = colours.ForStarDifficulty(c.NewValue.Stars); - starIcon.Colour = c.NewValue.Stars >= 7.5 - ? colourProvider?.Content1 ?? Color4.White - : colourProvider?.Background5 ?? Color4Extensions.FromHex("303d47"); - - starsText.Colour = c.NewValue.Stars >= 7.5 - ? colourProvider?.Content1 ?? Color4.White.Opacity(0.75f) - : colourProvider?.Background5 ?? Color4.Black.Opacity(0.75f); + starIcon.Colour = c.NewValue.Stars >= 6.5 ? Color4.Yellow : colourProvider?.Background5 ?? Color4Extensions.FromHex("303d47"); + starsText.Colour = c.NewValue.Stars >= 6.5 ? Color4.Yellow : colourProvider?.Background5 ?? Color4.Black.Opacity(0.75f); }, true); } } From 95b134f3d82ff42868c6726dcd0ad22220efe327 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 17 Jul 2021 05:41:34 +0300 Subject: [PATCH 0847/2442] Use `OsuColour.Orange1` instead of pure yellow --- osu.Game/Beatmaps/Drawables/StarRatingDisplayV2.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/StarRatingDisplayV2.cs b/osu.Game/Beatmaps/Drawables/StarRatingDisplayV2.cs index 6154c8e811..5a942cdeee 100644 --- a/osu.Game/Beatmaps/Drawables/StarRatingDisplayV2.cs +++ b/osu.Game/Beatmaps/Drawables/StarRatingDisplayV2.cs @@ -86,8 +86,8 @@ namespace osu.Game.Beatmaps.Drawables background.Colour = colours.ForStarDifficulty(c.NewValue.Stars); - starIcon.Colour = c.NewValue.Stars >= 6.5 ? Color4.Yellow : colourProvider?.Background5 ?? Color4Extensions.FromHex("303d47"); - starsText.Colour = c.NewValue.Stars >= 6.5 ? Color4.Yellow : colourProvider?.Background5 ?? Color4.Black.Opacity(0.75f); + starIcon.Colour = c.NewValue.Stars >= 6.5 ? colours.Orange1 : colourProvider?.Background5 ?? Color4Extensions.FromHex("303d47"); + starsText.Colour = c.NewValue.Stars >= 6.5 ? colours.Orange1 : colourProvider?.Background5 ?? Color4.Black.Opacity(0.75f); }, true); } } From 14da5ab813d1d7fe523827a59de535c2846d9298 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 17 Jul 2021 05:43:04 +0300 Subject: [PATCH 0848/2442] Remove defined size from the star rating display --- osu.Game/Beatmaps/Drawables/StarRatingDisplayV2.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/StarRatingDisplayV2.cs b/osu.Game/Beatmaps/Drawables/StarRatingDisplayV2.cs index 5a942cdeee..e8961ae414 100644 --- a/osu.Game/Beatmaps/Drawables/StarRatingDisplayV2.cs +++ b/osu.Game/Beatmaps/Drawables/StarRatingDisplayV2.cs @@ -43,8 +43,6 @@ namespace osu.Game.Beatmaps.Drawables /// The already computed to display the star difficulty of. public StarRatingDisplayV2(StarDifficulty starDifficulty) { - Size = new Vector2(52f, 20f); - Current.Value = starDifficulty; InternalChild = new CircularContainer From 0a8d37defa3a167ae725f62c9ab39799f03449bd Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 17 Jul 2021 05:48:38 +0300 Subject: [PATCH 0849/2442] Test star rating display with multiple sizes --- .../Visual/UserInterface/TestSceneStarRatingDisplayV2.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplayV2.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplayV2.cs index 1b85e25e92..43178bb280 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplayV2.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplayV2.cs @@ -14,8 +14,10 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneStarRatingDisplayV2 : OsuTestScene { - [Test] - public void TestDisplay() + [TestCase(52f, 20f)] + [TestCase(52f, 16f)] + [TestCase(50f, 14f)] + public void TestDisplay(float width, float height) { AddStep("load displays", () => { @@ -37,6 +39,7 @@ namespace osu.Game.Tests.Visual.UserInterface { Anchor = Anchor.Centre, Origin = Anchor.Centre, + Size = new Vector2(width, height), }) }) }; @@ -52,6 +55,7 @@ namespace osu.Game.Tests.Visual.UserInterface { Anchor = Anchor.Centre, Origin = Anchor.Centre, + Size = new Vector2(52f, 20f), }); AddRepeatStep("set random value", () => From d4399f10f9b65bb737da9785ceb44a1bf10ef6e7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 17 Jul 2021 06:54:11 +0300 Subject: [PATCH 0850/2442] Merge both variants of the star rating display --- .../Ranking/TestSceneStarRatingDisplay.cs | 65 --------- ...layV2.cs => TestSceneStarRatingDisplay.cs} | 36 ++++- ...atingDisplayV2.cs => StarRatingDisplay.cs} | 21 ++- .../Components/StarRatingRangeDisplay.cs | 12 +- .../Screens/Play/BeatmapMetadataDisplay.cs | 2 +- .../Expanded/ExpandedPanelMiddleContent.cs | 1 + .../Ranking/Expanded/StarRatingDisplay.cs | 129 ------------------ osu.Game/Screens/Select/BeatmapInfoWedge.cs | 1 - 8 files changed, 56 insertions(+), 211 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs rename osu.Game.Tests/Visual/UserInterface/{TestSceneStarRatingDisplayV2.cs => TestSceneStarRatingDisplay.cs} (66%) rename osu.Game/Beatmaps/Drawables/{StarRatingDisplayV2.cs => StarRatingDisplay.cs} (75%) delete mode 100644 osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs b/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs deleted file mode 100644 index 566452249f..0000000000 --- a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Linq; -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Utils; -using osu.Game.Beatmaps; -using osu.Game.Screens.Ranking.Expanded; -using osuTK; - -namespace osu.Game.Tests.Visual.Ranking -{ - public class TestSceneStarRatingDisplay : OsuTestScene - { - [Test] - public void TestDisplay() - { - AddStep("load displays", () => Child = new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - ChildrenEnumerable = new[] - { - 1.23, - 2.34, - 3.45, - 4.56, - 5.67, - 6.78, - 10.11, - }.Select(starRating => new StarRatingDisplay(new StarDifficulty(starRating, 0)) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre - }) - }); - } - - [Test] - public void TestChangingStarRatingDisplay() - { - StarRatingDisplay starRating = null; - - AddStep("load display", () => Child = starRating = new StarRatingDisplay(new StarDifficulty(5.55, 1)) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(3f), - }); - - AddRepeatStep("set random value", () => - { - starRating.Current.Value = new StarDifficulty(RNG.NextDouble(0.0, 11.0), 1); - }, 10); - - AddSliderStep("set exact stars", 0.0, 11.0, 5.55, d => - { - if (starRating != null) - starRating.Current.Value = new StarDifficulty(d, 1); - }); - } - } -} diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplayV2.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs similarity index 66% rename from osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplayV2.cs rename to osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs index 43178bb280..4c65500fbe 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplayV2.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs @@ -12,12 +12,36 @@ using osuTK; namespace osu.Game.Tests.Visual.UserInterface { - public class TestSceneStarRatingDisplayV2 : OsuTestScene + public class TestSceneStarRatingDisplay : OsuTestScene { + [Test] + public void TestOldColoursDisplay() + { + AddStep("load displays", () => Child = new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + ChildrenEnumerable = new[] + { + 1.23, + 2.34, + 3.45, + 4.56, + 5.67, + 6.78, + 10.11, + }.Select(starRating => new StarRatingDisplay(new StarDifficulty(starRating, 0)) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }) + }); + } + [TestCase(52f, 20f)] [TestCase(52f, 16f)] [TestCase(50f, 14f)] - public void TestDisplay(float width, float height) + public void TestNewColoursDisplay(float width, float height) { AddStep("load displays", () => { @@ -35,7 +59,7 @@ namespace osu.Game.Tests.Visual.UserInterface AutoSizeAxes = Axes.Both, Spacing = new Vector2(2f), Direction = FillDirection.Vertical, - ChildrenEnumerable = Enumerable.Range(0, 10).Select(j => new StarRatingDisplayV2(new StarDifficulty(i + j * 0.1f, 0)) + ChildrenEnumerable = Enumerable.Range(0, 10).Select(j => new StarRatingDisplay(new StarDifficulty(i + j * 0.1f, 0), true) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -47,11 +71,11 @@ namespace osu.Game.Tests.Visual.UserInterface } [Test] - public void TestChangingStarRatingDisplay() + public void TestChangingStarRatingDisplay([Values(false, true)] bool useNewColours) { - StarRatingDisplayV2 starRating = null; + StarRatingDisplay starRating = null; - AddStep("load display", () => Child = starRating = new StarRatingDisplayV2(new StarDifficulty(5.55, 1)) + AddStep("load display", () => Child = starRating = new StarRatingDisplay(new StarDifficulty(5.55, 1), useNewColours) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Beatmaps/Drawables/StarRatingDisplayV2.cs b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs similarity index 75% rename from osu.Game/Beatmaps/Drawables/StarRatingDisplayV2.cs rename to osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs index e8961ae414..ed751c66de 100644 --- a/osu.Game/Beatmaps/Drawables/StarRatingDisplayV2.cs +++ b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs @@ -17,8 +17,12 @@ using osuTK.Graphics; namespace osu.Game.Beatmaps.Drawables { - public class StarRatingDisplayV2 : CompositeDrawable, IHasCurrentValue + /// + /// A pill that displays the star rating of a beatmap. + /// + public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue { + private readonly bool useNewDifficultyColours; private readonly Box background; private readonly SpriteIcon starIcon; private readonly OsuSpriteText starsText; @@ -38,13 +42,18 @@ namespace osu.Game.Beatmaps.Drawables private OverlayColourProvider colourProvider { get; set; } /// - /// Creates a new using an already computed . + /// Creates a new using an already computed . /// - /// The already computed to display the star difficulty of. - public StarRatingDisplayV2(StarDifficulty starDifficulty) + /// The already computed to display. + /// Use the new spectrum-based difficulty colours for the display, rather than the old. + public StarRatingDisplay(StarDifficulty starDifficulty, bool useNewDifficultyColours = false) { + this.useNewDifficultyColours = useNewDifficultyColours; + Current.Value = starDifficulty; + Size = new Vector2(52f, 20f); + InternalChild = new CircularContainer { Masking = true, @@ -82,7 +91,9 @@ namespace osu.Game.Beatmaps.Drawables { starsText.Text = c.NewValue.Stars.ToString("0.00"); - background.Colour = colours.ForStarDifficulty(c.NewValue.Stars); + background.Colour = useNewDifficultyColours + ? colours.ForStarDifficulty(c.NewValue.Stars) + : colours.ForDifficultyRating(c.NewValue.DifficultyRating); starIcon.Colour = c.NewValue.Stars >= 6.5 ? colours.Orange1 : colourProvider?.Background5 ?? Color4Extensions.FromHex("303d47"); starsText.Colour = c.NewValue.Stars >= 6.5 ? colours.Orange1 : colourProvider?.Background5 ?? Color4.Black.Opacity(0.75f); diff --git a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs index 8c1b10e3bd..6f5e48f09c 100644 --- a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs @@ -8,14 +8,16 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; -using osu.Game.Screens.Ranking.Expanded; using osuTK; namespace osu.Game.Screens.OnlinePlay.Components { public class StarRatingRangeDisplay : OnlinePlayComposite { + private readonly bool useNewDifficultyColours; + [Resolved] private OsuColour colours { get; set; } @@ -24,8 +26,10 @@ namespace osu.Game.Screens.OnlinePlay.Components private StarRatingDisplay maxDisplay; private Drawable maxBackground; - public StarRatingRangeDisplay() + public StarRatingRangeDisplay(bool useNewDifficultyColours = false) { + this.useNewDifficultyColours = useNewDifficultyColours; + AutoSizeAxes = Axes.Both; } @@ -62,8 +66,8 @@ namespace osu.Game.Screens.OnlinePlay.Components AutoSizeAxes = Axes.Both, Children = new Drawable[] { - minDisplay = new StarRatingDisplay(default), - maxDisplay = new StarRatingDisplay(default) + minDisplay = new StarRatingDisplay(default) { Size = new Vector2(52f, 16f) }, + maxDisplay = new StarRatingDisplay(default) { Size = new Vector2(52f, 16f) } } } }; diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index 4265a83ce1..d77673580a 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -10,12 +10,12 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play.HUD; -using osu.Game.Screens.Ranking.Expanded; using osuTK; namespace osu.Game.Screens.Play diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index e10fe5726d..bcb5e7999f 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs deleted file mode 100644 index 2b86100be8..0000000000 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Globalization; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.UserInterface; -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osuTK; -using osuTK.Graphics; - -namespace osu.Game.Screens.Ranking.Expanded -{ - /// - /// A pill that displays the star rating of a . - /// - public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue - { - private Box background; - private FillFlowContainer content; - private OsuTextFlowContainer textFlow; - - [Resolved] - private OsuColour colours { get; set; } - - private readonly BindableWithCurrent current = new BindableWithCurrent(); - - public Bindable Current - { - get => current.Current; - set => current.Current = value; - } - - /// - /// Creates a new using an already computed . - /// - /// The already computed to display the star difficulty of. - public StarRatingDisplay(StarDifficulty starDifficulty) - { - Current.Value = starDifficulty; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours, BeatmapDifficultyCache difficultyCache) - { - AutoSizeAxes = Axes.Both; - - InternalChildren = new Drawable[] - { - new CircularContainer - { - RelativeSizeAxes = Axes.Both, - Masking = true, - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both, - }, - } - }, - content = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 8, Vertical = 4 }, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(2, 0), - Children = new Drawable[] - { - new SpriteIcon - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Size = new Vector2(7), - Icon = FontAwesome.Solid.Star, - }, - textFlow = new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - TextAnchor = Anchor.BottomLeft, - } - } - } - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - Current.BindValueChanged(_ => updateDisplay(), true); - } - - private void updateDisplay() - { - var starRatingParts = Current.Value.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); - string wholePart = starRatingParts[0]; - string fractionPart = starRatingParts[1]; - string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; - - var stars = Current.Value.Stars; - - background.Colour = colours.ForStarDifficulty(stars); - content.Colour = stars >= 6.5 ? colours.Orange1 : Color4.Black; - - textFlow.Clear(); - textFlow.AddText($"{wholePart}", s => - { - s.Font = s.Font.With(size: 14); - s.UseFullGlyphHeight = false; - }); - - textFlow.AddText($"{separator}{fractionPart}", s => - { - s.Font = s.Font.With(size: 7); - s.UseFullGlyphHeight = false; - }); - } - } -} diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 3779523094..67eb8220f7 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -28,7 +28,6 @@ using osu.Game.Extensions; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; -using osu.Game.Screens.Ranking.Expanded; using osu.Game.Graphics.Containers; namespace osu.Game.Screens.Select From 42370e48ec3b06e4861cb56175f521faece1f033 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 18 Jul 2021 08:20:06 +0300 Subject: [PATCH 0851/2442] Disable shadow on star display text --- osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs index ed751c66de..96e830ccc3 100644 --- a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs +++ b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs @@ -78,6 +78,7 @@ namespace osu.Game.Beatmaps.Drawables Origin = Anchor.Centre, Margin = new MarginPadding { Left = 10f, Bottom = 1f }, Font = OsuFont.Torus.With(size: 12f, weight: FontWeight.Bold), + Shadow = false, } } }; From 284fa496463bc3c5d718b141bf1dd81293705845 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 18 Jul 2021 08:20:22 +0300 Subject: [PATCH 0852/2442] Bring margins of components closer to existing designs --- osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs index 96e830ccc3..2b555cc096 100644 --- a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs +++ b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs @@ -68,7 +68,7 @@ namespace osu.Game.Beatmaps.Drawables { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Margin = new MarginPadding { Right = 30f }, + Margin = new MarginPadding { Right = 26.5f }, Icon = FontAwesome.Solid.Star, Size = new Vector2(8f), }, @@ -76,7 +76,7 @@ namespace osu.Game.Beatmaps.Drawables { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Margin = new MarginPadding { Left = 10f, Bottom = 1f }, + Margin = new MarginPadding { Left = 10f, Bottom = 1.5f }, Font = OsuFont.Torus.With(size: 12f, weight: FontWeight.Bold), Shadow = false, } From 7f9af0ae658331cc424b4572791ea5312bc63013 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 18 Jul 2021 08:21:27 +0300 Subject: [PATCH 0853/2442] Scale up "changing display" test cases --- .../Visual/UserInterface/TestSceneStarRatingDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs index 4c65500fbe..88f43cd701 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs @@ -79,7 +79,7 @@ namespace osu.Game.Tests.Visual.UserInterface { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(52f, 20f), + Scale = new Vector2(3f), }); AddRepeatStep("set random value", () => From b2332eb5b38091c6ea175cb27ead4b3d8d04c06f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 4 Aug 2021 17:06:07 +0300 Subject: [PATCH 0854/2442] Use new difficulty colours permanently --- .../TestSceneStarRatingDisplay.cs | 32 +++---------------- .../Beatmaps/Drawables/StarRatingDisplay.cs | 10 ++---- .../Components/StarRatingRangeDisplay.cs | 6 +--- 3 files changed, 7 insertions(+), 41 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs index 88f43cd701..2f3593c062 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs @@ -14,34 +14,10 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneStarRatingDisplay : OsuTestScene { - [Test] - public void TestOldColoursDisplay() - { - AddStep("load displays", () => Child = new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - ChildrenEnumerable = new[] - { - 1.23, - 2.34, - 3.45, - 4.56, - 5.67, - 6.78, - 10.11, - }.Select(starRating => new StarRatingDisplay(new StarDifficulty(starRating, 0)) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre - }) - }); - } - [TestCase(52f, 20f)] [TestCase(52f, 16f)] [TestCase(50f, 14f)] - public void TestNewColoursDisplay(float width, float height) + public void TestDisplay(float width, float height) { AddStep("load displays", () => { @@ -59,7 +35,7 @@ namespace osu.Game.Tests.Visual.UserInterface AutoSizeAxes = Axes.Both, Spacing = new Vector2(2f), Direction = FillDirection.Vertical, - ChildrenEnumerable = Enumerable.Range(0, 10).Select(j => new StarRatingDisplay(new StarDifficulty(i + j * 0.1f, 0), true) + ChildrenEnumerable = Enumerable.Range(0, 10).Select(j => new StarRatingDisplay(new StarDifficulty(i + j * 0.1f, 0)) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -71,11 +47,11 @@ namespace osu.Game.Tests.Visual.UserInterface } [Test] - public void TestChangingStarRatingDisplay([Values(false, true)] bool useNewColours) + public void TestChangingStarRatingDisplay() { StarRatingDisplay starRating = null; - AddStep("load display", () => Child = starRating = new StarRatingDisplay(new StarDifficulty(5.55, 1), useNewColours) + AddStep("load display", () => Child = starRating = new StarRatingDisplay(new StarDifficulty(5.55, 1)) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs index 2b555cc096..0520cea17a 100644 --- a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs +++ b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs @@ -22,7 +22,6 @@ namespace osu.Game.Beatmaps.Drawables /// public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue { - private readonly bool useNewDifficultyColours; private readonly Box background; private readonly SpriteIcon starIcon; private readonly OsuSpriteText starsText; @@ -45,11 +44,8 @@ namespace osu.Game.Beatmaps.Drawables /// Creates a new using an already computed . /// /// The already computed to display. - /// Use the new spectrum-based difficulty colours for the display, rather than the old. - public StarRatingDisplay(StarDifficulty starDifficulty, bool useNewDifficultyColours = false) + public StarRatingDisplay(StarDifficulty starDifficulty) { - this.useNewDifficultyColours = useNewDifficultyColours; - Current.Value = starDifficulty; Size = new Vector2(52f, 20f); @@ -92,9 +88,7 @@ namespace osu.Game.Beatmaps.Drawables { starsText.Text = c.NewValue.Stars.ToString("0.00"); - background.Colour = useNewDifficultyColours - ? colours.ForStarDifficulty(c.NewValue.Stars) - : colours.ForDifficultyRating(c.NewValue.DifficultyRating); + background.Colour = colours.ForStarDifficulty(c.NewValue.Stars); starIcon.Colour = c.NewValue.Stars >= 6.5 ? colours.Orange1 : colourProvider?.Background5 ?? Color4Extensions.FromHex("303d47"); starsText.Colour = c.NewValue.Stars >= 6.5 ? colours.Orange1 : colourProvider?.Background5 ?? Color4.Black.Opacity(0.75f); diff --git a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs index 6f5e48f09c..867fa88352 100644 --- a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs @@ -16,8 +16,6 @@ namespace osu.Game.Screens.OnlinePlay.Components { public class StarRatingRangeDisplay : OnlinePlayComposite { - private readonly bool useNewDifficultyColours; - [Resolved] private OsuColour colours { get; set; } @@ -26,10 +24,8 @@ namespace osu.Game.Screens.OnlinePlay.Components private StarRatingDisplay maxDisplay; private Drawable maxBackground; - public StarRatingRangeDisplay(bool useNewDifficultyColours = false) + public StarRatingRangeDisplay() { - this.useNewDifficultyColours = useNewDifficultyColours; - AutoSizeAxes = Axes.Both; } From b63d47259480ddc46579b76cb8505102f249000c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 4 Aug 2021 18:17:02 +0300 Subject: [PATCH 0855/2442] Adjust font size to match designs Looks silly when using `12f`, I've added a todo comment so that this specific case does not get forgotten when CSS-compatible font sizing gets supported. The todo comment may not be 100% required but very unsure about silently changing it and forgetting about it. --- osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs index 0520cea17a..2c40aebbe1 100644 --- a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs +++ b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs @@ -64,7 +64,7 @@ namespace osu.Game.Beatmaps.Drawables { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Margin = new MarginPadding { Right = 26.5f }, + Margin = new MarginPadding { Right = 30f }, Icon = FontAwesome.Solid.Star, Size = new Vector2(8f), }, @@ -72,8 +72,10 @@ namespace osu.Game.Beatmaps.Drawables { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Margin = new MarginPadding { Left = 10f, Bottom = 1.5f }, - Font = OsuFont.Torus.With(size: 12f, weight: FontWeight.Bold), + Margin = new MarginPadding { Left = 10f, Bottom = 2f }, + // todo: this should be size: 12f, but to match up with the design, it needs to be 14.4f + // see https://github.com/ppy/osu-framework/issues/3271. + Font = OsuFont.Torus.With(size: 14.4f, weight: FontWeight.Bold), Shadow = false, } } From c84bd2c74dd75193d7cc8c2b80af7ee29a8787ff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Aug 2021 13:22:59 +0900 Subject: [PATCH 0856/2442] Update new obsolete usages --- .../Screens/OnlinePlay/Match/Components/CreateRoomButton.cs | 2 +- .../OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs | 2 +- .../Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/CreateRoomButton.cs b/osu.Game/Screens/OnlinePlay/Match/Components/CreateRoomButton.cs index 711eaa0ca1..cd4dee5e3a 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/CreateRoomButton.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/CreateRoomButton.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components case PlatformAction.DocumentNew: // might as well also handle new tab. it's a bit of an undefined flow on this screen. case PlatformAction.TabNew: - Click(); + TriggerClick(); return true; } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs index 6293751ac0..d1e2f20755 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs @@ -87,7 +87,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match switch (action) { case GlobalAction.Select: - selectButton.Click(); + selectButton.TriggerClick(); return true; } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index c3257f8d94..b03a2e0677 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -382,7 +382,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match switch (action) { case GlobalAction.Select: - Click(); + TriggerClick(); return true; } From 2ccf7e75b08a18d7cbacd6cd165b8edb3685bb85 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Aug 2021 13:24:59 +0900 Subject: [PATCH 0857/2442] Fix new possible nullref inspection due to delegate initialisation in constructor --- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index ce1f223403..0efa66bac0 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -61,8 +61,6 @@ namespace osu.Game.Screens.Play protected GameplayMenuOverlay() { RelativeSizeAxes = Axes.Both; - - State.ValueChanged += s => InternalButtons.Deselect(); } [BackgroundDependencyLoader] @@ -142,6 +140,8 @@ namespace osu.Game.Screens.Play }, }; + State.ValueChanged += s => InternalButtons.Deselect(); + updateRetryCount(); } From fd54487186b6ad4cb1ee7c3a30d051becefb4680 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Aug 2021 14:00:16 +0900 Subject: [PATCH 0858/2442] Add safety against pushing to non-current screen --- .../OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs | 6 +++++- .../Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs index d1e2f20755..fb17291bec 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs @@ -49,7 +49,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match RelativeSizeAxes = Axes.X, Height = 40, Text = "Select beatmap", - Action = () => matchSubScreen.Push(new MultiplayerMatchSongSelect()), + Action = () => + { + if (matchSubScreen.IsCurrentScreen()) + matchSubScreen.Push(new MultiplayerMatchSongSelect()); + }, Alpha = 0 } } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 092394446b..45aca24ab2 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -230,7 +230,11 @@ namespace osu.Game.Screens.OnlinePlay.Playlists settingsOverlay = new PlaylistsMatchSettingsOverlay { RelativeSizeAxes = Axes.Both, - EditPlaylist = () => this.Push(new PlaylistsSongSelect()), + EditPlaylist = () => + { + if (this.IsCurrentScreen()) + this.Push(new PlaylistsSongSelect()); + }, State = { Value = roomId.Value == null ? Visibility.Visible : Visibility.Hidden } } }); From 22bd6c7556dbd872a87348d48470768fbad87c55 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Aug 2021 14:00:35 +0900 Subject: [PATCH 0859/2442] Move keyboard progress flow handling to `MatchSettingsOverlay` --- .../Match/Components/MatchSettingsOverlay.cs | 32 +++++++++++++++++- .../Match/BeatmapSelectionControl.cs | 26 ++------------- .../Match/MultiplayerMatchSettingsOverlay.cs | 33 ++++++------------- .../PlaylistsMatchSettingsOverlay.cs | 8 ++++- 4 files changed, 51 insertions(+), 48 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs index 61bb39d0c5..5b89685c4c 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs @@ -4,15 +4,17 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Bindings; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Input.Bindings; using osuTK; using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay.Match.Components { - public abstract class MatchSettingsOverlay : FocusedOverlayContainer + public abstract class MatchSettingsOverlay : FocusedOverlayContainer, IKeyBindingHandler { protected const float TRANSITION_DURATION = 350; protected const float FIELD_PADDING = 45; @@ -21,6 +23,8 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components protected override bool BlockScrollInput => false; + protected abstract OsuButton SubmitButton { get; } + [BackgroundDependencyLoader] private void load() { @@ -29,6 +33,8 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components Add(Settings = CreateSettings()); } + protected abstract void SelectBeatmap(); + protected abstract OnlinePlayComposite CreateSettings(); protected override void PopIn() @@ -41,6 +47,30 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components Settings.MoveToY(-1, TRANSITION_DURATION, Easing.InSine); } + public bool OnPressed(GlobalAction action) + { + switch (action) + { + case GlobalAction.Select: + if (SubmitButton.Enabled.Value) + { + SubmitButton.TriggerClick(); + return true; + } + else + { + SelectBeatmap(); + return true; + } + } + + return false; + } + + public void OnReleased(GlobalAction action) + { + } + protected class SettingsTextBox : OsuTextBox { [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs index fb17291bec..56b87302c2 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs @@ -5,15 +5,13 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.Bindings; using osu.Framework.Screens; -using osu.Game.Input.Bindings; using osu.Game.Online.API; using osu.Game.Screens.OnlinePlay.Match.Components; namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { - public class BeatmapSelectionControl : RoomSubScreenComposite, IKeyBindingHandler + public class BeatmapSelectionControl : RoomSubScreenComposite { [Resolved] private MultiplayerMatchSubScreen matchSubScreen { get; set; } @@ -74,6 +72,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match }, true); } + public void BeginSelection() => selectButton.TriggerClick(); + private void updateBeatmap() { if (SelectedItem.Value == null) @@ -81,25 +81,5 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match else beatmapPanelContainer.Child = new DrawableRoomPlaylistItem(SelectedItem.Value, false, false); } - - public bool OnPressed(GlobalAction action) - { - // only handle keyboard input if there is no current selection. - if (SelectedItem.Value != null) - return false; - - switch (action) - { - case GlobalAction.Select: - selectButton.TriggerClick(); - return true; - } - - return false; - } - - public void OnReleased(GlobalAction action) - { - } } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index b03a2e0677..1ddac94b33 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -12,13 +12,11 @@ using osu.Framework.Extensions.ExceptionExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Bindings; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using osu.Game.Input.Bindings; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Overlays; @@ -30,8 +28,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { public class MultiplayerMatchSettingsOverlay : MatchSettingsOverlay { + private MatchSettings settings; + + protected override OsuButton SubmitButton => settings.ApplyButton; + + protected override void SelectBeatmap() => settings.SelectBeatmap(); + protected override OnlinePlayComposite CreateSettings() - => new MatchSettings + => settings = new MatchSettings { RelativeSizeAxes = Axes.Both, RelativePositionAxes = Axes.Y, @@ -56,6 +60,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private LoadingLayer loadingLayer; private BeatmapSelectionControl initialBeatmapControl; + public void SelectBeatmap() => initialBeatmapControl.BeginSelection(); + [Resolved] private IRoomManager manager { get; set; } @@ -355,7 +361,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match } } - public class CreateOrUpdateButton : TriangleButton, IKeyBindingHandler + public class CreateOrUpdateButton : TriangleButton { [Resolved(typeof(Room), nameof(Room.RoomID))] private Bindable roomId { get; set; } @@ -373,25 +379,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match Triangles.ColourLight = colours.YellowLight; Triangles.ColourDark = colours.YellowDark; } - - public bool OnPressed(GlobalAction action) - { - if (!Enabled.Value) - return false; - - switch (action) - { - case GlobalAction.Select: - TriggerClick(); - return true; - } - - return false; - } - - public void OnReleased(GlobalAction action) - { - } } } } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs index 88ac5ef6e5..3b680a378c 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs @@ -26,8 +26,14 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { public Action EditPlaylist; + private MatchSettings settings; + + protected override OsuButton SubmitButton => settings.ApplyButton; + + protected override void SelectBeatmap() => EditPlaylist(); + protected override OnlinePlayComposite CreateSettings() - => new MatchSettings + => settings = new MatchSettings { RelativeSizeAxes = Axes.Both, RelativePositionAxes = Axes.Y, From bf720f7e06edcbd2ee49350a9cb701f3b42f8b0a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Aug 2021 14:14:07 +0900 Subject: [PATCH 0860/2442] Ensure operations are not performed during loading --- .../OnlinePlay/Match/Components/MatchSettingsOverlay.cs | 5 +++++ .../Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs | 5 +++++ .../OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs index 5b89685c4c..2676453a7e 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs @@ -25,6 +25,8 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components protected abstract OsuButton SubmitButton { get; } + protected abstract bool IsLoading { get; } + [BackgroundDependencyLoader] private void load() { @@ -52,6 +54,9 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components switch (action) { case GlobalAction.Select: + if (IsLoading) + return true; + if (SubmitButton.Enabled.Value) { SubmitButton.TriggerClick(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 1ddac94b33..9d4d6ae079 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -32,6 +32,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match protected override OsuButton SubmitButton => settings.ApplyButton; + [Resolved] + private OngoingOperationTracker ongoingOperationTracker { get; set; } + + protected override bool IsLoading => ongoingOperationTracker.InProgress.Value; + protected override void SelectBeatmap() => settings.SelectBeatmap(); protected override OnlinePlayComposite CreateSettings() diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs index 3b680a378c..9847903c48 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs @@ -30,6 +30,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists protected override OsuButton SubmitButton => settings.ApplyButton; + protected override bool IsLoading => settings.IsLoading; // should probably be replaced with an OngoingOperationTracker. + protected override void SelectBeatmap() => EditPlaylist(); protected override OnlinePlayComposite CreateSettings() @@ -51,6 +53,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists public RoomAvailabilityPicker AvailabilityPicker; public TriangleButton ApplyButton; + public bool IsLoading => loadingLayer.State.Value == Visibility.Visible; + public OsuSpriteText ErrorText; private LoadingLayer loadingLayer; From 2b973b9831daca95b12de46381fb4bb1b451f62e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Aug 2021 14:21:51 +0900 Subject: [PATCH 0861/2442] Redirect beatmap selection to intentionally click the button directly --- .../OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs index 9847903c48..2640f99ea5 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs @@ -32,7 +32,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists protected override bool IsLoading => settings.IsLoading; // should probably be replaced with an OngoingOperationTracker. - protected override void SelectBeatmap() => EditPlaylist(); + protected override void SelectBeatmap() => settings.SelectBeatmap(); protected override OnlinePlayComposite CreateSettings() => settings = new MatchSettings @@ -61,6 +61,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private DrawableRoomPlaylist playlist; private OsuSpriteText playlistLength; + private PurpleTriangleButton editPlaylistButton; + [Resolved(CanBeNull = true)] private IRoomManager manager { get; set; } @@ -209,7 +211,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists }, new Drawable[] { - new PurpleTriangleButton + editPlaylistButton = new PurpleTriangleButton { RelativeSizeAxes = Axes.X, Height = 40, @@ -302,6 +304,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists ApplyButton.Enabled.Value = hasValidSettings; } + public void SelectBeatmap() => editPlaylistButton.TriggerClick(); + private void onPlaylistChanged(object sender, NotifyCollectionChangedEventArgs e) => playlistLength.Text = $"Length: {Playlist.GetTotalDuration()}"; From 94aa5fbca7ad9b4fd924dc35528e67dca6417f4f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Aug 2021 16:31:34 +0900 Subject: [PATCH 0862/2442] Fix doubled json property (runtime error) --- osu.Game/Online/Rooms/Room.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index de90907552..4bd5b1a788 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -61,7 +61,7 @@ namespace osu.Game.Online.Rooms public readonly Bindable Availability = new Bindable(); [Cached] - [JsonProperty("type")] + [JsonIgnore] public readonly Bindable Type = new Bindable(); // Todo: osu-framework bug (https://github.com/ppy/osu-framework/issues/4106) From 27ff428491cc31ea8af72b9e31f90c27251c4b06 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Aug 2021 17:33:19 +0900 Subject: [PATCH 0863/2442] Revert "Temporary changes to compile with latest framework" This reverts commit 34c671f712d71c8a3e3821d7dc45e46f055e8f1b. --- osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs | 4 +--- osu.Game/Overlays/Rankings/Tables/RankingsTable.cs | 3 +-- osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs | 5 ++--- osu.Game/Screens/Edit/EditorTable.cs | 4 +--- 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index 4b44c34c6d..ddd1dfa6cd 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -8,8 +8,6 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.Extensions.EnumExtensions; -using osu.Framework.Extensions.LocalisationExtensions; -using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -217,7 +215,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private class HeaderText : OsuSpriteText { - public HeaderText(LocalisableString text) + public HeaderText(string text) { Text = text.ToUpper(); Font = OsuFont.GetFont(size: 10, weight: FontWeight.Bold); diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 3f8779b97b..585b5c22aa 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -10,7 +10,6 @@ using osu.Framework.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Localisation; using osu.Game.Users; using osu.Game.Users.Drawables; using osuTK; @@ -110,7 +109,7 @@ namespace osu.Game.Overlays.Rankings.Tables { private readonly bool isHighlighted; - public HeaderText(LocalisableString text, bool isHighlighted) + public HeaderText(string text, bool isHighlighted) { this.isHighlighted = isHighlighted; diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs index a77ce81c23..a6969f483f 100644 --- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Users; @@ -20,7 +19,7 @@ namespace osu.Game.Overlays.Rankings.Tables { } - protected virtual IEnumerable GradeColumns => new List { "SS", "S", "A" }; + protected virtual IEnumerable GradeColumns => new List { "SS", "S", "A" }; protected override TableColumn[] CreateAdditionalHeaders() => new[] { @@ -67,7 +66,7 @@ namespace osu.Game.Overlays.Rankings.Tables private class UserTableHeaderText : HeaderText { - public UserTableHeaderText(LocalisableString text, bool isHighlighted, bool isGrade) + public UserTableHeaderText(string text, bool isHighlighted, bool isGrade) : base(text, isHighlighted) { Margin = new MarginPadding diff --git a/osu.Game/Screens/Edit/EditorTable.cs b/osu.Game/Screens/Edit/EditorTable.cs index 77c3a2f26c..9578b96897 100644 --- a/osu.Game/Screens/Edit/EditorTable.cs +++ b/osu.Game/Screens/Edit/EditorTable.cs @@ -2,12 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; -using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -48,7 +46,7 @@ namespace osu.Game.Screens.Edit private class HeaderText : OsuSpriteText { - public HeaderText(LocalisableString text) + public HeaderText(string text) { Text = text.ToUpper(); Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold); From e6cd05ea938417c49bbffaf7e5d065c68affe3e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Aug 2021 19:06:05 +0900 Subject: [PATCH 0864/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 591db6e2c2..f8cd4e41d4 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 0c26bce8c9..ec59b28ceb 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index b7f252820c..bcc4b5f254 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From e13a82ed630639fb0f221ae795acf3a2ec0bbaff Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 5 Aug 2021 14:07:35 +0300 Subject: [PATCH 0865/2442] Fix colour picker antialiasing --- osu.Game/Graphics/UserInterfaceV2/OsuHSVColourPicker.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuHSVColourPicker.cs b/osu.Game/Graphics/UserInterfaceV2/OsuHSVColourPicker.cs index 06056f239b..30e38e8938 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuHSVColourPicker.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuHSVColourPicker.cs @@ -89,8 +89,6 @@ namespace osu.Game.Graphics.UserInterfaceV2 { SelectionArea.CornerRadius = corner_radius; SelectionArea.Masking = true; - // purposefully use hard non-AA'd masking to avoid edge artifacts. - SelectionArea.MaskingSmoothness = 0; } protected override Marker CreateMarker() => new OsuMarker(); From c56b6a5379cb97e41da9caae4fa0b6f9b2176ed4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Aug 2021 20:52:40 +0900 Subject: [PATCH 0866/2442] Fix compilation failure --- osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs index e19665497d..a8fda19c60 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs @@ -17,6 +17,7 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Screens; using osu.Game.Screens.OnlinePlay.Components; +using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; using osu.Game.Screens.OnlinePlay.Multiplayer.Participants; @@ -150,10 +151,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void createRoom(Func room) { - AddStep("open room", () => - { - multiplayerScreen.OpenNewRoom(room()); - }); + AddStep("open room", () => multiplayerScreen.ChildrenOfType().Single().Open(room())); AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); AddWaitStep("wait for transition", 2); From 5521f38cfbc240f222a79c949512a8d87cd56ead Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Aug 2021 20:56:09 +0900 Subject: [PATCH 0867/2442] Adjust spacing --- osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 06c95dcb7d..9d1f495ab9 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -235,7 +235,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - Spacing = new Vector2(4), + Spacing = new Vector2(5), Children = new Drawable[] { new RoomStatusPill From bd394d937750f3b82aa12e15ee169e9ef5fa7db0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Aug 2021 20:56:14 +0900 Subject: [PATCH 0868/2442] Fix pluralisation --- .../Multiplayer/TestSceneDrawableRoom.cs | 47 +++++++++++++++++++ .../Lounge/Components/PlaylistCountPill.cs | 4 +- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index faabed0e17..adada5890e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -13,7 +13,9 @@ using osu.Framework.Utils; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Rooms; using osu.Game.Online.Rooms.RoomStatuses; +using osu.Game.Rulesets.Osu; using osu.Game.Screens.OnlinePlay.Lounge.Components; +using osu.Game.Tests.Beatmaps; using osu.Game.Users; using osuTK; @@ -43,12 +45,57 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = { Value = "Room 1" }, Status = { Value = new RoomStatusOpen() }, EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, + Playlist = + { + new PlaylistItem + { + Beatmap = + { + Value = new TestBeatmap(new OsuRuleset().RulesetInfo) + { + BeatmapInfo = + { + StarDifficulty = 2.5 + } + }.BeatmapInfo, + } + } + } }), createDrawableRoom(new Room { Name = { Value = "Room 2" }, Status = { Value = new RoomStatusPlaying() }, EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, + Playlist = + { + new PlaylistItem + { + Beatmap = + { + Value = new TestBeatmap(new OsuRuleset().RulesetInfo) + { + BeatmapInfo = + { + StarDifficulty = 2.5 + } + }.BeatmapInfo, + } + }, + new PlaylistItem + { + Beatmap = + { + Value = new TestBeatmap(new OsuRuleset().RulesetInfo) + { + BeatmapInfo = + { + StarDifficulty = 4.5 + } + }.BeatmapInfo, + } + } + } }), createDrawableRoom(new Room { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs index 8d8309fcaa..2fe3c7b668 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Specialized; +using Humanizer; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics; @@ -46,7 +47,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { count.Clear(); count.AddText(Playlist.Count.ToString(), s => s.Font = s.Font.With(weight: FontWeight.Bold)); - count.AddText(" Maps"); + count.AddText(" "); + count.AddText("Beatmap".ToQuantity(Playlist.Count, ShowQuantityAs.None)); } } } From b8ec1cb9847701aa88de554c35798281b0089b84 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Aug 2021 20:58:51 +0900 Subject: [PATCH 0869/2442] Hide star rating max display for equal difficulties --- .../Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs index b2e35d7020..3b4ac69466 100644 --- a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs @@ -7,6 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Screens.Ranking.Expanded; @@ -85,6 +86,7 @@ namespace osu.Game.Screens.OnlinePlay.Components minDisplay.Current.Value = minDifficulty; maxDisplay.Current.Value = maxDifficulty; + maxDisplay.Alpha = Precision.AlmostEquals(minDifficulty.Stars, maxDifficulty.Stars) ? 0 : 1; minBackground.Colour = colours.ForDifficultyRating(minDifficulty.DifficultyRating, true); maxBackground.Colour = colours.ForDifficultyRating(maxDifficulty.DifficultyRating, true); From 2f2e3d736665c50675aff9ab0cd3b7c6cd6d731b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Aug 2021 21:01:21 +0900 Subject: [PATCH 0870/2442] Use higher res background image --- osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 9d1f495ab9..ae408f9f84 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -161,7 +161,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components RelativeSizeAxes = Axes.Both, Colour = Color4Extensions.FromHex(@"#27302E"), }, - new OnlinePlayBackgroundSprite(BeatmapSetCoverType.List) + new OnlinePlayBackgroundSprite(BeatmapSetCoverType.Cover) { RelativeSizeAxes = Axes.Both }, From 438f0ce7027497816836431b0aaf4d470bd4faef Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Aug 2021 21:02:14 +0900 Subject: [PATCH 0871/2442] Increase default number of avatars --- osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index ae408f9f84..146add34b7 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -103,7 +103,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } - private int numberOfAvatars = 3; + private int numberOfAvatars = 7; public int NumberOfAvatars { From 87fd1eaf067919d49edf3513d8438b9e01743a8e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Aug 2021 21:19:23 +0900 Subject: [PATCH 0872/2442] Explain negative padding --- osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 146add34b7..08137d1615 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -171,6 +171,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { Name = @"Room content", RelativeSizeAxes = Axes.Both, + // This negative padding resolves 1px gaps between this background and the background above. Padding = new MarginPadding { Left = 20, Vertical = -0.5f }, Child = new Container { From c74e620ce9bec562a1aed8af85a1990af291e817 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Aug 2021 21:20:55 +0900 Subject: [PATCH 0873/2442] Add constant for background colour --- .../OnlinePlay/Lounge/Components/DrawableRoom.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 08137d1615..ee0e1b5adc 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -40,6 +40,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private const float transition_duration = 60; private const float height = 100; + private static readonly Color4 background_colour = Color4Extensions.FromHex(@"#27302E"); + public event Action StateChanged; private Drawable selectionBox; @@ -159,7 +161,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"#27302E"), + Colour = background_colour, }, new OnlinePlayBackgroundSprite(BeatmapSetCoverType.Cover) { @@ -189,7 +191,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"#27302E"), + Colour = background_colour, }, new OnlinePlayBackgroundSprite(BeatmapSetCoverType.Cover) { @@ -209,12 +211,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"#27302E"), + Colour = background_colour, }, new Box { RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal(Color4Extensions.FromHex(@"#27302E"), Color4Extensions.FromHex(@"#27302E").Opacity(0.3f)) + Colour = ColourInfo.GradientHorizontal(background_colour, background_colour.Opacity(0.3f)) }, } } From fd6d488657b55d14770aabe8436b456096fd0cf1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Aug 2021 21:40:09 +0900 Subject: [PATCH 0874/2442] Add thousands separator to rank range pill --- .../Multiplayer/TestSceneRankRangePill.cs | 28 +++++++++++++++++++ .../Lounge/Components/RankRangePill.cs | 7 +++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs index 5e28b3f8e5..9e03743e8d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs @@ -63,5 +63,33 @@ namespace osu.Game.Tests.Visual.Multiplayer Client.RemoveUser(API.LocalUser.Value); }); } + + [TestCase(1, 10)] + [TestCase(10, 100)] + [TestCase(100, 1000)] + [TestCase(1000, 10000)] + [TestCase(10000, 100000)] + [TestCase(100000, 1000000)] + [TestCase(1000000, 10000000)] + public void TestRange(int min, int max) + { + AddStep("add users", () => + { + Client.AddUser(new User + { + Id = 2, + Statistics = { GlobalRank = min } + }); + + Client.AddUser(new User + { + Id = 3, + Statistics = { GlobalRank = max } + }); + + // Remove the local user so only the ones above are displayed. + Client.RemoveUser(API.LocalUser.Value); + }); + } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RankRangePill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RankRangePill.cs index 8f685ba033..42fe0bfecd 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RankRangePill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RankRangePill.cs @@ -65,13 +65,16 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components return; } + int minRank = Room.Users.Select(u => u.User?.Statistics.GlobalRank ?? 0).DefaultIfEmpty(0).Min(); + int maxRank = Room.Users.Select(u => u.User?.Statistics.GlobalRank ?? 0).DefaultIfEmpty(0).Max(); + rankFlow.AddText("#"); - rankFlow.AddText(Room.Users.Select(u => u.User?.Statistics.GlobalRank ?? 0).DefaultIfEmpty(0).Min().ToString(), s => s.Font = s.Font.With(weight: FontWeight.Bold)); + rankFlow.AddText(minRank.ToString("#,0"), s => s.Font = s.Font.With(weight: FontWeight.Bold)); rankFlow.AddText(" - "); rankFlow.AddText("#"); - rankFlow.AddText(Room.Users.Select(u => u.User?.Statistics.GlobalRank ?? 0).DefaultIfEmpty(0).Max().ToString(), s => s.Font = s.Font.With(weight: FontWeight.Bold)); + rankFlow.AddText(maxRank.ToString("#,0"), s => s.Font = s.Font.With(weight: FontWeight.Bold)); } } } From 0ea982c036814109f43851716d3fc9124cdfb3ca Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Aug 2021 22:49:41 +0900 Subject: [PATCH 0875/2442] Update recent participants list to use participant_count --- .../TestSceneRecentParticipantsList.cs | 42 ++++++++++--------- .../Components/RecentParticipantsList.cs | 16 +++---- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs index c9959b3467..f05c092dfb 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs @@ -36,13 +36,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("add 50 users", () => { for (int i = 0; i < 50; i++) - { - SelectedRoom.Value.RecentParticipants.Add(new User - { - Id = i, - Username = $"User {i}" - }); - } + addUser(i); }); AddStep("set 3 avatars", () => list.NumberOfAvatars = 3); @@ -60,34 +54,44 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("add 50 users", () => { for (int i = 0; i < 50; i++) - { - SelectedRoom.Value.RecentParticipants.Add(new User - { - Id = i, - Username = $"User {i}" - }); - } + addUser(i); }); - AddStep("remove from start", () => SelectedRoom.Value.RecentParticipants.RemoveAt(0)); + AddStep("remove from start", () => removeUserAt(0)); AddAssert("3 avatars displayed", () => list.ChildrenOfType().Count() == 3); AddAssert("46 hidden users", () => list.ChildrenOfType().Single().Count == 46); - AddStep("remove from end", () => SelectedRoom.Value.RecentParticipants.RemoveAt(SelectedRoom.Value.RecentParticipants.Count - 1)); + AddStep("remove from end", () => removeUserAt(SelectedRoom.Value.RecentParticipants.Count - 1)); AddAssert("3 avatars displayed", () => list.ChildrenOfType().Count() == 3); AddAssert("45 hidden users", () => list.ChildrenOfType().Single().Count == 45); - AddRepeatStep("remove 45 users", () => SelectedRoom.Value.RecentParticipants.RemoveAt(0), 45); + AddRepeatStep("remove 45 users", () => removeUserAt(0), 45); AddAssert("3 avatars displayed", () => list.ChildrenOfType().Count() == 3); AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0); AddAssert("hidden users bubble hidden", () => list.ChildrenOfType().Single().Alpha < 0.5f); - AddStep("remove another user", () => SelectedRoom.Value.RecentParticipants.RemoveAt(0)); + AddStep("remove another user", () => removeUserAt(0)); AddAssert("2 avatars displayed", () => list.ChildrenOfType().Count() == 2); AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0); - AddRepeatStep("remove the remaining two users", () => SelectedRoom.Value.RecentParticipants.RemoveAt(0), 2); + AddRepeatStep("remove the remaining two users", () => removeUserAt(0), 2); AddAssert("0 avatars displayed", () => !list.ChildrenOfType().Any()); } + + private void addUser(int id) + { + SelectedRoom.Value.ParticipantCount.Value++; + SelectedRoom.Value.RecentParticipants.Add(new User + { + Id = id, + Username = $"User {id}" + }); + } + + private void removeUserAt(int index) + { + SelectedRoom.Value.ParticipantCount.Value--; + SelectedRoom.Value.RecentParticipants.RemoveAt(index); + } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs index bbea1ea1c3..da410716e4 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs @@ -88,6 +88,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components base.LoadComplete(); RecentParticipants.BindCollectionChanged(onParticipantsChanged, true); + ParticipantCount.BindValueChanged(_ => updateHiddenUserCount(), true); } private int numberOfAvatars = 3; @@ -141,8 +142,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { if (avatarFlow.Count < NumberOfAvatars) avatarFlow.Add(new CircularAvatar { User = user }); - else - hiddenUsers.Count++; + + updateHiddenUserCount(); } private void removeUser(User user) @@ -150,21 +151,20 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components if (avatarFlow.RemoveAll(a => a.User == user) > 0) { if (RecentParticipants.Count >= NumberOfAvatars) - { avatarFlow.Add(new CircularAvatar { User = RecentParticipants.First(u => avatarFlow.All(a => a.User != u)) }); - hiddenUsers.Count--; - } } - else - hiddenUsers.Count--; + + updateHiddenUserCount(); } private void clearUsers() { avatarFlow.Clear(); - hiddenUsers.Count = 0; + updateHiddenUserCount(); } + private void updateHiddenUserCount() => hiddenUsers.Count = ParticipantCount.Value - avatarFlow.Count; + private class CircularAvatar : CompositeDrawable { public User User From 74bffeac5e0419bdc2d50164ed9420f0e723f167 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Aug 2021 17:03:44 +0900 Subject: [PATCH 0876/2442] Minor design adustments (paddings/sizing) --- .../Lounge/Components/DrawableRoom.cs | 2 +- .../Lounge/Components/PillContainer.cs | 25 +++++++++++++------ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index ee0e1b5adc..e973dc75a7 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -288,7 +288,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Scale = new Vector2(0.85f) + Scale = new Vector2(0.8f) } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/PillContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/PillContainer.cs index ca153f7e9a..109851a16b 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/PillContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/PillContainer.cs @@ -17,12 +17,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components public readonly Drawable Background; - protected override Container Content { get; } = new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - }; + protected override Container Content => content; + private readonly Container content; public PillContainer() { @@ -59,7 +55,22 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components }, Content = new[] { - new[] { Content } + new[] + { + new Container + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Padding = new MarginPadding { Bottom = 2 }, + Child = content = new Container + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + } + } } } } From 7d670c6d351467843c51f6d996083dfeccebef33 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Aug 2021 18:00:12 +0900 Subject: [PATCH 0877/2442] Fix gap in fill colour --- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 9a3c75dcc6..3210ef0112 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -93,15 +93,15 @@ namespace osu.Game.Beatmaps.Drawables new CircularContainer { RelativeSizeAxes = Axes.Both, - Scale = new Vector2(0.84f), Anchor = Anchor.Centre, Origin = Anchor.Centre, Masking = true, EdgeEffect = new EdgeEffectParameters { - Colour = Color4.Black.Opacity(0.08f), + Colour = Color4.Black.Opacity(0.06f), + Type = EdgeEffectType.Shadow, - Radius = 5, + Radius = 3, }, Child = background = new Box { From bdfdd00afed0be62426123033f828de0be0ed590 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Aug 2021 18:52:01 +0900 Subject: [PATCH 0878/2442] Adjust spacings and sizings of left-side details --- .../Multiplayer/TestSceneDrawableRoom.cs | 2 +- .../Lounge/Components/DrawableRoom.cs | 71 ++++++++++--------- 2 files changed, 40 insertions(+), 33 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index adada5890e..e6a1bbeb92 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { createDrawableRoom(new Room { - Name = { Value = "Room 1" }, + Name = { Value = "Flyte's Trash Playlist" }, Status = { Value = new RoomStatusOpen() }, EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, Playlist = diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index e973dc75a7..179aef6a9b 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -236,39 +236,46 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { new FillFlowContainer { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(5), - Children = new Drawable[] - { - new RoomStatusPill - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft - }, - specialCategoryPill = new RoomSpecialCategoryPill - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft - }, - new EndDateInfo - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft - }, - } - }, - new FillFlowContainer - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, AutoSizeAxes = Axes.Both, Direction = FillDirection.Vertical, Children = new Drawable[] { - new RoomNameText(), - new RoomHostText() - } + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(5), + Children = new Drawable[] + { + new RoomStatusPill + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft + }, + specialCategoryPill = new RoomSpecialCategoryPill + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft + }, + new EndDateInfo + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft + }, + } + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = 3 }, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new RoomNameText(), + new RoomHostText(), + } + } + }, }, new FillFlowContainer { @@ -276,7 +283,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Origin = Anchor.BottomLeft, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - Spacing = new Vector2(4), + Spacing = new Vector2(5), Children = new Drawable[] { new PlaylistCountPill @@ -429,7 +436,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components public RoomNameText() { - Font = OsuFont.GetFont(size: 24); + Font = OsuFont.GetFont(size: 28); } [BackgroundDependencyLoader] @@ -451,7 +458,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components [BackgroundDependencyLoader] private void load() { - InternalChild = hostText = new LinkFlowContainer(s => s.Font = OsuFont.GetFont(size: 14)) + InternalChild = hostText = new LinkFlowContainer(s => s.Font = OsuFont.GetFont(size: 16)) { AutoSizeAxes = Axes.Both }; From 798b16fc245b83ae8f1165ba442a3e948bbac31a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Aug 2021 18:52:46 +0900 Subject: [PATCH 0879/2442] Remove unused params --- osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 179aef6a9b..6ab46d5527 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -163,7 +163,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components RelativeSizeAxes = Axes.Both, Colour = background_colour, }, - new OnlinePlayBackgroundSprite(BeatmapSetCoverType.Cover) + new OnlinePlayBackgroundSprite { RelativeSizeAxes = Axes.Both }, @@ -193,7 +193,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components RelativeSizeAxes = Axes.Both, Colour = background_colour, }, - new OnlinePlayBackgroundSprite(BeatmapSetCoverType.Cover) + new OnlinePlayBackgroundSprite { RelativeSizeAxes = Axes.Both }, From 9019e0947a31c2fd3b0f28b81f68a2520343e2b3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Aug 2021 18:54:02 +0900 Subject: [PATCH 0880/2442] Remove unused using --- osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 6ab46d5527..440c23b8fd 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -19,7 +19,6 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; From 8dc167ac9a5edfff25b818e10e0417e630205be0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Aug 2021 18:56:01 +0900 Subject: [PATCH 0881/2442] Set default `MultiplayerRoomSettings` type to something that isn't playlists --- osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs index 706bc750d3..001cf2aa93 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs @@ -41,7 +41,7 @@ namespace osu.Game.Online.Multiplayer public string Password { get; set; } = string.Empty; [Key(8)] - public MatchType MatchType { get; set; } + public MatchType MatchType { get; set; } = MatchType.HeadToHead; public bool Equals(MultiplayerRoomSettings other) => BeatmapID == other.BeatmapID From b401dc0b2ea6609f7b7220fc480c0ae3578b73a0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Aug 2021 18:58:50 +0900 Subject: [PATCH 0882/2442] Remove playlist button --- osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.cs index c72fa24b67..c6f9b0f207 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.cs @@ -30,8 +30,6 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components AddItem(MatchType.HeadToHead); AddItem(MatchType.TeamVersus); - // TODO: remove after osu-web is updated to set the correct default type. - AddItem(MatchType.Playlists); } private class GameTypePickerItem : DisableableTabItem From 19ed24a06f47a03e2443cea8291616a820068c1a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Aug 2021 18:59:19 +0900 Subject: [PATCH 0883/2442] Remove unnecessary duplicate background --- .../Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 440c23b8fd..53af2fe5c7 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -187,15 +187,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = background_colour, - }, - new OnlinePlayBackgroundSprite - { - RelativeSizeAxes = Axes.Both - }, new GridContainer { RelativeSizeAxes = Axes.Both, From c680012523ff9f4a1e8ab10f788401a9f3b68986 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Aug 2021 19:06:16 +0900 Subject: [PATCH 0884/2442] Buffer the entire star rating range to fix overlapping alpha --- .../Components/StarRatingRangeDisplay.cs | 57 ++++++++++--------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs index 3b4ac69466..efc93ffeb0 100644 --- a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs @@ -33,38 +33,41 @@ namespace osu.Game.Screens.OnlinePlay.Components [BackgroundDependencyLoader] private void load() { - InternalChildren = new Drawable[] + InternalChild = new BufferedContainer { - new Container + AutoSizeAxes = Axes.Both, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Masking = true, - CornerRadius = 1, - Children = new[] + new Container { - minBackground = new Box + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Vertical = 1 }, + Children = new[] { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.5f), - }, - maxBackground = new Box - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.5f), - }, - } - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Children = new Drawable[] + minBackground = new Box + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.5f), + }, + maxBackground = new Box + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.5f), + }, + } + }, + new FillFlowContainer { - minDisplay = new StarRatingDisplay(default), - maxDisplay = new StarRatingDisplay(default) + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + minDisplay = new StarRatingDisplay(default), + maxDisplay = new StarRatingDisplay(default) + } } } }; From 724edcbecd764ff2b8e4473499763f5c82c984f1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Aug 2021 19:22:48 +0900 Subject: [PATCH 0885/2442] Toggle the expanded state of the multiplayer leaderboard with the user's HUD Resolves https://github.com/ppy/osu/discussions/14140. --- Until now, the multiplayer leaderboard would expand during break time. Now, it respects the user's HUD visibility status (which can be toggled using Shift+Tab). --- osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index 043cce4630..b54a4a7726 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -96,7 +96,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { base.LoadComplete(); - ((IBindable)leaderboard.Expanded).BindTo(IsBreakTime); + ((IBindable)leaderboard.Expanded).BindTo(HUDOverlay.ShowHud); } protected override void StartGameplay() From f262f288fceef2e066761367a0f25297ff411da9 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 6 Aug 2021 19:58:46 +0900 Subject: [PATCH 0886/2442] Fix DHO state is overwritten to `Idle` on `LoadComplete` The state may already be changed before `LoadComplete` is called because DHO is already added to the draw hierarchy. --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 25f3b8931a..ff6168ee37 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -190,7 +190,9 @@ namespace osu.Game.Rulesets.Objects.Drawables comboIndexBindable.BindValueChanged(_ => UpdateComboColour()); comboIndexWithOffsetsBindable.BindValueChanged(_ => UpdateComboColour(), true); - updateState(ArmedState.Idle, true); + // If the state is changed, transforms are already initialized. + if (state.Value == ArmedState.Idle) + updateState(ArmedState.Idle, true); } /// From 53b98520340a9abf2d0b6415de8d78eb9c27ff12 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 6 Aug 2021 19:59:02 +0900 Subject: [PATCH 0887/2442] Add test case for DHO state change before load complete --- .../Gameplay/TestSceneDrawableHitObject.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs index 0ce71696bd..58f4c4c8db 100644 --- a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs +++ b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs @@ -2,11 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; +using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; using osu.Game.Tests.Visual; namespace osu.Game.Tests.Gameplay @@ -121,6 +123,18 @@ namespace osu.Game.Tests.Gameplay AddAssert("Drawable lifetime is restored", () => dho.LifetimeStart == 666 && dho.LifetimeEnd == 999); } + [Test] + public void TestStateChangeBeforeLoadComplete() + { + TestDrawableHitObject dho = null; + AddStep("Add DHO and apply result", () => + { + Child = dho = new TestDrawableHitObject(new HitObject { StartTime = Time.Current }); + dho.MissForcefully(); + }); + AddAssert("DHO state is correct", () => dho.State.Value == ArmedState.Miss); + } + private class TestDrawableHitObject : DrawableHitObject { public const double INITIAL_LIFETIME_OFFSET = 100; @@ -141,6 +155,19 @@ namespace osu.Game.Tests.Gameplay if (SetLifetimeStartOnApply) LifetimeStart = LIFETIME_ON_APPLY; } + + public void MissForcefully() => ApplyResult(r => r.Type = HitResult.Miss); + + protected override void UpdateHitStateTransforms(ArmedState state) + { + if (state != ArmedState.Miss) + { + base.UpdateHitStateTransforms(state); + return; + } + + this.FadeOut(1000); + } } private class TestLifetimeEntry : HitObjectLifetimeEntry From f1ea8308284284134a74ad780f238d425c33b68e Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 6 Aug 2021 20:56:49 +0900 Subject: [PATCH 0888/2442] Re-add on-click feedback to MonthSection and OsuDropdown headers --- osu.Game/Graphics/UserInterface/OsuDropdown.cs | 2 +- osu.Game/Overlays/News/Sidebar/MonthSection.cs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index b97f12df02..61dd5fb2d9 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -288,7 +288,7 @@ namespace osu.Game.Graphics.UserInterface }, }; - AddInternal(new HoverSounds()); + AddInternal(new HoverClickSounds()); } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/News/Sidebar/MonthSection.cs b/osu.Game/Overlays/News/Sidebar/MonthSection.cs index cd6ab224a9..948f312f15 100644 --- a/osu.Game/Overlays/News/Sidebar/MonthSection.cs +++ b/osu.Game/Overlays/News/Sidebar/MonthSection.cs @@ -79,8 +79,6 @@ namespace osu.Game.Overlays.News.Sidebar private readonly SpriteIcon icon; - protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds(); - public DropdownHeader(int month, int year) { var date = new DateTime(year, month, 1); From 3f06ecdd4875027370f649b2e6784ef40a3922a2 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 6 Aug 2021 20:58:53 +0900 Subject: [PATCH 0889/2442] Add open/close sounds to menus --- osu.Game/Graphics/UserInterface/OsuMenu.cs | 35 ++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuMenu.cs b/osu.Game/Graphics/UserInterface/OsuMenu.cs index e7bf4f66ee..b98a29aacb 100644 --- a/osu.Game/Graphics/UserInterface/OsuMenu.cs +++ b/osu.Game/Graphics/UserInterface/OsuMenu.cs @@ -1,6 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osuTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -13,6 +16,12 @@ namespace osu.Game.Graphics.UserInterface { public class OsuMenu : Menu { + private Sample sampleOpen; + private Sample sampleClose; + + // todo: this shouldn't be required after https://github.com/ppy/osu-framework/issues/4519 is fixed. + private bool wasOpened; + public OsuMenu(Direction direction, bool topLevelMenu = false) : base(direction, topLevelMenu) { @@ -22,8 +31,30 @@ namespace osu.Game.Graphics.UserInterface ItemsContainer.Padding = new MarginPadding(5); } - protected override void AnimateOpen() => this.FadeIn(300, Easing.OutQuint); - protected override void AnimateClose() => this.FadeOut(300, Easing.OutQuint); + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sampleOpen = audio.Samples.Get(@"UI/dropdown-open"); + sampleClose = audio.Samples.Get(@"UI/dropdown-close"); + } + + protected override void AnimateOpen() + { + if (!wasOpened) + sampleOpen?.Play(); + + this.FadeIn(300, Easing.OutQuint); + wasOpened = true; + } + + protected override void AnimateClose() + { + if (wasOpened) + sampleClose?.Play(); + + this.FadeOut(300, Easing.OutQuint); + wasOpened = false; + } protected override void UpdateSize(Vector2 newSize) { From 5031b19b422dea125aa49e963f3db14d3b75228b Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 6 Aug 2021 23:30:12 +0900 Subject: [PATCH 0890/2442] Add sounds to multiplayer games list --- .../Lounge/Components/DrawableRoom.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index c455cb0c50..7da964d84b 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -5,6 +5,8 @@ using System; using System.Collections.Generic; using osu.Framework; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; @@ -44,6 +46,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components public event Action StateChanged; + protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds(); + private readonly Box selectionBox; [Resolved(canBeNull: true)] @@ -62,6 +66,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private SelectionState state; + private Sample sampleSelect; + private Sample sampleJoin; + public SelectionState State { get => state; @@ -125,7 +132,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, AudioManager audio) { float stripWidth = side_strip_width * (Room.Category.Value == RoomCategory.Spotlight ? 2 : 1); @@ -221,6 +228,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components }, }, }; + + sampleSelect = audio.Samples.Get($@"UI/{HoverSampleSet.Default.GetDescription()}-select"); + sampleJoin = audio.Samples.Get($@"UI/{HoverSampleSet.Submit.GetDescription()}-select"); } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) @@ -273,22 +283,25 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { } - protected override bool ShouldBeConsideredForInput(Drawable child) => state == SelectionState.Selected; + protected override bool ShouldBeConsideredForInput(Drawable child) => state == SelectionState.Selected || child is HoverSounds; protected override bool OnClick(ClickEvent e) { if (Room != selectedRoom.Value) { + sampleSelect?.Play(); selectedRoom.Value = Room; return true; } if (Room.HasPassword.Value) { + sampleJoin?.Play(); this.ShowPopover(); return true; } + sampleJoin?.Play(); lounge?.Join(Room, null); return base.OnClick(e); From 52400961f6ee5974c1bb0205005954a597ed2e99 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 6 Aug 2021 20:59:26 +0900 Subject: [PATCH 0891/2442] Add open/close sounds to context menus --- .../UserInterface/TestSceneContextMenu.cs | 61 ++++++++++++++++--- .../Cursor/OsuContextMenuContainer.cs | 2 +- .../Graphics/UserInterface/OsuContextMenu.cs | 53 ++++++++++++++-- 3 files changed, 100 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneContextMenu.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneContextMenu.cs index 53693d1b70..3b43f8485a 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneContextMenu.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneContextMenu.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.Collections.Generic; +using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -68,13 +70,40 @@ namespace osu.Game.Tests.Visual.UserInterface ); } - private class MyContextMenuContainer : Container, IHasContextMenu + private static MenuItem[] makeMenu() { - public MenuItem[] ContextMenuItems => new MenuItem[] + return new MenuItem[] { new OsuMenuItem(@"Some option"), new OsuMenuItem(@"Highlighted option", MenuItemType.Highlighted), new OsuMenuItem(@"Another option"), + new OsuMenuItem(@"Nested option >") + { + Items = new MenuItem[] + { + new OsuMenuItem(@"Sub-One"), + new OsuMenuItem(@"Sub-Two"), + new OsuMenuItem(@"Sub-Three"), + new OsuMenuItem(@"Sub-Nested option >") + { + Items = new MenuItem[] + { + new OsuMenuItem(@"Double Sub-One"), + new OsuMenuItem(@"Double Sub-Two"), + new OsuMenuItem(@"Double Sub-Three"), + new OsuMenuItem(@"Sub-Sub-Nested option >") + { + Items = new MenuItem[] + { + new OsuMenuItem(@"Too Deep One"), + new OsuMenuItem(@"Too Deep Two"), + new OsuMenuItem(@"Too Deep Three"), + } + } + } + } + } + }, new OsuMenuItem(@"Choose me please"), new OsuMenuItem(@"And me too"), new OsuMenuItem(@"Trying to fill"), @@ -82,17 +111,29 @@ namespace osu.Game.Tests.Visual.UserInterface }; } + private class MyContextMenuContainer : Container, IHasContextMenu + { + public MenuItem[] ContextMenuItems => makeMenu(); + } + private class AnotherContextMenuContainer : Container, IHasContextMenu { - public MenuItem[] ContextMenuItems => new MenuItem[] + public MenuItem[] ContextMenuItems { - new OsuMenuItem(@"Simple option"), - new OsuMenuItem(@"Simple very very long option"), - new OsuMenuItem(@"Change width", MenuItemType.Highlighted, () => this.ResizeWidthTo(Width * 2, 100, Easing.OutQuint)), - new OsuMenuItem(@"Change height", MenuItemType.Highlighted, () => this.ResizeHeightTo(Height * 2, 100, Easing.OutQuint)), - new OsuMenuItem(@"Change width back", MenuItemType.Destructive, () => this.ResizeWidthTo(Width / 2, 100, Easing.OutQuint)), - new OsuMenuItem(@"Change height back", MenuItemType.Destructive, () => this.ResizeHeightTo(Height / 2, 100, Easing.OutQuint)), - }; + get + { + List items = makeMenu().ToList(); + items.AddRange(new MenuItem[] + { + new OsuMenuItem(@"Change width", MenuItemType.Highlighted, () => this.ResizeWidthTo(Width * 2, 100, Easing.OutQuint)), + new OsuMenuItem(@"Change height", MenuItemType.Highlighted, () => this.ResizeHeightTo(Height * 2, 100, Easing.OutQuint)), + new OsuMenuItem(@"Change width back", MenuItemType.Destructive, () => this.ResizeWidthTo(Width / 2, 100, Easing.OutQuint)), + new OsuMenuItem(@"Change height back", MenuItemType.Destructive, () => this.ResizeHeightTo(Height / 2, 100, Easing.OutQuint)), + }); + + return items.ToArray(); + } + } } } } diff --git a/osu.Game/Graphics/Cursor/OsuContextMenuContainer.cs b/osu.Game/Graphics/Cursor/OsuContextMenuContainer.cs index fbb3fa0e6c..e64d2d98f4 100644 --- a/osu.Game/Graphics/Cursor/OsuContextMenuContainer.cs +++ b/osu.Game/Graphics/Cursor/OsuContextMenuContainer.cs @@ -9,6 +9,6 @@ namespace osu.Game.Graphics.Cursor { public class OsuContextMenuContainer : ContextMenuContainer { - protected override Menu CreateMenu() => new OsuContextMenu(); + protected override Menu CreateMenu() => new OsuContextMenu(true); } } diff --git a/osu.Game/Graphics/UserInterface/OsuContextMenu.cs b/osu.Game/Graphics/UserInterface/OsuContextMenu.cs index 8c7b44f952..7cf8b3eca8 100644 --- a/osu.Game/Graphics/UserInterface/OsuContextMenu.cs +++ b/osu.Game/Graphics/UserInterface/OsuContextMenu.cs @@ -3,6 +3,9 @@ using osuTK.Graphics; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Effects; @@ -13,8 +16,16 @@ namespace osu.Game.Graphics.UserInterface public class OsuContextMenu : OsuMenu { private const int fade_duration = 250; + private Sample sampleOpen; + private Sample sampleClose; + private Sample sampleClick; - public OsuContextMenu() + // todo: this shouldn't be required after https://github.com/ppy/osu-framework/issues/4519 is fixed. + private bool wasOpened; + private readonly bool playClickSample; + private readonly Menu parentMenu; + + public OsuContextMenu(bool playClickSample = false, Menu parentMenu = null) : base(Direction.Vertical) { MaskingContainer.CornerRadius = 5; @@ -28,17 +39,49 @@ namespace osu.Game.Graphics.UserInterface ItemsContainer.Padding = new MarginPadding { Vertical = DrawableOsuMenuItem.MARGIN_VERTICAL }; MaxHeight = 250; + + this.playClickSample = playClickSample; + this.parentMenu = parentMenu; } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, AudioManager audio) { BackgroundColour = colours.ContextMenuGray; + sampleClick = audio.Samples.Get($"UI/{HoverSampleSet.Default.GetDescription()}-select"); + sampleOpen = audio.Samples.Get(@"UI/dropdown-open"); + sampleClose = audio.Samples.Get(@"UI/dropdown-close"); } - protected override void AnimateOpen() => this.FadeIn(fade_duration, Easing.OutQuint); - protected override void AnimateClose() => this.FadeOut(fade_duration, Easing.OutQuint); + protected override void AnimateOpen() + { + this.FadeIn(fade_duration, Easing.OutQuint); - protected override Menu CreateSubMenu() => new OsuContextMenu(); + if (playClickSample) + sampleClick?.Play(); + + if (!wasOpened) + sampleOpen?.Play(); + + wasOpened = true; + } + + protected override void AnimateClose() + { + this.FadeOut(fade_duration, Easing.OutQuint); + + if (parentMenu?.State == MenuState.Closed) + { + wasOpened = false; + return; + } + + if (wasOpened) + sampleClose?.Play(); + + wasOpened = false; + } + + protected override Menu CreateSubMenu() => new OsuContextMenu(false, this); } } From 067ff0e0ad4051ceaa7a8726d8f38d3ce884a8d6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 6 Aug 2021 18:38:14 +0300 Subject: [PATCH 0892/2442] Store last opened settings subpanel rather than relying on LINQ --- osu.Game/Overlays/SettingsOverlay.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index 54b780615d..050502b3be 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -9,7 +9,6 @@ using osu.Game.Overlays.Settings.Sections; using osu.Game.Overlays.Settings.Sections.Input; using osuTK.Graphics; using System.Collections.Generic; -using System.Linq; using osu.Framework.Bindables; using osu.Framework.Localisation; using osu.Game.Localisation; @@ -38,6 +37,8 @@ namespace osu.Game.Overlays private readonly List subPanels = new List(); + private SettingsSubPanel lastOpenedSubPanel; + protected override Drawable CreateHeader() => new SettingsHeader(Title, Description); protected override Drawable CreateFooter() => new SettingsFooter(); @@ -46,21 +47,21 @@ namespace osu.Game.Overlays { } - public override bool AcceptsFocus => subPanels.All(s => s.State.Value != Visibility.Visible); + public override bool AcceptsFocus => lastOpenedSubPanel == null || lastOpenedSubPanel.State.Value == Visibility.Hidden; private T createSubPanel(T subPanel) where T : SettingsSubPanel { subPanel.Depth = 1; subPanel.Anchor = Anchor.TopRight; - subPanel.State.ValueChanged += subPanelStateChanged; + subPanel.State.ValueChanged += e => subPanelStateChanged(subPanel, e); subPanels.Add(subPanel); return subPanel; } - private void subPanelStateChanged(ValueChangedEvent state) + private void subPanelStateChanged(SettingsSubPanel panel, ValueChangedEvent state) { switch (state.NewValue) { @@ -69,6 +70,8 @@ namespace osu.Game.Overlays SectionsContainer.FadeOut(300, Easing.OutQuint); ContentContainer.MoveToX(-WIDTH, 500, Easing.OutQuint); + + lastOpenedSubPanel = panel; break; case Visibility.Hidden: @@ -80,7 +83,7 @@ namespace osu.Game.Overlays } } - protected override float ExpandedPosition => subPanels.Any(s => s.State.Value == Visibility.Visible) ? -WIDTH : base.ExpandedPosition; + protected override float ExpandedPosition => lastOpenedSubPanel?.State.Value == Visibility.Visible ? -WIDTH : base.ExpandedPosition; [BackgroundDependencyLoader] private void load() From 8e8e0fb8d8c9abce26fe64a0098507c8967d0739 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 6 Aug 2021 18:36:43 +0300 Subject: [PATCH 0893/2442] Add placement-dependent horizontal screen offset properties --- osu.Game/Overlays/NotificationOverlay.cs | 5 +++++ osu.Game/Overlays/SettingsOverlay.cs | 2 ++ osu.Game/Overlays/SettingsPanel.cs | 6 ++++++ 3 files changed, 13 insertions(+) diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index b26e17b34c..af4f41901f 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -30,6 +30,11 @@ namespace osu.Game.Overlays private FlowContainer sections; + /// + /// A horizontal offset to apply to the game-wide screen. + /// + public float HorizontalScreenOffset => -width + X; + /// /// Provide a source for the toolbar height. /// diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index 050502b3be..9ed1d950e3 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -21,6 +21,8 @@ namespace osu.Game.Overlays public LocalisableString Title => SettingsStrings.HeaderTitle; public LocalisableString Description => SettingsStrings.HeaderDescription; + public override float HorizontalScreenOffset => base.HorizontalScreenOffset + (lastOpenedSubPanel?.HorizontalScreenOffset ?? 0f); + protected override IEnumerable CreateSections() => new SettingsSection[] { new GeneralSection(), diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index eae828c142..2916ea013f 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -34,6 +34,11 @@ namespace osu.Game.Overlays protected override Container Content => ContentContainer; + /// + /// A horizontal offset to apply to the game-wide screen. + /// + public virtual float HorizontalScreenOffset => (WIDTH + Content?.X) ?? 0f; + protected Sidebar Sidebar; private SidebarButton selectedSidebarButton; @@ -64,6 +69,7 @@ namespace osu.Game.Overlays { InternalChild = ContentContainer = new NonMaskedContent { + X = -WIDTH + ExpandedPosition, Width = WIDTH, RelativeSizeAxes = Axes.Y, Children = new Drawable[] From f77037ef57d6df00845867ae436ceb77e3f525dd Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 6 Aug 2021 18:38:48 +0300 Subject: [PATCH 0894/2442] Replace state-based screen offsetting logic with `HorizontalScreenOffset`s --- osu.Game/OsuGame.cs | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 3cfa2cc755..85428a12e3 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -828,21 +828,6 @@ namespace osu.Game { if (mode.NewValue != OverlayActivation.All) CloseAllOverlays(); }; - - void updateScreenOffset() - { - float offset = 0; - - if (Settings.State.Value == Visibility.Visible) - offset += Toolbar.HEIGHT / 2; - if (notifications.State.Value == Visibility.Visible) - offset -= Toolbar.HEIGHT / 2; - - screenOffsetContainer.MoveToX(offset, SettingsPanel.TRANSITION_LENGTH, Easing.OutQuint); - } - - Settings.State.ValueChanged += _ => updateScreenOffset(); - notifications.State.ValueChanged += _ => updateScreenOffset(); } private void showOverlayAboveOthers(OverlayContainer overlay, OverlayContainer[] otherOverlays) @@ -1026,6 +1011,9 @@ namespace osu.Game screenOffsetContainer.Padding = new MarginPadding { Top = ToolbarOffset }; overlayContent.Padding = new MarginPadding { Top = ToolbarOffset }; + screenOffsetContainer.X = Settings.HorizontalScreenOffset * 0.125f + + notifications.HorizontalScreenOffset * 0.125f; + MenuCursorContainer.CanShowCursor = (ScreenStack.CurrentScreen as IOsuScreen)?.CursorVisible ?? false; } From 2f187cb90f0ecf3eb1cff9edcd95ea9703cfa856 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 7 Aug 2021 01:17:58 +0900 Subject: [PATCH 0895/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index f8cd4e41d4..03e01a24ca 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ec59b28ceb..227493c74a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index bcc4b5f254..ad5b26e968 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From ac157f6cef91f35252599059451fa315819180cf Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 6 Aug 2021 22:10:03 +0300 Subject: [PATCH 0896/2442] Fix settings panel children not processing transforms while masked away --- osu.Game/Overlays/SettingsPanel.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index 2916ea013f..79d78e6805 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -34,6 +34,12 @@ namespace osu.Game.Overlays protected override Container Content => ContentContainer; + /// + /// The always needs to be present for to process transforms while overlay is masked away. + /// todo: there may be a better solution for this and the existing , likely requires a refactor. + /// + public override bool IsPresent => true; + /// /// A horizontal offset to apply to the game-wide screen. /// From 8dc0650ca71d9143d0268268a6f21f826d658679 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 6 Aug 2021 22:36:40 +0300 Subject: [PATCH 0897/2442] Add test coverage --- .../Visual/Menus/TestSceneSideOverlays.cs | 62 +++++++++++++++++++ .../Visual/Navigation/OsuGameTestScene.cs | 7 ++- osu.Game/OsuGame.cs | 33 +++++----- osu.Game/Overlays/NotificationOverlay.cs | 8 +-- 4 files changed, 90 insertions(+), 20 deletions(-) create mode 100644 osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs diff --git a/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs b/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs new file mode 100644 index 0000000000..34259574f3 --- /dev/null +++ b/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs @@ -0,0 +1,62 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Game.Overlays; +using osu.Game.Overlays.Settings.Sections.Input; +using osu.Game.Tests.Visual.Navigation; + +namespace osu.Game.Tests.Visual.Menus +{ + public class TestSceneSideOverlays : OsuGameTestScene + { + [SetUpSteps] + public override void SetUpSteps() + { + base.SetUpSteps(); + + AddAssert("no screen offset applied", () => Game.ScreenOffsetContainer.X == 0f); + } + + [Test] + public void TestScreenOffsettingOnSettingsOverlay() + { + AddStep("open settings", () => Game.Settings.Show()); + AddUntilStep("right screen offset applied", () => Game.ScreenOffsetContainer.X == SettingsPanel.WIDTH * OsuGame.SCREEN_OFFSET_RATIO); + + AddStep("hide settings", () => Game.Settings.Hide()); + AddUntilStep("screen offset removed", () => Game.ScreenOffsetContainer.X == 0f); + } + + [Test] + public void TestScreenOffsettingAccountsForKeyBindingPanel() + { + AddStep("open settings", () => Game.Settings.Show()); + AddStep("open key binding panel", () => Game.Settings.ChildrenOfType().Single().Show()); + AddUntilStep("right screen offset applied", () => Game.ScreenOffsetContainer.X == SettingsPanel.WIDTH * OsuGame.SCREEN_OFFSET_RATIO); + + AddStep("hide key binding", () => Game.Settings.ChildrenOfType().Single().Show()); + AddUntilStep("right screen offset still applied", () => Game.ScreenOffsetContainer.X == SettingsPanel.WIDTH * OsuGame.SCREEN_OFFSET_RATIO); + + AddStep("open key binding", () => Game.Settings.Show()); + AddUntilStep("right screen offset still applied", () => Game.ScreenOffsetContainer.X == SettingsPanel.WIDTH * OsuGame.SCREEN_OFFSET_RATIO); + + AddStep("hide settings", () => Game.Settings.Hide()); + AddAssert("key binding panel still open", () => Game.Settings.ChildrenOfType().Single().State.Value == Visibility.Visible); + AddUntilStep("screen offset removed", () => Game.ScreenOffsetContainer.X == 0f); + } + + [Test] + public void TestScreenOffsettingOnNotificationOverlay() + { + AddStep("open notifications", () => Game.Notifications.Show()); + AddUntilStep("right screen offset applied", () => Game.ScreenOffsetContainer.X == -NotificationOverlay.WIDTH * OsuGame.SCREEN_OFFSET_RATIO); + + AddStep("hide notifications", () => Game.Notifications.Hide()); + AddUntilStep("screen offset removed", () => Game.ScreenOffsetContainer.X == 0f); + } + } +} diff --git a/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs b/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs index f9a991f756..5cd55ed233 100644 --- a/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs +++ b/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Platform; using osu.Framework.Screens; @@ -103,7 +104,11 @@ namespace osu.Game.Tests.Visual.Navigation public new ScoreManager ScoreManager => base.ScoreManager; - public new SettingsPanel Settings => base.Settings; + public new Container ScreenOffsetContainer => base.ScreenOffsetContainer; + + public new SettingsOverlay Settings => base.Settings; + + public new NotificationOverlay Notifications => base.Notifications; public new MusicController MusicController => base.MusicController; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 85428a12e3..d2f86f812e 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -64,6 +64,8 @@ namespace osu.Game /// public class OsuGame : OsuGameBase, IKeyBindingHandler { + public const float SCREEN_OFFSET_RATIO = 0.125f; + public Toolbar Toolbar; private ChatOverlay chatOverlay; @@ -71,7 +73,7 @@ namespace osu.Game private ChannelManager channelManager; [NotNull] - private readonly NotificationOverlay notifications = new NotificationOverlay(); + protected readonly NotificationOverlay Notifications = new NotificationOverlay(); private BeatmapListingOverlay beatmapListing; @@ -97,7 +99,7 @@ namespace osu.Game private ScalingContainer screenContainer; - private Container screenOffsetContainer; + protected Container ScreenOffsetContainer; [Resolved] private FrameworkConfigManager frameworkConfig { get; set; } @@ -312,7 +314,7 @@ namespace osu.Game case LinkAction.OpenEditorTimestamp: case LinkAction.JoinMultiplayerMatch: case LinkAction.Spectate: - waitForReady(() => notifications, _ => notifications.Post(new SimpleNotification + waitForReady(() => Notifications, _ => Notifications.Post(new SimpleNotification { Text = @"This link type is not yet supported!", Icon = FontAwesome.Solid.LifeRing, @@ -611,12 +613,12 @@ namespace osu.Game MenuCursorContainer.CanShowCursor = menuScreen?.CursorVisible ?? false; // todo: all archive managers should be able to be looped here. - SkinManager.PostNotification = n => notifications.Post(n); + SkinManager.PostNotification = n => Notifications.Post(n); - BeatmapManager.PostNotification = n => notifications.Post(n); + BeatmapManager.PostNotification = n => Notifications.Post(n); BeatmapManager.PresentImport = items => PresentBeatmap(items.First()); - ScoreManager.PostNotification = n => notifications.Post(n); + ScoreManager.PostNotification = n => Notifications.Post(n); ScoreManager.PresentImport = items => PresentScore(items.First()); // make config aware of how to lookup skins for on-screen display purposes. @@ -655,7 +657,7 @@ namespace osu.Game ActionRequested = action => volume.Adjust(action), ScrollActionRequested = (action, amount, isPrecise) => volume.Adjust(action, amount, isPrecise), }, - screenOffsetContainer = new Container + ScreenOffsetContainer = new Container { RelativeSizeAxes = Axes.Both, Children = new Drawable[] @@ -724,7 +726,7 @@ namespace osu.Game loadComponentSingleFile(onScreenDisplay, Add, true); - loadComponentSingleFile(notifications.With(d => + loadComponentSingleFile(Notifications.With(d => { d.GetToolbarHeight = () => ToolbarOffset; d.Anchor = Anchor.TopRight; @@ -733,7 +735,7 @@ namespace osu.Game loadComponentSingleFile(new CollectionManager(Storage) { - PostNotification = n => notifications.Post(n), + PostNotification = n => Notifications.Post(n), }, Add, true); loadComponentSingleFile(stableImportManager, Add); @@ -785,7 +787,7 @@ namespace osu.Game Add(new MusicKeyBindingHandler()); // side overlays which cancel each other. - var singleDisplaySideOverlays = new OverlayContainer[] { Settings, notifications }; + var singleDisplaySideOverlays = new OverlayContainer[] { Settings, Notifications }; foreach (var overlay in singleDisplaySideOverlays) { @@ -859,7 +861,7 @@ namespace osu.Game if (recentLogCount < short_term_display_limit) { - Schedule(() => notifications.Post(new SimpleErrorNotification + Schedule(() => Notifications.Post(new SimpleErrorNotification { Icon = entry.Level == LogLevel.Important ? FontAwesome.Solid.ExclamationCircle : FontAwesome.Solid.Bomb, Text = entry.Message.Truncate(256) + (entry.Exception != null && IsDeployedBuild ? "\n\nThis error has been automatically reported to the devs." : string.Empty), @@ -867,7 +869,7 @@ namespace osu.Game } else if (recentLogCount == short_term_display_limit) { - Schedule(() => notifications.Post(new SimpleNotification + Schedule(() => Notifications.Post(new SimpleNotification { Icon = FontAwesome.Solid.EllipsisH, Text = "Subsequent messages have been logged. Click to view log files.", @@ -1008,11 +1010,12 @@ namespace osu.Game { base.UpdateAfterChildren(); - screenOffsetContainer.Padding = new MarginPadding { Top = ToolbarOffset }; + ScreenOffsetContainer.Padding = new MarginPadding { Top = ToolbarOffset }; overlayContent.Padding = new MarginPadding { Top = ToolbarOffset }; - screenOffsetContainer.X = Settings.HorizontalScreenOffset * 0.125f + - notifications.HorizontalScreenOffset * 0.125f; + var settingsOffset = Settings.HorizontalScreenOffset * SCREEN_OFFSET_RATIO; + var notificationsOffset = Notifications.HorizontalScreenOffset * SCREEN_OFFSET_RATIO; + ScreenOffsetContainer.X = settingsOffset + notificationsOffset; MenuCursorContainer.CanShowCursor = (ScreenStack.CurrentScreen as IOsuScreen)?.CursorVisible ?? false; } diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index af4f41901f..9be3212159 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays public LocalisableString Title => NotificationsStrings.HeaderTitle; public LocalisableString Description => NotificationsStrings.HeaderDescription; - private const float width = 320; + public const float WIDTH = 320; public const float TRANSITION_LENGTH = 600; @@ -33,7 +33,7 @@ namespace osu.Game.Overlays /// /// A horizontal offset to apply to the game-wide screen. /// - public float HorizontalScreenOffset => -width + X; + public float HorizontalScreenOffset => -WIDTH + X; /// /// Provide a source for the toolbar height. @@ -43,7 +43,7 @@ namespace osu.Game.Overlays [BackgroundDependencyLoader] private void load() { - Width = width; + Width = WIDTH; RelativeSizeAxes = Axes.Y; Children = new Drawable[] @@ -157,7 +157,7 @@ namespace osu.Game.Overlays markAllRead(); - this.MoveToX(width, TRANSITION_LENGTH, Easing.OutQuint); + this.MoveToX(WIDTH, TRANSITION_LENGTH, Easing.OutQuint); this.FadeTo(0, TRANSITION_LENGTH, Easing.OutQuint); } From 7bbc917f75dba460e055c8eb2981f8808967c9a5 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 6 Aug 2021 22:50:57 +0200 Subject: [PATCH 0898/2442] Localise beatmap picker. --- osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs | 41 ++++++++++++++----- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs index 66886b0882..b16fb76ec3 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs @@ -11,11 +11,13 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osuTK; @@ -26,7 +28,8 @@ namespace osu.Game.Overlays.BeatmapSet private const float tile_icon_padding = 7; private const float tile_spacing = 2; - private readonly OsuSpriteText version, starRating; + private readonly OsuSpriteText version, starRating, starRatingText; + private readonly FillFlowContainer starRatingContainer; private readonly Statistic plays, favourites; public readonly DifficultiesContainer Difficulties; @@ -68,14 +71,14 @@ namespace osu.Game.Overlays.BeatmapSet OnLostHover = () => { showBeatmap(Beatmap.Value); - starRating.FadeOut(100); + starRatingContainer.FadeOut(100); }, }, new FillFlowContainer { AutoSizeAxes = Axes.Both, Spacing = new Vector2(5f), - Children = new[] + Children = new Drawable[] { version = new OsuSpriteText { @@ -83,14 +86,31 @@ namespace osu.Game.Overlays.BeatmapSet Origin = Anchor.BottomLeft, Font = OsuFont.GetFont(size: 17, weight: FontWeight.Bold) }, - starRating = new OsuSpriteText + starRatingContainer = new FillFlowContainer { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Font = OsuFont.GetFont(size: 11, weight: FontWeight.Bold), - Text = "Star Difficulty", Alpha = 0, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(2f, 0), Margin = new MarginPadding { Bottom = 1 }, + Children = new[] + { + starRatingText = new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Font = OsuFont.GetFont(size: 11, weight: FontWeight.Bold), + Text = BeatmapsetsStrings.ShowStatsStars, + }, + starRating = new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Font = OsuFont.GetFont(size: 11, weight: FontWeight.Bold), + Text = string.Empty, + }, + } }, }, }, @@ -124,6 +144,7 @@ namespace osu.Game.Overlays.BeatmapSet private void load(OsuColour colours) { starRating.Colour = colours.Yellow; + starRatingText.Colour = colours.Yellow; updateDisplay(); } @@ -149,14 +170,14 @@ namespace osu.Game.Overlays.BeatmapSet OnHovered = beatmap => { showBeatmap(beatmap); - starRating.Text = beatmap.StarDifficulty.ToString("Star Difficulty 0.##"); - starRating.FadeIn(100); + starRating.Text = beatmap.StarDifficulty.ToLocalisableString(@"0.##"); + starRatingContainer.FadeIn(100); }, OnClicked = beatmap => { Beatmap.Value = beatmap; }, }); } - starRating.FadeOut(100); + starRatingContainer.FadeOut(100); Beatmap.Value = Difficulties.FirstOrDefault()?.Beatmap; plays.Value = BeatmapSet?.OnlineInfo.PlayCount ?? 0; favourites.Value = BeatmapSet?.OnlineInfo.FavouriteCount ?? 0; @@ -300,7 +321,7 @@ namespace osu.Game.Overlays.BeatmapSet set { this.value = value; - text.Text = Value.ToString(@"N0"); + text.Text = Value.ToLocalisableString(@"N0"); } } From ae733e202ff574b9a3065b9bc72fca71b8960580 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 7 Aug 2021 00:36:52 +0300 Subject: [PATCH 0899/2442] Fix tests executing before overlays load --- osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs b/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs index 34259574f3..598998586d 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs @@ -19,6 +19,7 @@ namespace osu.Game.Tests.Visual.Menus base.SetUpSteps(); AddAssert("no screen offset applied", () => Game.ScreenOffsetContainer.X == 0f); + AddUntilStep("wait for overlays", () => Game.Settings.IsLoaded && Game.Notifications.IsLoaded); } [Test] From 9ac5c9aa2f8d426d1733140702ab3d3c518bce99 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 7 Aug 2021 01:27:54 +0300 Subject: [PATCH 0900/2442] Fix notification overlay having incorrect initial X --- osu.Game/Overlays/NotificationOverlay.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 9be3212159..cb5d4d57c6 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -43,6 +43,7 @@ namespace osu.Game.Overlays [BackgroundDependencyLoader] private void load() { + X = WIDTH; Width = WIDTH; RelativeSizeAxes = Axes.Y; From ff1730f9f8edfe29ca2eae30a3ff0442a42fd282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 7 Aug 2021 14:38:54 +0200 Subject: [PATCH 0901/2442] Do not play open/close samples for top-level menus --- osu.Game/Graphics/UserInterface/OsuMenu.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuMenu.cs b/osu.Game/Graphics/UserInterface/OsuMenu.cs index b98a29aacb..a16adcbd57 100644 --- a/osu.Game/Graphics/UserInterface/OsuMenu.cs +++ b/osu.Game/Graphics/UserInterface/OsuMenu.cs @@ -40,7 +40,7 @@ namespace osu.Game.Graphics.UserInterface protected override void AnimateOpen() { - if (!wasOpened) + if (!TopLevelMenu && !wasOpened) sampleOpen?.Play(); this.FadeIn(300, Easing.OutQuint); @@ -49,7 +49,7 @@ namespace osu.Game.Graphics.UserInterface protected override void AnimateClose() { - if (wasOpened) + if (!TopLevelMenu && wasOpened) sampleClose?.Play(); this.FadeOut(300, Easing.OutQuint); From e924ea8d934d043a446d92a0ffeea7885cee46ec Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 7 Aug 2021 18:52:27 +0300 Subject: [PATCH 0902/2442] Make `ScreenOffsetContainer` privatly settable only --- osu.Game/OsuGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index d2f86f812e..6fb884f80a 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -99,7 +99,7 @@ namespace osu.Game private ScalingContainer screenContainer; - protected Container ScreenOffsetContainer; + protected Container ScreenOffsetContainer { get; private set; } [Resolved] private FrameworkConfigManager frameworkConfig { get; set; } From fc48696718796a1cb3597e9132e362c8ec187ff5 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 7 Aug 2021 18:27:55 +0200 Subject: [PATCH 0903/2442] Localise detail buttons. --- .../Localisation/DownloadButtonStrings.cs | 24 +++++++++++++++++++ .../BeatmapSet/Buttons/FavouriteButton.cs | 3 ++- .../Buttons/HeaderDownloadButton.cs | 14 ++++++----- 3 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 osu.Game/Localisation/DownloadButtonStrings.cs diff --git a/osu.Game/Localisation/DownloadButtonStrings.cs b/osu.Game/Localisation/DownloadButtonStrings.cs new file mode 100644 index 0000000000..736f309624 --- /dev/null +++ b/osu.Game/Localisation/DownloadButtonStrings.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class DownloadButtonStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.DownloadButton"; + + /// + /// "Downloading..." + /// + public static LocalisableString Downloading => new TranslatableString(getKey(@"downloading"), @"Downloading..."); + + /// + /// "Importing..." + /// + public static LocalisableString Importing => new TranslatableString(getKey(@"importing"), @"Importing..."); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs index bb87e7151b..43dd1438f1 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs @@ -13,6 +13,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Notifications; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; using osuTK; @@ -35,7 +36,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons { if (!Enabled.Value) return string.Empty; - return (favourited.Value ? "Unfavourite" : "Favourite") + " this beatmapset"; + return favourited.Value ? BeatmapsetsStrings.ShowDetailsUnfavourite : BeatmapsetsStrings.ShowDetailsFavourite; } } diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs index cef623e59b..441ef2f6cf 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs @@ -15,9 +15,11 @@ using osu.Game.Graphics.Sprites; using osu.Game.Online; using osu.Game.Online.API; using osu.Game.Overlays.BeatmapListing.Panels; +using osu.Game.Resources.Localisation.Web; using osu.Game.Users; using osuTK; using osuTK.Graphics; +using osu.Game.Localisation; namespace osu.Game.Overlays.BeatmapSet.Buttons { @@ -27,7 +29,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons private readonly bool noVideo; - public LocalisableString TooltipText => button.Enabled.Value ? "download this beatmap" : "login to download"; + public LocalisableString TooltipText => button.Enabled.Value ? BeatmapsetsStrings.ShowDetailsDownloadDefault : BeatmapsetsStrings.ShowDetailsLoggedOut; private readonly IBindable localUser = new Bindable(); @@ -113,7 +115,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons { new OsuSpriteText { - Text = "Downloading...", + Text = DownloadButtonStrings.Downloading, Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold) }, }; @@ -124,7 +126,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons { new OsuSpriteText { - Text = "Importing...", + Text = DownloadButtonStrings.Importing, Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold) }, }; @@ -139,7 +141,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons { new OsuSpriteText { - Text = "Download", + Text = BeatmapsetsStrings.ShowDetailsDownloadDefault, Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold) }, new OsuSpriteText @@ -158,12 +160,12 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons private void enabledChanged(ValueChangedEvent e) => this.FadeColour(e.NewValue ? Color4.White : Color4.Gray, 200, Easing.OutQuint); - private string getVideoSuffixText() + private LocalisableString getVideoSuffixText() { if (!BeatmapSet.Value.OnlineInfo.HasVideo) return string.Empty; - return noVideo ? "without Video" : "with Video"; + return noVideo ? BeatmapsetsStrings.ShowDetailsDownloadNoVideo : BeatmapsetsStrings.ShowDetailsDownloadVideo; } } } From 9f3013e2c89a1ea018008cb6221d9165e72f13a2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 7 Aug 2021 19:30:12 +0300 Subject: [PATCH 0904/2442] Remove all `HorizontalScreenOffset` calculations from overlays --- osu.Game/Overlays/NotificationOverlay.cs | 5 ----- osu.Game/Overlays/SettingsOverlay.cs | 2 -- osu.Game/Overlays/SettingsPanel.cs | 5 ----- 3 files changed, 12 deletions(-) diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index cb5d4d57c6..e3956089c2 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -30,11 +30,6 @@ namespace osu.Game.Overlays private FlowContainer sections; - /// - /// A horizontal offset to apply to the game-wide screen. - /// - public float HorizontalScreenOffset => -WIDTH + X; - /// /// Provide a source for the toolbar height. /// diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index 9ed1d950e3..050502b3be 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -21,8 +21,6 @@ namespace osu.Game.Overlays public LocalisableString Title => SettingsStrings.HeaderTitle; public LocalisableString Description => SettingsStrings.HeaderDescription; - public override float HorizontalScreenOffset => base.HorizontalScreenOffset + (lastOpenedSubPanel?.HorizontalScreenOffset ?? 0f); - protected override IEnumerable CreateSections() => new SettingsSection[] { new GeneralSection(), diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index 79d78e6805..64c3be4b9a 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -40,11 +40,6 @@ namespace osu.Game.Overlays /// public override bool IsPresent => true; - /// - /// A horizontal offset to apply to the game-wide screen. - /// - public virtual float HorizontalScreenOffset => (WIDTH + Content?.X) ?? 0f; - protected Sidebar Sidebar; private SidebarButton selectedSidebarButton; From 19a19f915cde29015df6e085406af348c487bba4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 7 Aug 2021 19:56:51 +0300 Subject: [PATCH 0905/2442] Adjust settings panel to autosize to zero when hiding it Previously, when hiding the settings overlay, it remains to have a width of `56` (sidebar width), this is due to the panel content being placed next to the sidebar, so therefore the content has to move 400 (PANEL_WIDTH) + 56 (sidebar_width) backwards, for the overlay to have a width of 0 on hide. --- .../Visual/Settings/TestSceneTabletSettings.cs | 2 +- osu.Game/Overlays/SettingsOverlay.cs | 4 ++-- osu.Game/Overlays/SettingsPanel.cs | 12 ++++++++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs index a62980addf..da474a64ba 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs @@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Settings new TabletSettings(tabletHandler) { RelativeSizeAxes = Axes.None, - Width = SettingsPanel.WIDTH, + Width = SettingsPanel.PANEL_WIDTH, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, } diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index 050502b3be..55e8aee266 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -69,7 +69,7 @@ namespace osu.Game.Overlays Sidebar?.FadeColour(Color4.DarkGray, 300, Easing.OutQuint); SectionsContainer.FadeOut(300, Easing.OutQuint); - ContentContainer.MoveToX(-WIDTH, 500, Easing.OutQuint); + ContentContainer.MoveToX(-PANEL_WIDTH, 500, Easing.OutQuint); lastOpenedSubPanel = panel; break; @@ -83,7 +83,7 @@ namespace osu.Game.Overlays } } - protected override float ExpandedPosition => lastOpenedSubPanel?.State.Value == Visibility.Visible ? -WIDTH : base.ExpandedPosition; + protected override float ExpandedPosition => lastOpenedSubPanel?.State.Value == Visibility.Visible ? -PANEL_WIDTH : base.ExpandedPosition; [BackgroundDependencyLoader] private void load() diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index 64c3be4b9a..8b953e8655 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -28,7 +28,15 @@ namespace osu.Game.Overlays private const float sidebar_width = Sidebar.DEFAULT_WIDTH; - public const float WIDTH = 400; + /// + /// The width of the settings panel content, excluding the sidebar. + /// + public const float PANEL_WIDTH = 400; + + /// + /// The full width of the settings panel, including the sidebar. + /// + public const float WIDTH = sidebar_width + PANEL_WIDTH; protected Container ContentContainer; @@ -71,7 +79,7 @@ namespace osu.Game.Overlays InternalChild = ContentContainer = new NonMaskedContent { X = -WIDTH + ExpandedPosition, - Width = WIDTH, + Width = PANEL_WIDTH, RelativeSizeAxes = Axes.Y, Children = new Drawable[] { From d099bb8ab631424f6a841d4f9947dc1ca3ba45bf Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 7 Aug 2021 19:33:22 +0300 Subject: [PATCH 0906/2442] Calculate offsets from overlay `ScreenSpaceDrawQuad`s instead --- .../Visual/Menus/TestSceneSideOverlays.cs | 21 ------------------- osu.Game/OsuGame.cs | 7 ++++--- 2 files changed, 4 insertions(+), 24 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs b/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs index 598998586d..21db7e2802 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs @@ -1,12 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; using NUnit.Framework; -using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Overlays; -using osu.Game.Overlays.Settings.Sections.Input; using osu.Game.Tests.Visual.Navigation; namespace osu.Game.Tests.Visual.Menus @@ -32,24 +29,6 @@ namespace osu.Game.Tests.Visual.Menus AddUntilStep("screen offset removed", () => Game.ScreenOffsetContainer.X == 0f); } - [Test] - public void TestScreenOffsettingAccountsForKeyBindingPanel() - { - AddStep("open settings", () => Game.Settings.Show()); - AddStep("open key binding panel", () => Game.Settings.ChildrenOfType().Single().Show()); - AddUntilStep("right screen offset applied", () => Game.ScreenOffsetContainer.X == SettingsPanel.WIDTH * OsuGame.SCREEN_OFFSET_RATIO); - - AddStep("hide key binding", () => Game.Settings.ChildrenOfType().Single().Show()); - AddUntilStep("right screen offset still applied", () => Game.ScreenOffsetContainer.X == SettingsPanel.WIDTH * OsuGame.SCREEN_OFFSET_RATIO); - - AddStep("open key binding", () => Game.Settings.Show()); - AddUntilStep("right screen offset still applied", () => Game.ScreenOffsetContainer.X == SettingsPanel.WIDTH * OsuGame.SCREEN_OFFSET_RATIO); - - AddStep("hide settings", () => Game.Settings.Hide()); - AddAssert("key binding panel still open", () => Game.Settings.ChildrenOfType().Single().State.Value == Visibility.Visible); - AddUntilStep("screen offset removed", () => Game.ScreenOffsetContainer.X == 0f); - } - [Test] public void TestScreenOffsettingOnNotificationOverlay() { diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 6fb884f80a..1539d984ae 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1013,9 +1013,10 @@ namespace osu.Game ScreenOffsetContainer.Padding = new MarginPadding { Top = ToolbarOffset }; overlayContent.Padding = new MarginPadding { Top = ToolbarOffset }; - var settingsOffset = Settings.HorizontalScreenOffset * SCREEN_OFFSET_RATIO; - var notificationsOffset = Notifications.HorizontalScreenOffset * SCREEN_OFFSET_RATIO; - ScreenOffsetContainer.X = settingsOffset + notificationsOffset; + ScreenOffsetContainer.X = 0f; + + if (Settings.IsLoaded) ScreenOffsetContainer.X += (ToLocalSpace(Settings.ScreenSpaceDrawQuad.TopRight).X) * SCREEN_OFFSET_RATIO; + if (Notifications.IsLoaded) ScreenOffsetContainer.X += (ToLocalSpace(Notifications.ScreenSpaceDrawQuad.TopLeft).X - DrawWidth) * SCREEN_OFFSET_RATIO; MenuCursorContainer.CanShowCursor = (ScreenStack.CurrentScreen as IOsuScreen)?.CursorVisible ?? false; } From db270a79ab2ea0cc04a04dda75d5d7687741fc06 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 8 Aug 2021 03:54:20 +0900 Subject: [PATCH 0907/2442] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 03e01a24ca..7a0a542ee9 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 227493c74a..0a6522f15e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index ad5b26e968..00222877f1 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From 3a0dd5b019e2401f917d2656ae73ef9ddd09847b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 8 Aug 2021 01:37:45 +0300 Subject: [PATCH 0908/2442] Add more insane star difficulties for visual testing --- .../Visual/UserInterface/TestSceneStarRatingDisplay.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs index 2f3593c062..a8bc5664f3 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; +using osu.Game.Graphics.Containers; using osuTK; namespace osu.Game.Tests.Visual.UserInterface @@ -28,19 +29,19 @@ namespace osu.Game.Tests.Visual.UserInterface AutoSizeAxes = Axes.Both, Spacing = new Vector2(2f), Direction = FillDirection.Horizontal, - ChildrenEnumerable = Enumerable.Range(0, 10).Select(i => new FillFlowContainer + ChildrenEnumerable = Enumerable.Range(0, 15).Select(i => new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, AutoSizeAxes = Axes.Both, Spacing = new Vector2(2f), Direction = FillDirection.Vertical, - ChildrenEnumerable = Enumerable.Range(0, 10).Select(j => new StarRatingDisplay(new StarDifficulty(i + j * 0.1f, 0)) + ChildrenEnumerable = Enumerable.Range(0, 10).Select(j => new StarRatingDisplay(new StarDifficulty(i * (i >= 11 ? 25f : 1f) + j * 0.1f, 0)) { Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(width, height), - }) + }), }) }; }); From 9e805dcd44843ef04ea23ed26d311d3a39daebc0 Mon Sep 17 00:00:00 2001 From: TheOmyNomy Date: Sun, 8 Aug 2021 21:27:32 +1000 Subject: [PATCH 0909/2442] Fix legacy slider body colour interpolation --- .../Skinning/Legacy/LegacySliderBody.cs | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs index 744ded37c9..9e4b57ca10 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs @@ -3,7 +3,6 @@ using System; using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Utils; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Skinning.Default; using osuTK.Graphics; @@ -40,7 +39,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Color4 outerColour = AccentColour.Darken(0.1f); Color4 innerColour = lighten(AccentColour, 0.5f); - return Interpolation.ValueAt(position / realGradientPortion, outerColour, innerColour, 0, 1); + // Stable doesn't use linear space / gamma-correct colour interpolation + // for slider bodies, so we can't use Interpolation.ValueAt(). + // Instead, we use a local method that interpolates between the colours directly in sRGB space. + return valueAt(position / realGradientPortion, outerColour, innerColour, 0, 1); } /// @@ -55,6 +57,26 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Math.Min(1, color.B * (1 + 0.5f * amount) + 1 * amount), color.A); } + + private static Color4 valueAt(double time, Color4 startColour, Color4 endColour, double startTime, double endTime) + { + if (startColour == endColour) + return startColour; + + double current = time - startTime; + double duration = endTime - startTime; + + if (duration == 0 || current == 0) + return startColour; + + float t = (float)Math.Max(0, Math.Min(1, current / duration)); + + return new Color4( + startColour.R + t * (endColour.R - startColour.R), + startColour.G + t * (endColour.G - startColour.G), + startColour.B + t * (endColour.B - startColour.B), + startColour.A + t * (endColour.A - startColour.A)); + } } } } From 140d29d53762b1d0df5e44fdcd125410769ca50b Mon Sep 17 00:00:00 2001 From: TheOmyNomy Date: Sun, 8 Aug 2021 23:54:35 +1000 Subject: [PATCH 0910/2442] Use helper methods instead of local valueAt() method --- .../Skinning/Legacy/LegacySliderBody.cs | 34 +++++-------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs index 9e4b57ca10..a8bb69c080 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Utils; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Skinning.Default; using osuTK.Graphics; @@ -36,13 +37,14 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy position -= realBorderPortion; - Color4 outerColour = AccentColour.Darken(0.1f); - Color4 innerColour = lighten(AccentColour, 0.5f); + // Stable interpolates slider body colour directly in sRGB space, and because + // Interpolation.ValueAt() uses linear space, we have to counteract applying it + // by calling ToSRGB() on the input colours, and ToLinear() on the resulting colour. - // Stable doesn't use linear space / gamma-correct colour interpolation - // for slider bodies, so we can't use Interpolation.ValueAt(). - // Instead, we use a local method that interpolates between the colours directly in sRGB space. - return valueAt(position / realGradientPortion, outerColour, innerColour, 0, 1); + Color4 outerColour = AccentColour.Darken(0.1f).ToSRGB(); + Color4 innerColour = lighten(AccentColour, 0.5f).ToSRGB(); + + return Interpolation.ValueAt(position / realGradientPortion, outerColour, innerColour, 0, 1).ToLinear(); } /// @@ -57,26 +59,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Math.Min(1, color.B * (1 + 0.5f * amount) + 1 * amount), color.A); } - - private static Color4 valueAt(double time, Color4 startColour, Color4 endColour, double startTime, double endTime) - { - if (startColour == endColour) - return startColour; - - double current = time - startTime; - double duration = endTime - startTime; - - if (duration == 0 || current == 0) - return startColour; - - float t = (float)Math.Max(0, Math.Min(1, current / duration)); - - return new Color4( - startColour.R + t * (endColour.R - startColour.R), - startColour.G + t * (endColour.G - startColour.G), - startColour.B + t * (endColour.B - startColour.B), - startColour.A + t * (endColour.A - startColour.A)); - } } } } From acdd08c96649b9162f27be19d9a85f3f0bb8b3db Mon Sep 17 00:00:00 2001 From: MBmasher Date: Sun, 8 Aug 2021 23:56:03 +1000 Subject: [PATCH 0911/2442] Add Flashlight skill --- .../Difficulty/OsuDifficultyAttributes.cs | 1 + .../Difficulty/OsuDifficultyCalculator.cs | 5 +- .../Difficulty/OsuPerformanceCalculator.cs | 51 ++++++++++---- .../Difficulty/Skills/Flashlight.cs | 67 +++++++++++++++++++ .../Difficulty/Skills/OsuStrainSkill.cs | 2 + 5 files changed, 111 insertions(+), 15 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index 141138c125..1e870dac68 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty { public double AimStrain { get; set; } public double SpeedStrain { get; set; } + public double FlashlightStrain { get; set; } public double ApproachRate { get; set; } public double OverallDifficulty { get; set; } public int HitCircleCount { get; set; } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index e47f82fb39..b0dd4dc9b0 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -34,6 +34,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; + double flashlightRating = Math.Sqrt(skills[2].DifficultyValue()) * difficulty_multiplier; double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2; HitWindows hitWindows = new OsuHitWindows(); @@ -56,6 +57,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty Mods = mods, AimStrain = aimRating, SpeedStrain = speedRating, + FlashlightStrain = flashlightRating, ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5, OverallDifficulty = (80 - hitWindowGreat) / 6, MaxCombo = maxCombo, @@ -82,7 +84,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[] { new Aim(mods), - new Speed(mods) + new Speed(mods), + new Flashlight(mods) }; protected override Mod[] DifficultyAdjustmentMods => new Mod[] diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index e6ab978dfb..d409eae9af 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -52,11 +52,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty double aimValue = computeAimValue(); double speedValue = computeSpeedValue(); double accuracyValue = computeAccuracyValue(); + double flashlightValue = computeFlashlightValue(); double totalValue = Math.Pow( Math.Pow(aimValue, 1.1) + Math.Pow(speedValue, 1.1) + - Math.Pow(accuracyValue, 1.1), 1.0 / 1.1 + Math.Pow(accuracyValue, 1.1) + + Math.Pow(flashlightValue, 1.1), 1.0 / 1.1 ) * multiplier; if (categoryRatings != null) @@ -64,6 +66,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty categoryRatings.Add("Aim", aimValue); categoryRatings.Add("Speed", speedValue); categoryRatings.Add("Accuracy", accuracyValue); + categoryRatings.Add("Flashlight", flashlightValue); categoryRatings.Add("OD", Attributes.OverallDifficulty); categoryRatings.Add("AR", Attributes.ApproachRate); categoryRatings.Add("Max Combo", Attributes.MaxCombo); @@ -109,19 +112,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(h => h is OsuModHidden)) aimValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); - double flashlightBonus = 1.0; - - if (mods.Any(h => h is OsuModFlashlight)) - { - // Apply object-based bonus for flashlight. - flashlightBonus = 1.0 + 0.35 * Math.Min(1.0, totalHits / 200.0) + - (totalHits > 200 - ? 0.3 * Math.Min(1.0, (totalHits - 200) / 300.0) + - (totalHits > 500 ? (totalHits - 500) / 1200.0 : 0.0) - : 0.0); - } - - aimValue *= Math.Max(flashlightBonus, approachRateBonus); + aimValue *= approachRateBonus; // Scale the aim value with accuracy _slightly_ aimValue *= 0.5 + accuracy / 2.0; @@ -197,6 +188,38 @@ namespace osu.Game.Rulesets.Osu.Difficulty return accuracyValue; } + private double computeFlashlightValue() + { + double flashlightValue = 0.0; + + if (mods.Any(h => h is OsuModFlashlight)) { + flashlightValue = Math.Pow(Attributes.FlashlightStrain, 2.0) * 25.0; + + // Add an additional bonus for HDFL. + if (mods.Any(h => h is OsuModHidden)) + flashlightValue *= 1.2; + + // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. + if (countMiss > 0) + flashlightValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), Math.Pow(countMiss, .875)); + + // Combo scaling + if (Attributes.MaxCombo > 0) + flashlightValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); + + // Account for shorter maps having a higher ratio of 0 combo/100 combo flashlight radius. + flashlightValue *= 0.5 + 0.15 * Math.Min(1.0, totalHits / 200.0) + + (totalHits > 200 ? 0.35 * Math.Min(1.0, (totalHits - 200) / 600.0) : 0.0); + + // Scale the aim value with accuracy _slightly_ + flashlightValue *= 0.5 + accuracy / 2.0; + // It is important to also consider accuracy difficulty when doing that + flashlightValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500; + } + + return flashlightValue; + } + private int totalHits => countGreat + countOk + countMeh + countMiss; private int totalSuccessfulHits => countGreat + countOk + countMeh; } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs new file mode 100644 index 0000000000..b48e6e30c0 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -0,0 +1,67 @@ +// 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.Linq; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; +using osu.Game.Rulesets.Osu.Objects; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Difficulty.Skills +{ + /// + /// Represents the skill required to memorise and hit every object in a map with the Flashlight mod enabled. + /// + public class Flashlight : OsuStrainSkill + { + public Flashlight(Mod[] mods) + : base(mods) + { + } + + protected override double SkillMultiplier => 0.065; + protected override double StrainDecayBase => 0.15; + protected override double DecayWeight => 1.0; + + protected override double StrainValueOf(DifficultyHitObject current) + { + if (current.BaseObject is Spinner) + return 0; + + var osuCurrent = (OsuDifficultyHitObject)current; + var osuHitObject = (OsuHitObject)(osuCurrent.BaseObject); + + double scalingFactor = 52.0 / osuHitObject.Radius; + double smallDistNerf = 1.0; + + double result = 0.0; + + if (Previous.Count > 0) + { + double cumulativeStrainTime = 0.0; + + for (int i = 0; i < Previous.Count; i++) { + var osuPrevious = (OsuDifficultyHitObject)Previous[i]; + var osuPreviousHitObject = (OsuHitObject)(osuPrevious.BaseObject); + + if (!(osuPrevious.BaseObject is Spinner)) { + double JumpDistance = (osuHitObject.StackedPosition - osuPreviousHitObject.EndPosition).Length; + + cumulativeStrainTime += osuPrevious.StrainTime; + + // We want to nerf objects that can be easily seen within the Flashlight circle radius. + if (i == 0 && JumpDistance < 50.0) { + smallDistNerf = JumpDistance / 50.0; + } + + result += Math.Pow(0.8, i) * scalingFactor * JumpDistance / cumulativeStrainTime; + } + } + } + + return Math.Pow(smallDistNerf * result, 2.5); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index e47edc37cc..f74298cdca 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -28,6 +28,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// protected virtual double DifficultyMultiplier => 1.06; + protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. + protected OsuStrainSkill(Mod[] mods) : base(mods) { From c72224fa9412d72367a112e461029b864d5f782f Mon Sep 17 00:00:00 2001 From: Gabe Livengood <47010459+ggliv@users.noreply.github.com> Date: Sun, 8 Aug 2021 13:45:13 -0400 Subject: [PATCH 0912/2442] Add "Mirror" mod to osu!catch --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 1 + .../Mods/CatchModMirror.cs | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index eafa1b9b9d..9fee6b2bc1 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -117,6 +117,7 @@ namespace osu.Game.Rulesets.Catch { new CatchModDifficultyAdjust(), new CatchModClassic(), + new CatchModMirror(), }; case ModType.Automation: diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs new file mode 100644 index 0000000000..8eb092bfb3 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs @@ -0,0 +1,39 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Objects; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Mods +{ + public class CatchModMirror : ModMirror, IApplicableToHitObject + { + public override string Description => "Fruits are flipped horizontally."; + + public void ApplyToHitObject(HitObject hitObject) + { + var catchObject = (CatchHitObject)hitObject; + + if (catchObject is BananaShower) + return; + + catchObject.OriginalX = CatchPlayfield.WIDTH - catchObject.OriginalX; + + foreach (var nested in catchObject.NestedHitObjects.Cast()) + nested.OriginalX = CatchPlayfield.WIDTH - nested.OriginalX; + + if (!(catchObject is JuiceStream juiceStream)) + return; + + var controlPoints = juiceStream.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); + foreach (var point in controlPoints) + point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y); + + juiceStream.Path = new SliderPath(controlPoints, juiceStream.Path.ExpectedDistance.Value); + } + } +} From bf0d4b6ef1c9d20e69c43abb3d945b92ca2c135d Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 8 Aug 2021 10:25:51 +0200 Subject: [PATCH 0913/2442] Localise basic stats. --- osu.Game/Overlays/BeatmapSet/BasicStats.cs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/BasicStats.cs b/osu.Game/Overlays/BeatmapSet/BasicStats.cs index 3f1034759e..ce348bd753 100644 --- a/osu.Game/Overlays/BeatmapSet/BasicStats.cs +++ b/osu.Game/Overlays/BeatmapSet/BasicStats.cs @@ -13,6 +13,7 @@ using osu.Game.Beatmaps; using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; using osuTK; namespace osu.Game.Overlays.BeatmapSet @@ -53,7 +54,7 @@ namespace osu.Game.Overlays.BeatmapSet private void updateDisplay() { - bpm.Value = BeatmapSet?.OnlineInfo?.BPM.ToString(@"0.##") ?? "-"; + bpm.Value = BeatmapSet?.OnlineInfo?.BPM.ToLocalisableString(@"0.##") ?? (LocalisableString)"-"; if (beatmap == null) { @@ -63,9 +64,11 @@ namespace osu.Game.Overlays.BeatmapSet } else { + length.TooltipText = BeatmapsetsStrings.ShowStatsTotalLength(TimeSpan.FromMilliseconds(beatmap.Length).ToFormattedDuration()); length.Value = TimeSpan.FromMilliseconds(beatmap.Length).ToFormattedDuration(); - circleCount.Value = beatmap.OnlineInfo.CircleCount.ToString(); - sliderCount.Value = beatmap.OnlineInfo.SliderCount.ToString(); + + circleCount.Value = beatmap.OnlineInfo.CircleCount.ToLocalisableString("N0"); + sliderCount.Value = beatmap.OnlineInfo.SliderCount.ToLocalisableString("N0"); } } @@ -78,10 +81,10 @@ namespace osu.Game.Overlays.BeatmapSet Direction = FillDirection.Horizontal, Children = new[] { - length = new Statistic(FontAwesome.Regular.Clock, "Length") { Width = 0.25f }, - bpm = new Statistic(FontAwesome.Regular.Circle, "BPM") { Width = 0.25f }, - circleCount = new Statistic(FontAwesome.Regular.Circle, "Circle Count") { Width = 0.25f }, - sliderCount = new Statistic(FontAwesome.Regular.Circle, "Slider Count") { Width = 0.25f }, + length = new Statistic(FontAwesome.Regular.Clock, BeatmapsetsStrings.ShowStatsTotalLength(string.Empty)) { Width = 0.25f }, + bpm = new Statistic(FontAwesome.Regular.Circle, BeatmapsetsStrings.ShowStatsBpm) { Width = 0.25f }, + circleCount = new Statistic(FontAwesome.Regular.Circle, BeatmapsetsStrings.ShowStatsCountCircles) { Width = 0.25f }, + sliderCount = new Statistic(FontAwesome.Regular.Circle, BeatmapsetsStrings.ShowStatsCountSliders) { Width = 0.25f }, }, }; } @@ -96,7 +99,7 @@ namespace osu.Game.Overlays.BeatmapSet { private readonly OsuSpriteText value; - public LocalisableString TooltipText { get; } + public LocalisableString TooltipText { get; set; } public LocalisableString Value { @@ -104,7 +107,7 @@ namespace osu.Game.Overlays.BeatmapSet set => this.value.Text = value; } - public Statistic(IconUsage icon, string name) + public Statistic(IconUsage icon, LocalisableString name) { TooltipText = name; RelativeSizeAxes = Axes.X; From 3a741affa325fac471a06febff810827a76f44d5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 8 Aug 2021 21:27:48 +0300 Subject: [PATCH 0914/2442] Remove whitespaces --- osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs index 8eb092bfb3..ce5b78e12f 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Catch.Mods public class CatchModMirror : ModMirror, IApplicableToHitObject { public override string Description => "Fruits are flipped horizontally."; - + public void ApplyToHitObject(HitObject hitObject) { var catchObject = (CatchHitObject)hitObject; From 5e0f9d0af9beffed5189b7cce890b972c5b32413 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 8 Aug 2021 22:00:12 +0200 Subject: [PATCH 0915/2442] Localise user ratings. --- osu.Game/Screens/Select/Details/UserRatings.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Select/Details/UserRatings.cs b/osu.Game/Screens/Select/Details/UserRatings.cs index cf5e3ba1b3..a45bcd0666 100644 --- a/osu.Game/Screens/Select/Details/UserRatings.cs +++ b/osu.Game/Screens/Select/Details/UserRatings.cs @@ -9,6 +9,8 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using System.Linq; using osu.Game.Beatmaps; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Screens.Select.Details { @@ -35,8 +37,8 @@ namespace osu.Game.Screens.Select.Details if (metrics == null) { - negativeRatings.Text = "0"; - positiveRatings.Text = "0"; + negativeRatings.Text = 0.ToLocalisableString("N0"); + positiveRatings.Text = 0.ToLocalisableString("N0"); ratingsBar.Length = 0; graph.Values = new float[rating_range]; } @@ -47,8 +49,8 @@ namespace osu.Game.Screens.Select.Details var negativeCount = ratings.Take(rating_range / 2).Sum(); var totalCount = ratings.Sum(); - negativeRatings.Text = negativeCount.ToString(); - positiveRatings.Text = (totalCount - negativeCount).ToString(); + negativeRatings.Text = negativeCount.ToLocalisableString("N0"); + positiveRatings.Text = (totalCount - negativeCount).ToLocalisableString("N0"); ratingsBar.Length = totalCount == 0 ? 0 : (float)negativeCount / totalCount; graph.Values = ratings.Take(rating_range).Select(r => (float)r); } @@ -70,7 +72,7 @@ namespace osu.Game.Screens.Select.Details { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Text = "User Rating", + Text = BeatmapsetsStrings.ShowStatsUserRating, Font = OsuFont.GetFont(size: 12), Margin = new MarginPadding { Bottom = 5 }, }, @@ -88,14 +90,14 @@ namespace osu.Game.Screens.Select.Details { negativeRatings = new OsuSpriteText { - Text = "0", + Text = 0.ToLocalisableString("N0"), Font = OsuFont.GetFont(size: 12) }, positiveRatings = new OsuSpriteText { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - Text = @"0", + Text = 0.ToLocalisableString("N0"), Font = OsuFont.GetFont(size: 12) }, }, @@ -104,7 +106,7 @@ namespace osu.Game.Screens.Select.Details { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Text = "Rating Spread", + Text = BeatmapsetsStrings.ShowStatsRatingSpread, Font = OsuFont.GetFont(size: 12), Margin = new MarginPadding { Bottom = 5 }, }, From 6b1a4a53d44d980c28b510996269fe9f3742f270 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Mon, 9 Aug 2021 08:31:28 +1000 Subject: [PATCH 0916/2442] Cleanup of code --- .../Difficulty/OsuPerformanceCalculator.cs | 6 +++--- .../Difficulty/Skills/Flashlight.cs | 12 +++++------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index d409eae9af..3634374f50 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty Math.Pow( Math.Pow(aimValue, 1.1) + Math.Pow(speedValue, 1.1) + - Math.Pow(accuracyValue, 1.1) + + Math.Pow(accuracyValue, 1.1) + Math.Pow(flashlightValue, 1.1), 1.0 / 1.1 ) * multiplier; @@ -194,7 +194,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(h => h is OsuModFlashlight)) { flashlightValue = Math.Pow(Attributes.FlashlightStrain, 2.0) * 25.0; - + // Add an additional bonus for HDFL. if (mods.Any(h => h is OsuModHidden)) flashlightValue *= 1.2; @@ -205,7 +205,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty // Combo scaling if (Attributes.MaxCombo > 0) - flashlightValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); + flashlightValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); // Account for shorter maps having a higher ratio of 0 combo/100 combo flashlight radius. flashlightValue *= 0.5 + 0.15 * Math.Min(1.0, totalHits / 200.0) + diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index b48e6e30c0..fd771ab768 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -2,12 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Objects; -using osuTK; namespace osu.Game.Rulesets.Osu.Difficulty.Skills { @@ -45,18 +43,18 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills for (int i = 0; i < Previous.Count; i++) { var osuPrevious = (OsuDifficultyHitObject)Previous[i]; var osuPreviousHitObject = (OsuHitObject)(osuPrevious.BaseObject); - + if (!(osuPrevious.BaseObject is Spinner)) { - double JumpDistance = (osuHitObject.StackedPosition - osuPreviousHitObject.EndPosition).Length; + double jumpDistance = (osuHitObject.StackedPosition - osuPreviousHitObject.EndPosition).Length; cumulativeStrainTime += osuPrevious.StrainTime; // We want to nerf objects that can be easily seen within the Flashlight circle radius. - if (i == 0 && JumpDistance < 50.0) { - smallDistNerf = JumpDistance / 50.0; + if (i == 0 && jumpDistance < 50.0) { + smallDistNerf = jumpDistance / 50.0; } - result += Math.Pow(0.8, i) * scalingFactor * JumpDistance / cumulativeStrainTime; + result += Math.Pow(0.8, i) * scalingFactor * jumpDistance / cumulativeStrainTime; } } } From a552b659d3f3fae9ac274686c240d83d1b761d51 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sun, 8 Aug 2021 19:36:27 -0700 Subject: [PATCH 0917/2442] Update build status badge to github actions --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 016bd7d922..8f922f74a7 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ # osu! -[![Build status](https://ci.appveyor.com/api/projects/status/u2p01nx7l6og8buh?svg=true)](https://ci.appveyor.com/project/peppy/osu) +[![Build status](https://github.com/ppy/osu/actions/workflows/ci.yml/badge.svg?branch=master&event=push)](https://github.com/ppy/osu/actions/workflows/ci.yml) [![GitHub release](https://img.shields.io/github/release/ppy/osu.svg)](https://github.com/ppy/osu/releases/latest) [![CodeFactor](https://www.codefactor.io/repository/github/ppy/osu/badge)](https://www.codefactor.io/repository/github/ppy/osu) [![dev chat](https://discordapp.com/api/guilds/188630481301012481/widget.png?style=shield)](https://discord.gg/ppy) From 30cda318f93fe5947cb44335b9ed163e2294d6fc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Aug 2021 15:57:18 +0900 Subject: [PATCH 0918/2442] Reorganise code slightly --- .../Mods/CatchModMirror.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs index ce5b78e12f..7fe7e70fd0 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs @@ -16,24 +16,24 @@ namespace osu.Game.Rulesets.Catch.Mods public void ApplyToHitObject(HitObject hitObject) { - var catchObject = (CatchHitObject)hitObject; - - if (catchObject is BananaShower) + if (hitObject is BananaShower) return; + var catchObject = (CatchHitObject)hitObject; + catchObject.OriginalX = CatchPlayfield.WIDTH - catchObject.OriginalX; foreach (var nested in catchObject.NestedHitObjects.Cast()) nested.OriginalX = CatchPlayfield.WIDTH - nested.OriginalX; - if (!(catchObject is JuiceStream juiceStream)) - return; + if (catchObject is JuiceStream juiceStream) + { + var controlPoints = juiceStream.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); + foreach (var point in controlPoints) + point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y); - var controlPoints = juiceStream.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); - foreach (var point in controlPoints) - point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y); - - juiceStream.Path = new SliderPath(controlPoints, juiceStream.Path.ExpectedDistance.Value); + juiceStream.Path = new SliderPath(controlPoints, juiceStream.Path.ExpectedDistance.Value); + } } } } From 7cb743a734da2bae3b9349e573f25ff0a0a90907 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Aug 2021 16:17:51 +0900 Subject: [PATCH 0919/2442] Move font sizing to base class --- osu.Game/Screens/OnlinePlay/Match/Components/CreateRoomButton.cs | 1 + .../OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs | 1 - .../Screens/OnlinePlay/Playlists/CreatePlaylistsRoomButton.cs | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/CreateRoomButton.cs b/osu.Game/Screens/OnlinePlay/Match/Components/CreateRoomButton.cs index cd4dee5e3a..3801463095 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/CreateRoomButton.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/CreateRoomButton.cs @@ -12,6 +12,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components [BackgroundDependencyLoader] private void load() { + SpriteText.Font = SpriteText.Font.With(size: 14); Triangles.TriangleScale = 1.5f; } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs index da141f96dd..e80923ed47 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs @@ -22,7 +22,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [BackgroundDependencyLoader] private void load() { - SpriteText.Font = SpriteText.Font.With(size: 14); Text = "Create room"; isConnected = multiplayerClient.IsConnected.GetBoundCopy(); diff --git a/osu.Game/Screens/OnlinePlay/Playlists/CreatePlaylistsRoomButton.cs b/osu.Game/Screens/OnlinePlay/Playlists/CreatePlaylistsRoomButton.cs index 3fdff8c4b2..a9826a72eb 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/CreatePlaylistsRoomButton.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/CreatePlaylistsRoomButton.cs @@ -11,7 +11,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists [BackgroundDependencyLoader] private void load() { - SpriteText.Font = SpriteText.Font.With(size: 14); Text = "Create playlist"; } } From a12f6b78a4bbd265725f9981229c4dc2f7024c57 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Aug 2021 16:21:12 +0900 Subject: [PATCH 0920/2442] Split status retrieval into its own method --- .../OnlinePlay/Lounge/Components/RoomStatusPill.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs index ae17a80705..dfe7ff8cac 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs @@ -55,7 +55,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private void updateDisplay() { - RoomStatus status = EndDate.Value < DateTimeOffset.Now ? new RoomStatusEnded() : Status.Value ?? new RoomStatusOpen(); + RoomStatus status = getDisplayStatus(); pill.Background.Alpha = 1; pill.Background.FadeColour(status.GetAppropriateColour(colours), firstDisplay ? 0 : 100); @@ -63,5 +63,13 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components firstDisplay = false; } + + private RoomStatus getDisplayStatus() + { + if (EndDate.Value < DateTimeOffset.Now) + return new RoomStatusEnded(); + + return Status.Value ?? new RoomStatusOpen(); + } } } From f4739d0118eb7340205176f1a96261cf7898e7d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Aug 2021 16:28:43 +0900 Subject: [PATCH 0921/2442] Remove `MaskingSmoothness` to avoid making sheered container blurry --- .../OnlinePlay/Lounge/Components/RecentParticipantsList.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs index da410716e4..7f3fdf01d0 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs @@ -39,7 +39,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { RelativeSizeAxes = Axes.Both, Masking = true, - MaskingSmoothness = 2, CornerRadius = 10, Shear = new Vector2(0.2f, 0), Child = new Box From e08b1223ab7ef6f4d12413ae520fc8f7ca58f260 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Aug 2021 18:23:39 +0900 Subject: [PATCH 0922/2442] Move team colours to `OsuColour` --- osu.Game.Tournament/TournamentGame.cs | 4 ++-- osu.Game/Graphics/OsuColour.cs | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index cd0e601a2f..7a43fee013 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -26,8 +26,8 @@ namespace osu.Game.Tournament { public static ColourInfo GetTeamColour(TeamColour teamColour) => teamColour == TeamColour.Red ? COLOUR_RED : COLOUR_BLUE; - public static readonly Color4 COLOUR_RED = Color4Extensions.FromHex("#AA1414"); - public static readonly Color4 COLOUR_BLUE = Color4Extensions.FromHex("#1462AA"); + public static readonly Color4 COLOUR_RED = new OsuColour().TeamColourRed; + public static readonly Color4 COLOUR_BLUE = new OsuColour().TeamColourBlue; public static readonly Color4 ELEMENT_BACKGROUND_COLOUR = Color4Extensions.FromHex("#fff"); public static readonly Color4 ELEMENT_FOREGROUND_COLOUR = Color4Extensions.FromHex("#000"); diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index 1f87c06dd2..d7cfc4094c 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -130,6 +130,9 @@ namespace osu.Game.Graphics return Gray(brightness > 0.5f ? 0.2f : 0.9f); } + public readonly Color4 TeamColourRed = Color4Extensions.FromHex("#AA1414"); + public readonly Color4 TeamColourBlue = Color4Extensions.FromHex("#1462AA"); + // See https://github.com/ppy/osu-web/blob/master/resources/assets/less/colors.less public readonly Color4 PurpleLighter = Color4Extensions.FromHex(@"eeeeff"); public readonly Color4 PurpleLight = Color4Extensions.FromHex(@"aa88ff"); From aa4c6b93412e3cacb557d7195bef8bbd8e3d7be3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Aug 2021 17:53:38 +0900 Subject: [PATCH 0923/2442] Bring across tournament score display for game usage --- .../Components/TestSceneMatchScoreDisplay.cs | 2 +- ...play.cs => TournamentMatchScoreDisplay.cs} | 5 +- .../Screens/Gameplay/GameplayScreen.cs | 4 +- .../Screens/Play/HUD/MatchScoreDisplay.cs | 153 ++++++++++++++++++ 4 files changed, 159 insertions(+), 5 deletions(-) rename osu.Game.Tournament/Screens/Gameplay/Components/{MatchScoreDisplay.cs => TournamentMatchScoreDisplay.cs} (97%) create mode 100644 osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs diff --git a/osu.Game.Tournament.Tests/Components/TestSceneMatchScoreDisplay.cs b/osu.Game.Tournament.Tests/Components/TestSceneMatchScoreDisplay.cs index acd5d53310..11b5cc7556 100644 --- a/osu.Game.Tournament.Tests/Components/TestSceneMatchScoreDisplay.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneMatchScoreDisplay.cs @@ -16,7 +16,7 @@ namespace osu.Game.Tournament.Tests.Components public TestSceneMatchScoreDisplay() { - Add(new MatchScoreDisplay + Add(new TournamentMatchScoreDisplay { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/TournamentMatchScoreDisplay.cs similarity index 97% rename from osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs rename to osu.Game.Tournament/Screens/Gameplay/Components/TournamentMatchScoreDisplay.cs index 695c6d6f3e..994dee4da0 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchScoreDisplay.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/TournamentMatchScoreDisplay.cs @@ -16,7 +16,8 @@ using osuTK; namespace osu.Game.Tournament.Screens.Gameplay.Components { - public class MatchScoreDisplay : CompositeDrawable + // TODO: Update to derive from osu-side class? + public class TournamentMatchScoreDisplay : CompositeDrawable { private const float bar_height = 18; @@ -29,7 +30,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components private readonly Drawable score1Bar; private readonly Drawable score2Bar; - public MatchScoreDisplay() + public TournamentMatchScoreDisplay() { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index f61506d7f2..540b45eb56 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -86,7 +86,7 @@ namespace osu.Game.Tournament.Screens.Gameplay }, } }, - scoreDisplay = new MatchScoreDisplay + scoreDisplay = new TournamentMatchScoreDisplay { Y = -147, Anchor = Anchor.BottomCentre, @@ -148,7 +148,7 @@ namespace osu.Game.Tournament.Screens.Gameplay } private ScheduledDelegate scheduledOperation; - private MatchScoreDisplay scoreDisplay; + private TournamentMatchScoreDisplay scoreDisplay; private TourneyState lastState; private MatchHeader header; diff --git a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs new file mode 100644 index 0000000000..3df4925972 --- /dev/null +++ b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs @@ -0,0 +1,153 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osuTK; + +namespace osu.Game.Screens.Play.HUD +{ + public class MatchScoreDisplay : CompositeDrawable + { + private const float bar_height = 18; + + public BindableInt Team1Score = new BindableInt(); + public BindableInt Team2Score = new BindableInt(); + + private MatchScoreCounter score1Text; + private MatchScoreCounter score2Text; + + private Drawable score1Bar; + private Drawable score2Bar; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + InternalChildren = new[] + { + new Box + { + Name = "top bar red (static)", + RelativeSizeAxes = Axes.X, + Height = bar_height / 4, + Width = 0.5f, + Colour = colours.TeamColourRed, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopRight + }, + new Box + { + Name = "top bar blue (static)", + RelativeSizeAxes = Axes.X, + Height = bar_height / 4, + Width = 0.5f, + Colour = colours.TeamColourBlue, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopLeft + }, + score1Bar = new Box + { + Name = "top bar red", + RelativeSizeAxes = Axes.X, + Height = bar_height, + Width = 0, + Colour = colours.TeamColourRed, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopRight + }, + score1Text = new MatchScoreCounter + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre + }, + score2Bar = new Box + { + Name = "top bar blue", + RelativeSizeAxes = Axes.X, + Height = bar_height, + Width = 0, + Colour = colours.TeamColourBlue, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopLeft + }, + score2Text = new MatchScoreCounter + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre + }, + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Team1Score.BindValueChanged(_ => updateScores()); + Team2Score.BindValueChanged(_ => updateScores()); + } + + private void updateScores() + { + score1Text.Current.Value = Team1Score.Value; + score2Text.Current.Value = Team2Score.Value; + + var winningText = Team1Score.Value > Team2Score.Value ? score1Text : score2Text; + var losingText = Team1Score.Value <= Team2Score.Value ? score1Text : score2Text; + + winningText.Winning = true; + losingText.Winning = false; + + var winningBar = Team1Score.Value > Team2Score.Value ? score1Bar : score2Bar; + var losingBar = Team1Score.Value <= Team2Score.Value ? score1Bar : score2Bar; + + var diff = Math.Max(Team1Score.Value, Team2Score.Value) - Math.Min(Team1Score.Value, Team2Score.Value); + + losingBar.ResizeWidthTo(0, 400, Easing.OutQuint); + winningBar.ResizeWidthTo(Math.Min(0.4f, MathF.Pow(diff / 1500000f, 0.5f) / 2), 400, Easing.OutQuint); + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + score1Text.X = -Math.Max(5 + score1Text.DrawWidth / 2, score1Bar.DrawWidth); + score2Text.X = Math.Max(5 + score2Text.DrawWidth / 2, score2Bar.DrawWidth); + } + + private class MatchScoreCounter : ScoreCounter + { + private OsuSpriteText displayedSpriteText; + + public MatchScoreCounter() + { + Margin = new MarginPadding { Top = bar_height, Horizontal = 10 }; + } + + public bool Winning + { + set => updateFont(value); + } + + protected override OsuSpriteText CreateSpriteText() => base.CreateSpriteText().With(s => + { + displayedSpriteText = s; + displayedSpriteText.Spacing = new Vector2(-6); + updateFont(false); + }); + + private void updateFont(bool winning) + => displayedSpriteText.Font = winning + ? OsuFont.Torus.With(weight: FontWeight.Bold, size: 50, fixedWidth: true) + : OsuFont.Torus.With(weight: FontWeight.Regular, size: 40, fixedWidth: true); + } + } +} From fcec714b4f545ccc5fbddd4d21a61508f5b7bc62 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Aug 2021 19:01:07 +0900 Subject: [PATCH 0924/2442] Add safeties to avoid `MultiplayerPlayer` crashing when beatmap can't be loaded --- .../OnlinePlay/Multiplayer/MultiplayerPlayer.cs | 13 +++++++++++++ osu.Game/Screens/Play/Player.cs | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index b54a4a7726..404fc21fc8 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -57,6 +57,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [BackgroundDependencyLoader] private void load() { + if (!LoadedBeatmapSuccessfully) + return; + // todo: this should be implemented via a custom HUD implementation, and correctly masked to the main content area. LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(ScoreProcessor, userIds), HUDOverlay.Add); @@ -67,6 +70,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { base.LoadAsyncComplete(); + if (!LoadedBeatmapSuccessfully) + return; + if (!ValidForResume) return; // token retrieval may have failed. @@ -96,6 +102,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { base.LoadComplete(); + if (!LoadedBeatmapSuccessfully) + return; + ((IBindable)leaderboard.Expanded).BindTo(HUDOverlay.ShowHud); } @@ -118,6 +127,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override void Update() { base.Update(); + + if (!LoadedBeatmapSuccessfully) + return; + adjustLeaderboardPosition(); } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 09eaf1c543..1692975210 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -962,7 +962,7 @@ namespace osu.Game.Screens.Play screenSuspension?.Expire(); // if arriving here and the results screen preparation task hasn't run, it's safe to say the user has not completed the beatmap. - if (prepareScoreForDisplayTask == null) + if (Score != null && prepareScoreForDisplayTask == null) { Score.ScoreInfo.Passed = false; // potentially should be ScoreRank.F instead? this is the best alternative for now. From 54ffb8dc4e61cce3e37cec371a7067e938074062 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Aug 2021 19:01:16 +0900 Subject: [PATCH 0925/2442] Add basic multiplayer gameplay test coverage --- .../Multiplayer/TestMultiplayerGameplay.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestMultiplayerGameplay.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestMultiplayerGameplay.cs b/osu.Game.Tests/Visual/Multiplayer/TestMultiplayerGameplay.cs new file mode 100644 index 0000000000..4b75121575 --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestMultiplayerGameplay.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Game.Screens.OnlinePlay.Multiplayer; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestMultiplayerGameplay : MultiplayerTestScene + { + [Test] + public void TestBasic() + { + AddStep("load screen", () => + LoadScreen(new MultiplayerPlayer(Client.CurrentMatchPlayingItem.Value, Client.Room?.Users.Select(u => u.UserID).ToArray()))); + } + } +} From 0fa1f085df9720b6da39342da2ab6f2ae9337bb0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Aug 2021 20:06:57 +0900 Subject: [PATCH 0926/2442] Store `MultiplayerRoomUser` as part of tracked data --- .../Multiplayer/Spectate/MultiSpectatorLeaderboard.cs | 7 ++++--- .../Play/HUD/MultiplayerGameplayLeaderboard.cs | 11 +++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorLeaderboard.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorLeaderboard.cs index 55c4270c70..95f9fa27f1 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorLeaderboard.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorLeaderboard.cs @@ -4,6 +4,7 @@ using System; using JetBrains.Annotations; using osu.Framework.Timing; +using osu.Game.Online.Multiplayer; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play.HUD; @@ -32,7 +33,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate ((SpectatingTrackedUserData)data).Clock = null; } - protected override TrackedUserData CreateUserData(int userId, ScoreProcessor scoreProcessor) => new SpectatingTrackedUserData(userId, scoreProcessor); + protected override TrackedUserData CreateUserData(MultiplayerRoomUser user, ScoreProcessor scoreProcessor) => new SpectatingTrackedUserData(user, scoreProcessor); protected override void Update() { @@ -47,8 +48,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate [CanBeNull] public IClock Clock; - public SpectatingTrackedUserData(int userId, ScoreProcessor scoreProcessor) - : base(userId, scoreProcessor) + public SpectatingTrackedUserData(MultiplayerRoomUser user, ScoreProcessor scoreProcessor) + : base(user, scoreProcessor) { } diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index a10c16fcd5..74e5710677 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -11,6 +11,7 @@ using osu.Game.Configuration; using osu.Game.Database; using osu.Game.Online.API; using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Online.Spectator; using osu.Game.Rulesets.Scoring; @@ -55,7 +56,9 @@ namespace osu.Game.Screens.Play.HUD foreach (var userId in playingUsers) { - var trackedUser = CreateUserData(userId, scoreProcessor); + var user = multiplayerClient.Room?.Users.FirstOrDefault(u => u.UserID == userId); + + var trackedUser = CreateUserData(user, scoreProcessor); trackedUser.ScoringMode.BindTo(scoringMode); UserScores[userId] = trackedUser; } @@ -145,7 +148,7 @@ namespace osu.Game.Screens.Play.HUD protected class TrackedUserData { - public readonly int UserId; + public readonly MultiplayerRoomUser User; public readonly ScoreProcessor ScoreProcessor; public readonly BindableDouble Score = new BindableDouble(); @@ -157,9 +160,9 @@ namespace osu.Game.Screens.Play.HUD public readonly List Frames = new List(); - public TrackedUserData(int userId, ScoreProcessor scoreProcessor) + public TrackedUserData(MultiplayerRoomUser user, ScoreProcessor scoreProcessor) { - UserId = userId; + User = user; ScoreProcessor = scoreProcessor; ScoringMode.BindValueChanged(_ => UpdateScore()); From 1f69c61fd87a5a8b983bdcdbbca6be6d7811e198 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Aug 2021 16:38:21 +0900 Subject: [PATCH 0927/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 7a0a542ee9..f757847c10 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 0a6522f15e..3a840296ac 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 00222877f1..217ec6089a 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 1e5d9003d389d3ef882c4aa9c3d63c2f0a63f887 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Aug 2021 16:44:50 +0900 Subject: [PATCH 0928/2442] Add the ability for tests to alter the room and user states which during testing --- ...TestSceneMultiplayerGameplayLeaderboard.cs | 4 +++ .../Multiplayer/MultiplayerTestScene.cs | 29 +++++++++++-------- .../Multiplayer/TestMultiplayerClient.cs | 7 ++++- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs index 0e368b59dd..0aa47f0899 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs @@ -20,6 +20,7 @@ using osu.Game.Scoring; using osu.Game.Screens.Play.HUD; using osu.Game.Tests.Visual.OnlinePlay; using osu.Game.Tests.Visual.Spectator; +using osu.Game.Users; namespace osu.Game.Tests.Visual.Multiplayer { @@ -53,7 +54,10 @@ namespace osu.Game.Tests.Visual.Multiplayer var playable = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value); foreach (var user in users) + { SpectatorClient.StartPlay(user, Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0); + OnlinePlayDependencies.Client.AddUser(new User { Id = user }); + } // Todo: This is REALLY bad. Client.CurrentMatchPlayingUserIds.AddRange(users); diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs index 42345b7266..f259784170 100644 --- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs +++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs @@ -36,24 +36,29 @@ namespace osu.Game.Tests.Visual.Multiplayer { if (joinRoom) { - var room = new Room - { - Name = { Value = "test name" }, - Playlist = - { - new PlaylistItem - { - Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo }, - Ruleset = { Value = Ruleset.Value } - } - } - }; + var room = CreateRoom(); RoomManager.CreateRoom(room); SelectedRoom.Value = room; } }); + protected virtual Room CreateRoom() + { + return new Room + { + Name = { Value = "test name" }, + Playlist = + { + new PlaylistItem + { + Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo }, + Ruleset = { Value = Ruleset.Value } + } + } + }; + } + public override void SetUpSteps() { base.SetUpSteps(); diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 43aadf5acb..db491aac25 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -50,7 +50,12 @@ namespace osu.Game.Tests.Visual.Multiplayer public void Disconnect() => isConnected.Value = false; - public void AddUser(User user) => ((IMultiplayerClient)this).UserJoined(new MultiplayerRoomUser(user.Id) { User = user }); + public MultiplayerRoomUser AddUser(User user) + { + var roomUser = new MultiplayerRoomUser(user.Id) { User = user }; + ((IMultiplayerClient)this).UserJoined(roomUser); + return roomUser; + } public void AddNullUser(int userId) => ((IMultiplayerClient)this).UserJoined(new MultiplayerRoomUser(userId)); From ab522e1569e23000df98840a2f286f233a973284 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Aug 2021 16:50:25 +0900 Subject: [PATCH 0929/2442] Add test coverage of team display on leaderboard --- ...ceneMultiplayerGameplayLeaderboardTeams.cs | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs new file mode 100644 index 0000000000..0551f3b802 --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs @@ -0,0 +1,99 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Testing; +using osu.Framework.Utils; +using osu.Game.Configuration; +using osu.Game.Online.API; +using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; +using osu.Game.Online.Rooms; +using osu.Game.Rulesets.Osu.Scoring; +using osu.Game.Screens.Play.HUD; +using osu.Game.Tests.Visual.OnlinePlay; +using osu.Game.Tests.Visual.Spectator; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneMultiplayerGameplayLeaderboardTeams : MultiplayerTestScene + { + private static IEnumerable users => Enumerable.Range(0, 16); + + public new TestSceneMultiplayerGameplayLeaderboard.TestMultiplayerSpectatorClient SpectatorClient => + (TestSceneMultiplayerGameplayLeaderboard.TestMultiplayerSpectatorClient)OnlinePlayDependencies?.SpectatorClient; + + protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new TestDependencies(); + + protected class TestDependencies : MultiplayerTestSceneDependencies + { + protected override TestSpectatorClient CreateSpectatorClient() => new TestSceneMultiplayerGameplayLeaderboard.TestMultiplayerSpectatorClient(); + } + + private MultiplayerGameplayLeaderboard leaderboard; + + protected override Room CreateRoom() + { + var room = base.CreateRoom(); + room.Type.Value = MatchType.TeamVersus; + return room; + } + + [SetUpSteps] + public override void SetUpSteps() + { + AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = LookupCache.GetUserAsync(1).Result); + + AddStep("create leaderboard", () => + { + leaderboard?.Expire(); + + OsuScoreProcessor scoreProcessor; + Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value); + + var playable = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value); + + foreach (var user in users) + { + SpectatorClient.StartPlay(user, Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0); + var roomUser = OnlinePlayDependencies.Client.AddUser(new User { Id = user }); + + roomUser.MatchState = new TeamVersusUserState + { + TeamID = RNG.Next(0, 2) + }; + } + + // Todo: This is REALLY bad. + Client.CurrentMatchPlayingUserIds.AddRange(users); + + Children = new Drawable[] + { + scoreProcessor = new OsuScoreProcessor(), + }; + + scoreProcessor.ApplyBeatmap(playable); + + LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(scoreProcessor, users.ToArray()) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, Add); + }); + + AddUntilStep("wait for load", () => leaderboard.IsLoaded); + AddUntilStep("wait for user population", () => Client.CurrentMatchPlayingUserIds.Count > 0); + } + + [Test] + public void TestScoreUpdates() + { + AddRepeatStep("update state", () => SpectatorClient.RandomlyUpdateState(), 100); + AddToggleStep("switch compact mode", expanded => leaderboard.Expanded.Value = expanded); + } + } +} From e1d4eee1d2439167fa2362deaffe3dcb137956c6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Aug 2021 16:59:24 +0900 Subject: [PATCH 0930/2442] Add the ability to set custom overriding colours on `GameplayLeaderboardScore`s --- osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs | 10 ++++++---- .../Screens/Play/HUD/GameplayLeaderboardScore.cs | 16 ++++++++++------ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index 34efeab54c..63cb4f89f5 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -48,10 +48,9 @@ namespace osu.Game.Screens.Play.HUD /// public ILeaderboardScore AddPlayer([CanBeNull] User user, bool isTracked) { - var drawable = new GameplayLeaderboardScore(user, isTracked) - { - Expanded = { BindTarget = Expanded }, - }; + var drawable = CreateLeaderboardScoreDrawable(user, isTracked); + + drawable.Expanded.BindTo(Expanded); base.Add(drawable); drawable.TotalScore.BindValueChanged(_ => sorting.Invalidate(), true); @@ -61,6 +60,9 @@ namespace osu.Game.Screens.Play.HUD return drawable; } + protected virtual GameplayLeaderboardScore CreateLeaderboardScoreDrawable(User user, bool isTracked) => + new GameplayLeaderboardScore(user, isTracked); + public sealed override void Add(GameplayLeaderboardScore drawable) { throw new NotSupportedException($"Use {nameof(AddPlayer)} instead."); diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 10476e5565..433bf78e9b 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -54,6 +54,10 @@ namespace osu.Game.Screens.Play.HUD public BindableInt Combo { get; } = new BindableInt(); public BindableBool HasQuit { get; } = new BindableBool(); + public Color4? BackgroundColour { get; set; } + + public Color4? TextColour { get; set; } + private int? scorePosition; public int? ScorePosition @@ -331,19 +335,19 @@ namespace osu.Game.Screens.Play.HUD if (scorePosition == 1) { widthExtension = true; - panelColour = Color4Extensions.FromHex("7fcc33"); - textColour = Color4.White; + panelColour = BackgroundColour ?? Color4Extensions.FromHex("7fcc33"); + textColour = TextColour ?? Color4.White; } else if (trackedPlayer) { widthExtension = true; - panelColour = Color4Extensions.FromHex("ffd966"); - textColour = Color4Extensions.FromHex("2e576b"); + panelColour = BackgroundColour ?? Color4Extensions.FromHex("ffd966"); + textColour = TextColour ?? Color4Extensions.FromHex("2e576b"); } else { - panelColour = Color4Extensions.FromHex("3399cc"); - textColour = Color4.White; + panelColour = BackgroundColour ?? Color4Extensions.FromHex("3399cc"); + textColour = TextColour ?? Color4.White; } this.TransformTo(nameof(SizeContainerLeftPadding), widthExtension ? -top_player_left_width_extension : 0, panel_transition_duration, Easing.OutElastic); From 77c9aadd05d3495a9f55bbf6d54c19305ca821ed Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Aug 2021 17:04:06 +0900 Subject: [PATCH 0931/2442] Add team colour support to multiplaye gameplay leaderboard panels --- .../HUD/MultiplayerGameplayLeaderboard.cs | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 74e5710677..36a0365775 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -7,13 +7,17 @@ using System.Collections.Specialized; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; using osu.Game.Configuration; using osu.Game.Database; +using osu.Game.Graphics; using osu.Game.Online.API; using osu.Game.Online.Multiplayer; -using osu.Game.Online.Rooms; +using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; using osu.Game.Online.Spectator; using osu.Game.Rulesets.Scoring; +using osu.Game.Users; +using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD { @@ -103,6 +107,34 @@ namespace osu.Game.Screens.Play.HUD spectatorClient.OnNewFrames += handleIncomingFrames; } + [Resolved] + private OsuColour colours { get; set; } + + protected override GameplayLeaderboardScore CreateLeaderboardScoreDrawable(User user, bool isTracked) + { + var leaderboardScore = base.CreateLeaderboardScoreDrawable(user, isTracked); + + if (UserScores[user.Id].Team is int team) + { + leaderboardScore.BackgroundColour = getTeamColour(team).Lighten(1.2f); + leaderboardScore.TextColour = Color4.White; + } + + return leaderboardScore; + } + + private Color4 getTeamColour(int team) + { + switch (team) + { + case 0: + return colours.TeamColourRed; + + default: + return colours.TeamColourBlue; + } + } + private void usersChanged(object sender, NotifyCollectionChangedEventArgs e) { switch (e.Action) @@ -129,7 +161,7 @@ namespace osu.Game.Screens.Play.HUD trackedData.UpdateScore(); }); - protected virtual TrackedUserData CreateUserData(int userId, ScoreProcessor scoreProcessor) => new TrackedUserData(userId, scoreProcessor); + protected virtual TrackedUserData CreateUserData(MultiplayerRoomUser user, ScoreProcessor scoreProcessor) => new TrackedUserData(user, scoreProcessor); protected override void Dispose(bool isDisposing) { @@ -160,6 +192,8 @@ namespace osu.Game.Screens.Play.HUD public readonly List Frames = new List(); + public int? Team => (User.MatchState as TeamVersusUserState)?.TeamID; + public TrackedUserData(MultiplayerRoomUser user, ScoreProcessor scoreProcessor) { User = user; From cdc173e86782026e7d6b11f3b9d0f3af26fbb672 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Aug 2021 17:07:50 +0900 Subject: [PATCH 0932/2442] Add tracking of team totals to leaderboard implementation --- ...ceneMultiplayerGameplayLeaderboardTeams.cs | 6 ++++ .../HUD/MultiplayerGameplayLeaderboard.cs | 32 +++++++++++++++++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs index 0551f3b802..6376d4e305 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs @@ -83,6 +83,12 @@ namespace osu.Game.Tests.Visual.Multiplayer Anchor = Anchor.Centre, Origin = Anchor.Centre, }, Add); + + LoadComponentAsync(new MatchScoreDisplay + { + Team1Score = { BindTarget = leaderboard.Team1Score }, + Team2Score = { BindTarget = leaderboard.Team2Score } + }, Add); }); AddUntilStep("wait for load", () => leaderboard.IsLoaded); diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 36a0365775..19019e61f0 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -26,6 +26,12 @@ namespace osu.Game.Screens.Play.HUD { protected readonly Dictionary UserScores = new Dictionary(); + public readonly BindableInt Team1Score = new BindableInt(); + public readonly BindableInt Team2Score = new BindableInt(); + + [Resolved] + private OsuColour colours { get; set; } + [Resolved] private SpectatorClient spectatorClient { get; set; } @@ -39,6 +45,8 @@ namespace osu.Game.Screens.Play.HUD private readonly BindableList playingUsers; private Bindable scoringMode; + private bool hasTeams; + /// /// Construct a new leaderboard. /// @@ -65,6 +73,8 @@ namespace osu.Game.Screens.Play.HUD var trackedUser = CreateUserData(user, scoreProcessor); trackedUser.ScoringMode.BindTo(scoringMode); UserScores[userId] = trackedUser; + + hasTeams |= trackedUser.Team != null; } userLookupCache.GetUsersAsync(playingUsers.ToArray()).ContinueWith(users => Schedule(() => @@ -107,8 +117,7 @@ namespace osu.Game.Screens.Play.HUD spectatorClient.OnNewFrames += handleIncomingFrames; } - [Resolved] - private OsuColour colours { get; set; } + protected virtual TrackedUserData CreateUserData(MultiplayerRoomUser user, ScoreProcessor scoreProcessor) => new TrackedUserData(user, scoreProcessor); protected override GameplayLeaderboardScore CreateLeaderboardScoreDrawable(User user, bool isTracked) { @@ -159,9 +168,26 @@ namespace osu.Game.Screens.Play.HUD trackedData.Frames.Add(new TimedFrame(bundle.Frames.First().Time, bundle.Header)); trackedData.UpdateScore(); + + updateTotals(); }); - protected virtual TrackedUserData CreateUserData(MultiplayerRoomUser user, ScoreProcessor scoreProcessor) => new TrackedUserData(user, scoreProcessor); + private void updateTotals() + { + if (!hasTeams) + return; + + Team1Score.Value = 0; + Team2Score.Value = 0; + + foreach (var u in UserScores.Values) + { + if (u.Team == 0) + Team1Score.Value += (int)u.Score.Value; + else + Team2Score.Value += (int)u.Score.Value; + } + } protected override void Dispose(bool isDisposing) { From ebbf6467e815091149460b54b2ed6f5fec97e5b9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Aug 2021 17:23:02 +0900 Subject: [PATCH 0933/2442] Support more than two teams --- ...ceneMultiplayerGameplayLeaderboardTeams.cs | 17 ++++++++-------- .../HUD/MultiplayerGameplayLeaderboard.cs | 20 +++++++++---------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs index 6376d4e305..ce3add84c5 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs @@ -4,11 +4,9 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Framework.Utils; -using osu.Game.Configuration; using osu.Game.Online.API; using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; using osu.Game.Online.Rooms; @@ -82,13 +80,16 @@ namespace osu.Game.Tests.Visual.Multiplayer { Anchor = Anchor.Centre, Origin = Anchor.Centre, - }, Add); - - LoadComponentAsync(new MatchScoreDisplay + }, gameplayLeaderboard => { - Team1Score = { BindTarget = leaderboard.Team1Score }, - Team2Score = { BindTarget = leaderboard.Team2Score } - }, Add); + LoadComponentAsync(new MatchScoreDisplay + { + Team1Score = { BindTarget = leaderboard.TeamScores[0] }, + Team2Score = { BindTarget = leaderboard.TeamScores[1] } + }, Add); + + Add(gameplayLeaderboard); + }); }); AddUntilStep("wait for load", () => leaderboard.IsLoaded); diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 19019e61f0..2895d0cb5c 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -26,8 +26,7 @@ namespace osu.Game.Screens.Play.HUD { protected readonly Dictionary UserScores = new Dictionary(); - public readonly BindableInt Team1Score = new BindableInt(); - public readonly BindableInt Team2Score = new BindableInt(); + public readonly Dictionary TeamScores = new Dictionary(); [Resolved] private OsuColour colours { get; set; } @@ -45,7 +44,7 @@ namespace osu.Game.Screens.Play.HUD private readonly BindableList playingUsers; private Bindable scoringMode; - private bool hasTeams; + private bool hasTeams => TeamScores.Count > 0; /// /// Construct a new leaderboard. @@ -74,7 +73,8 @@ namespace osu.Game.Screens.Play.HUD trackedUser.ScoringMode.BindTo(scoringMode); UserScores[userId] = trackedUser; - hasTeams |= trackedUser.Team != null; + if (trackedUser.Team is int team && !TeamScores.ContainsKey(team)) + TeamScores.Add(team, new BindableInt()); } userLookupCache.GetUsersAsync(playingUsers.ToArray()).ContinueWith(users => Schedule(() => @@ -177,15 +177,15 @@ namespace osu.Game.Screens.Play.HUD if (!hasTeams) return; - Team1Score.Value = 0; - Team2Score.Value = 0; + foreach (var scores in TeamScores.Values) scores.Value = 0; foreach (var u in UserScores.Values) { - if (u.Team == 0) - Team1Score.Value += (int)u.Score.Value; - else - Team2Score.Value += (int)u.Score.Value; + if (u.Team == null) + continue; + + if (TeamScores.TryGetValue(u.Team.Value, out var team)) + team.Value += (int)u.Score.Value; } } From 24accdcab05b99e1b01a8f71d0f41609738d5d3c Mon Sep 17 00:00:00 2001 From: TheOmyNomy Date: Mon, 9 Aug 2021 18:56:47 +1000 Subject: [PATCH 0934/2442] Add LegacyUtils class with non linear colour interpolation method --- .../Skinning/Legacy/LegacySliderBody.cs | 12 ++-- osu.Game/Utils/LegacyUtils.cs | 65 +++++++++++++++++++ 2 files changed, 69 insertions(+), 8 deletions(-) create mode 100644 osu.Game/Utils/LegacyUtils.cs diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs index a8bb69c080..92941665e0 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs @@ -3,9 +3,9 @@ using System; using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Utils; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Skinning.Default; +using osu.Game.Utils; using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Skinning.Legacy @@ -37,14 +37,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy position -= realBorderPortion; - // Stable interpolates slider body colour directly in sRGB space, and because - // Interpolation.ValueAt() uses linear space, we have to counteract applying it - // by calling ToSRGB() on the input colours, and ToLinear() on the resulting colour. + Color4 outerColour = AccentColour.Darken(0.1f); + Color4 innerColour = lighten(AccentColour, 0.5f); - Color4 outerColour = AccentColour.Darken(0.1f).ToSRGB(); - Color4 innerColour = lighten(AccentColour, 0.5f).ToSRGB(); - - return Interpolation.ValueAt(position / realGradientPortion, outerColour, innerColour, 0, 1).ToLinear(); + return LegacyUtils.InterpolateNonLinear(position / realGradientPortion, outerColour, innerColour, 0, 1); } /// diff --git a/osu.Game/Utils/LegacyUtils.cs b/osu.Game/Utils/LegacyUtils.cs new file mode 100644 index 0000000000..9351125acd --- /dev/null +++ b/osu.Game/Utils/LegacyUtils.cs @@ -0,0 +1,65 @@ +// 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 osu.Framework.Graphics; +using osu.Framework.Graphics.Transforms; +using osuTK.Graphics; + +namespace osu.Game.Utils +{ + public static class LegacyUtils + { + public static Color4 InterpolateNonLinear(double time, Color4 startColour, Color4 endColour, double startTime, double endTime, Easing easing = Easing.None) + => InterpolateNonLinear(time, startColour, endColour, startTime, endTime, new DefaultEasingFunction(easing)); + + public static Colour4 InterpolateNonLinear(double time, Colour4 startColour, Colour4 endColour, double startTime, double endTime, Easing easing = Easing.None) + => InterpolateNonLinear(time, startColour, endColour, startTime, endTime, new DefaultEasingFunction(easing)); + + /// + /// Interpolates between two sRGB s directly in sRGB space. + /// + public static Color4 InterpolateNonLinear(double time, Color4 startColour, Color4 endColour, double startTime, double endTime, TEasing easing) where TEasing : IEasingFunction + { + if (startColour == endColour) + return startColour; + + double current = time - startTime; + double duration = endTime - startTime; + + if (duration == 0 || current == 0) + return startColour; + + float t = Math.Max(0, Math.Min(1, (float)easing.ApplyEasing(current / duration))); + + return new Color4( + startColour.R + t * (endColour.R - startColour.R), + startColour.G + t * (endColour.G - startColour.G), + startColour.B + t * (endColour.B - startColour.B), + startColour.A + t * (endColour.A - startColour.A)); + } + + /// + /// Interpolates between two sRGB s directly in sRGB space. + /// + public static Colour4 InterpolateNonLinear(double time, Colour4 startColour, Colour4 endColour, double startTime, double endTime, in TEasing easing) where TEasing : IEasingFunction + { + if (startColour == endColour) + return startColour; + + double current = time - startTime; + double duration = endTime - startTime; + + if (duration == 0 || current == 0) + return startColour; + + float t = Math.Max(0, Math.Min(1, (float)easing.ApplyEasing(current / duration))); + + return new Colour4( + startColour.R + t * (endColour.R - startColour.R), + startColour.G + t * (endColour.G - startColour.G), + startColour.B + t * (endColour.B - startColour.B), + startColour.A + t * (endColour.A - startColour.A)); + } + } +} From 121648b5937f6370183bbb0a184973d3f8436f82 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Aug 2021 18:13:28 +0900 Subject: [PATCH 0935/2442] Add gameplay-specific team score display which can expand and contract --- ...ceneMultiplayerGameplayLeaderboardTeams.cs | 16 ++++++- .../Multiplayer/GameplayMatchScoreDisplay.cs | 40 ++++++++++++++++++ .../Multiplayer/MultiplayerPlayer.cs | 42 +++++++++++++------ .../Screens/Play/HUD/MatchScoreDisplay.cs | 42 ++++++++++++------- 4 files changed, 111 insertions(+), 29 deletions(-) create mode 100644 osu.Game/Screens/OnlinePlay/Multiplayer/GameplayMatchScoreDisplay.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs index ce3add84c5..8eaa63a166 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs @@ -11,6 +11,7 @@ using osu.Game.Online.API; using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; using osu.Game.Online.Rooms; using osu.Game.Rulesets.Osu.Scoring; +using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.Play.HUD; using osu.Game.Tests.Visual.OnlinePlay; using osu.Game.Tests.Visual.Spectator; @@ -33,6 +34,7 @@ namespace osu.Game.Tests.Visual.Multiplayer } private MultiplayerGameplayLeaderboard leaderboard; + private GameplayMatchScoreDisplay gameplayScoreDisplay; protected override Room CreateRoom() { @@ -88,6 +90,14 @@ namespace osu.Game.Tests.Visual.Multiplayer Team2Score = { BindTarget = leaderboard.TeamScores[1] } }, Add); + LoadComponentAsync(gameplayScoreDisplay = new GameplayMatchScoreDisplay + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Team1Score = { BindTarget = leaderboard.TeamScores[0] }, + Team2Score = { BindTarget = leaderboard.TeamScores[1] } + }, Add); + Add(gameplayLeaderboard); }); }); @@ -100,7 +110,11 @@ namespace osu.Game.Tests.Visual.Multiplayer public void TestScoreUpdates() { AddRepeatStep("update state", () => SpectatorClient.RandomlyUpdateState(), 100); - AddToggleStep("switch compact mode", expanded => leaderboard.Expanded.Value = expanded); + AddToggleStep("switch compact mode", expanded => + { + leaderboard.Expanded.Value = expanded; + gameplayScoreDisplay.Expanded.Value = expanded; + }); } } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayMatchScoreDisplay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayMatchScoreDisplay.cs new file mode 100644 index 0000000000..20a88545c5 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayMatchScoreDisplay.cs @@ -0,0 +1,40 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Screens.Play.HUD; +using osuTK; + +namespace osu.Game.Screens.OnlinePlay.Multiplayer +{ + public class GameplayMatchScoreDisplay : MatchScoreDisplay + { + public Bindable Expanded = new Bindable(); + + protected override void LoadComplete() + { + base.LoadComplete(); + + Scale = new Vector2(0.5f); + + Expanded.BindValueChanged(expandedChanged, true); + } + + private void expandedChanged(ValueChangedEvent expanded) + { + if (expanded.NewValue) + { + Score1Text.FadeIn(500, Easing.OutQuint); + Score2Text.FadeIn(500, Easing.OutQuint); + this.ResizeWidthTo(2, 500, Easing.OutQuint); + } + else + { + Score1Text.FadeOut(500, Easing.OutQuint); + Score2Text.FadeOut(500, Easing.OutQuint); + this.ResizeWidthTo(1, 500, Easing.OutQuint); + } + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index 404fc21fc8..0ff7d70c11 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -3,9 +3,12 @@ using System; using System.Diagnostics; +using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Logging; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; @@ -37,6 +40,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private readonly int[] userIds; private LoadingLayer loadingDisplay; + private FillFlowContainer leaderboardFlow; /// /// Construct a multiplayer player. @@ -60,8 +64,32 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer if (!LoadedBeatmapSuccessfully) return; + HUDOverlay.Add(leaderboardFlow = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + }); + // todo: this should be implemented via a custom HUD implementation, and correctly masked to the main content area. - LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(ScoreProcessor, userIds), HUDOverlay.Add); + LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(ScoreProcessor, userIds), l => + { + if (!LoadedBeatmapSuccessfully) + return; + + ((IBindable)leaderboard.Expanded).BindTo(HUDOverlay.ShowHud); + + leaderboardFlow.Add(l); + + if (leaderboard.TeamScores.Count >= 2) + { + LoadComponentAsync(new GameplayMatchScoreDisplay + { + Team1Score = { BindTarget = leaderboard.TeamScores.First().Value }, + Team2Score = { BindTarget = leaderboard.TeamScores.Last().Value }, + Expanded = { BindTarget = HUDOverlay.ShowHud }, + }, leaderboardFlow.Add); + } + }); HUDOverlay.Add(loadingDisplay = new LoadingLayer(true) { Depth = float.MaxValue }); } @@ -98,16 +126,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer Debug.Assert(client.Room != null); } - protected override void LoadComplete() - { - base.LoadComplete(); - - if (!LoadedBeatmapSuccessfully) - return; - - ((IBindable)leaderboard.Expanded).BindTo(HUDOverlay.ShowHud); - } - protected override void StartGameplay() { // block base call, but let the server know we are ready to start. @@ -138,7 +156,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { const float padding = 44; // enough margin to avoid the hit error display. - leaderboard.Position = new Vector2(padding, padding + HUDOverlay.TopScoringElementsHeight); + leaderboardFlow.Position = new Vector2(padding, padding + HUDOverlay.TopScoringElementsHeight); } private void onMatchStarted() => Scheduler.Add(() => diff --git a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs index 3df4925972..d25ceb948e 100644 --- a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs +++ b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs @@ -21,8 +21,8 @@ namespace osu.Game.Screens.Play.HUD public BindableInt Team1Score = new BindableInt(); public BindableInt Team2Score = new BindableInt(); - private MatchScoreCounter score1Text; - private MatchScoreCounter score2Text; + protected MatchScoreCounter Score1Text; + protected MatchScoreCounter Score2Text; private Drawable score1Bar; private Drawable score2Bar; @@ -65,11 +65,6 @@ namespace osu.Game.Screens.Play.HUD Anchor = Anchor.TopCentre, Origin = Anchor.TopRight }, - score1Text = new MatchScoreCounter - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre - }, score2Bar = new Box { Name = "top bar blue", @@ -80,10 +75,25 @@ namespace osu.Game.Screens.Play.HUD Anchor = Anchor.TopCentre, Origin = Anchor.TopLeft }, - score2Text = new MatchScoreCounter + new Container { + RelativeSizeAxes = Axes.X, + Height = 50, Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre + Origin = Anchor.TopCentre, + Children = new Drawable[] + { + Score1Text = new MatchScoreCounter + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre + }, + Score2Text = new MatchScoreCounter + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre + }, + } }, }; } @@ -98,11 +108,11 @@ namespace osu.Game.Screens.Play.HUD private void updateScores() { - score1Text.Current.Value = Team1Score.Value; - score2Text.Current.Value = Team2Score.Value; + Score1Text.Current.Value = Team1Score.Value; + Score2Text.Current.Value = Team2Score.Value; - var winningText = Team1Score.Value > Team2Score.Value ? score1Text : score2Text; - var losingText = Team1Score.Value <= Team2Score.Value ? score1Text : score2Text; + var winningText = Team1Score.Value > Team2Score.Value ? Score1Text : Score2Text; + var losingText = Team1Score.Value <= Team2Score.Value ? Score1Text : Score2Text; winningText.Winning = true; losingText.Winning = false; @@ -119,11 +129,11 @@ namespace osu.Game.Screens.Play.HUD protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); - score1Text.X = -Math.Max(5 + score1Text.DrawWidth / 2, score1Bar.DrawWidth); - score2Text.X = Math.Max(5 + score2Text.DrawWidth / 2, score2Bar.DrawWidth); + Score1Text.X = -Math.Max(5 + Score1Text.DrawWidth / 2, score1Bar.DrawWidth); + Score2Text.X = Math.Max(5 + Score2Text.DrawWidth / 2, score2Bar.DrawWidth); } - private class MatchScoreCounter : ScoreCounter + protected class MatchScoreCounter : ScoreCounter { private OsuSpriteText displayedSpriteText; From 5f3d0871012f88bf1bfba1d7cb309ad69b372384 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Aug 2021 19:05:23 +0900 Subject: [PATCH 0936/2442] Also add team score display to multiplayer spectator screen --- .../TestSceneMultiSpectatorLeaderboard.cs | 4 ++ .../TestSceneMultiSpectatorScreen.cs | 43 ++++++++++++++++++ .../Spectate/MultiSpectatorScreen.cs | 44 +++++++++++++++---- 3 files changed, 83 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs index e14df62af1..22543b7b26 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs @@ -9,6 +9,7 @@ using osu.Framework.Timing; using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate; using osu.Game.Screens.Play.HUD; +using osu.Game.Users; namespace osu.Game.Tests.Visual.Multiplayer { @@ -31,7 +32,10 @@ namespace osu.Game.Tests.Visual.Multiplayer }; foreach (var (userId, _) in clocks) + { SpectatorClient.StartPlay(userId, 0); + OnlinePlayDependencies.Client.AddUser(new User { Id = userId }); + } }); AddStep("create leaderboard", () => diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index 072e32370d..e8ee9fe012 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -8,10 +8,12 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; using osu.Game.Rulesets.UI; using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate; using osu.Game.Screens.Play; using osu.Game.Tests.Beatmaps.IO; +using osu.Game.Users; namespace osu.Game.Tests.Visual.Multiplayer { @@ -49,6 +51,10 @@ namespace osu.Game.Tests.Visual.Multiplayer { Client.CurrentMatchPlayingUserIds.Add(PLAYER_1_ID); Client.CurrentMatchPlayingUserIds.Add(PLAYER_2_ID); + + OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_1_ID }); + OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_2_ID }); + playingUserIds.Add(PLAYER_1_ID); playingUserIds.Add(PLAYER_2_ID); }); @@ -76,6 +82,41 @@ namespace osu.Game.Tests.Visual.Multiplayer AddWaitStep("wait a bit", 20); } + [Test] + public void TestTeamDisplay() + { + AddStep("start players", () => + { + Client.CurrentMatchPlayingUserIds.Add(PLAYER_1_ID); + Client.CurrentMatchPlayingUserIds.Add(PLAYER_2_ID); + + var player1 = OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_1_ID }); + player1.MatchState = new TeamVersusUserState + { + TeamID = 0, + }; + + var player2 = OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_2_ID }); + player2.MatchState = new TeamVersusUserState + { + TeamID = 1, + }; + + SpectatorClient.StartPlay(PLAYER_1_ID, importedBeatmapId); + SpectatorClient.StartPlay(PLAYER_2_ID, importedBeatmapId); + + playingUserIds.Add(PLAYER_1_ID); + playingUserIds.Add(PLAYER_2_ID); + }); + + loadSpectateScreen(); + + sendFrames(PLAYER_1_ID, 1000); + sendFrames(PLAYER_2_ID, 1000); + + AddWaitStep("wait a bit", 20); + } + [Test] public void TestTimeDoesNotProgressWhileAllPlayersPaused() { @@ -265,6 +306,8 @@ namespace osu.Game.Tests.Visual.Multiplayer foreach (int id in userIds) { Client.CurrentMatchPlayingUserIds.Add(id); + OnlinePlayDependencies.Client.AddUser(new User { Id = id }); + SpectatorClient.StartPlay(id, beatmapId ?? importedBeatmapId); playingUserIds.Add(id); } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs index 56ed7a9564..9923c42583 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Online.Multiplayer; using osu.Game.Online.Spectator; using osu.Game.Screens.Play; +using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Spectate; namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate @@ -59,6 +60,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate private void load() { Container leaderboardContainer; + Container scoreDisplayContainer; + masterClockContainer = new MasterGameplayClockContainer(Beatmap.Value, 0); InternalChildren = new[] @@ -67,20 +70,36 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate masterClockContainer.WithChild(new GridContainer { RelativeSizeAxes = Axes.Both, - ColumnDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize) - }, + RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }, Content = new[] { new Drawable[] { - leaderboardContainer = new Container + scoreDisplayContainer = new Container { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y }, - grid = new PlayerGrid { RelativeSizeAxes = Axes.Both } + }, + new Drawable[] + { + new GridContainer + { + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }, + Content = new[] + { + new Drawable[] + { + leaderboardContainer = new Container + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X + }, + grid = new PlayerGrid { RelativeSizeAxes = Axes.Both } + } + } + } } } }) @@ -108,6 +127,15 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate leaderboard.AddClock(instance.UserId, instance.GameplayClock); leaderboardContainer.Add(leaderboard); + + if (leaderboard.TeamScores.Count >= 2) + { + LoadComponentAsync(new MatchScoreDisplay + { + Team1Score = { BindTarget = leaderboard.TeamScores.First().Value }, + Team2Score = { BindTarget = leaderboard.TeamScores.Last().Value }, + }, scoreDisplayContainer.Add); + } }); } From 551929cf5ad214fce24e99f6d76d185ae6ff8987 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Aug 2021 19:18:13 +0900 Subject: [PATCH 0937/2442] Simplify method of marking players as playing in test scenes --- .../TestSceneMultiSpectatorScreen.cs | 17 +++++------------ .../TestSceneMultiplayerGameplayLeaderboard.cs | 5 +---- ...tSceneMultiplayerGameplayLeaderboardTeams.cs | 5 +---- .../Online/Multiplayer/MultiplayerClient.cs | 14 ++++++++------ .../Play/HUD/MultiplayerGameplayLeaderboard.cs | 2 +- .../Visual/Multiplayer/TestMultiplayerClient.cs | 6 +++++- 6 files changed, 21 insertions(+), 28 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index e8ee9fe012..116349c71e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -49,11 +49,8 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("start players silently", () => { - Client.CurrentMatchPlayingUserIds.Add(PLAYER_1_ID); - Client.CurrentMatchPlayingUserIds.Add(PLAYER_2_ID); - - OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_1_ID }); - OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_2_ID }); + OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_1_ID }, true); + OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_2_ID }, true); playingUserIds.Add(PLAYER_1_ID); playingUserIds.Add(PLAYER_2_ID); @@ -87,16 +84,13 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("start players", () => { - Client.CurrentMatchPlayingUserIds.Add(PLAYER_1_ID); - Client.CurrentMatchPlayingUserIds.Add(PLAYER_2_ID); - - var player1 = OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_1_ID }); + var player1 = OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_1_ID }, true); player1.MatchState = new TeamVersusUserState { TeamID = 0, }; - var player2 = OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_2_ID }); + var player2 = OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_2_ID }, true); player2.MatchState = new TeamVersusUserState { TeamID = 1, @@ -305,8 +299,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { foreach (int id in userIds) { - Client.CurrentMatchPlayingUserIds.Add(id); - OnlinePlayDependencies.Client.AddUser(new User { Id = id }); + OnlinePlayDependencies.Client.AddUser(new User { Id = id }, true); SpectatorClient.StartPlay(id, beatmapId ?? importedBeatmapId); playingUserIds.Add(id); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs index 0aa47f0899..8121492a0b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs @@ -56,12 +56,9 @@ namespace osu.Game.Tests.Visual.Multiplayer foreach (var user in users) { SpectatorClient.StartPlay(user, Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0); - OnlinePlayDependencies.Client.AddUser(new User { Id = user }); + OnlinePlayDependencies.Client.AddUser(new User { Id = user }, true); } - // Todo: This is REALLY bad. - Client.CurrentMatchPlayingUserIds.AddRange(users); - Children = new Drawable[] { scoreProcessor = new OsuScoreProcessor(), diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs index 8eaa63a166..d363c6eed4 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs @@ -60,7 +60,7 @@ namespace osu.Game.Tests.Visual.Multiplayer foreach (var user in users) { SpectatorClient.StartPlay(user, Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0); - var roomUser = OnlinePlayDependencies.Client.AddUser(new User { Id = user }); + var roomUser = OnlinePlayDependencies.Client.AddUser(new User { Id = user }, true); roomUser.MatchState = new TeamVersusUserState { @@ -68,9 +68,6 @@ namespace osu.Game.Tests.Visual.Multiplayer }; } - // Todo: This is REALLY bad. - Client.CurrentMatchPlayingUserIds.AddRange(users); - Children = new Drawable[] { scoreProcessor = new OsuScoreProcessor(), diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index bffb2d341a..14beb38cde 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -62,7 +62,9 @@ namespace osu.Game.Online.Multiplayer /// /// The users in the joined which are participating in the current gameplay loop. /// - public readonly BindableList CurrentMatchPlayingUserIds = new BindableList(); + public IBindableList CurrentMatchPlayingUserIds => PlayingUserIds; + + protected readonly BindableList PlayingUserIds = new BindableList(); public readonly Bindable CurrentMatchPlayingItem = new Bindable(); @@ -179,7 +181,7 @@ namespace osu.Game.Online.Multiplayer { APIRoom = null; Room = null; - CurrentMatchPlayingUserIds.Clear(); + PlayingUserIds.Clear(); RoomUpdated?.Invoke(); }); @@ -376,7 +378,7 @@ namespace osu.Game.Online.Multiplayer return; Room.Users.Remove(user); - CurrentMatchPlayingUserIds.Remove(user.UserID); + PlayingUserIds.Remove(user.UserID); RoomUpdated?.Invoke(); }, false); @@ -659,16 +661,16 @@ namespace osu.Game.Online.Multiplayer /// The new state of the user. private void updateUserPlayingState(int userId, MultiplayerUserState state) { - bool wasPlaying = CurrentMatchPlayingUserIds.Contains(userId); + bool wasPlaying = PlayingUserIds.Contains(userId); bool isPlaying = state >= MultiplayerUserState.WaitingForLoad && state <= MultiplayerUserState.FinishedPlay; if (isPlaying == wasPlaying) return; if (isPlaying) - CurrentMatchPlayingUserIds.Add(userId); + PlayingUserIds.Add(userId); else - CurrentMatchPlayingUserIds.Remove(userId); + PlayingUserIds.Remove(userId); } private Task scheduleAsync(Action action, CancellationToken cancellationToken = default) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 2895d0cb5c..4968dc0706 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -41,7 +41,7 @@ namespace osu.Game.Screens.Play.HUD private UserLookupCache userLookupCache { get; set; } private readonly ScoreProcessor scoreProcessor; - private readonly BindableList playingUsers; + private readonly IBindableList playingUsers; private Bindable scoringMode; private bool hasTeams => TeamScores.Count > 0; diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index db491aac25..cffaea5c94 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -50,10 +50,14 @@ namespace osu.Game.Tests.Visual.Multiplayer public void Disconnect() => isConnected.Value = false; - public MultiplayerRoomUser AddUser(User user) + public MultiplayerRoomUser AddUser(User user, bool markAsPlaying = false) { var roomUser = new MultiplayerRoomUser(user.Id) { User = user }; ((IMultiplayerClient)this).UserJoined(roomUser); + + if (markAsPlaying) + PlayingUserIds.Add(user.Id); + return roomUser; } From ea6e441dec4201f10fe21bd71aea833f1cf5c9a3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Aug 2021 19:18:13 +0900 Subject: [PATCH 0938/2442] Simplify method of marking players as playing in test scenes --- .../Multiplayer/TestSceneMultiSpectatorScreen.cs | 9 ++++++--- .../TestSceneMultiplayerGameplayLeaderboard.cs | 7 ++++--- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 14 ++++++++------ .../Play/HUD/MultiplayerGameplayLeaderboard.cs | 2 +- .../Visual/Multiplayer/TestMultiplayerClient.cs | 11 ++++++++++- 5 files changed, 29 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index 072e32370d..e9fae32335 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -12,6 +12,7 @@ using osu.Game.Rulesets.UI; using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate; using osu.Game.Screens.Play; using osu.Game.Tests.Beatmaps.IO; +using osu.Game.Users; namespace osu.Game.Tests.Visual.Multiplayer { @@ -47,8 +48,9 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("start players silently", () => { - Client.CurrentMatchPlayingUserIds.Add(PLAYER_1_ID); - Client.CurrentMatchPlayingUserIds.Add(PLAYER_2_ID); + OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_1_ID }, true); + OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_2_ID }, true); + playingUserIds.Add(PLAYER_1_ID); playingUserIds.Add(PLAYER_2_ID); }); @@ -264,7 +266,8 @@ namespace osu.Game.Tests.Visual.Multiplayer { foreach (int id in userIds) { - Client.CurrentMatchPlayingUserIds.Add(id); + OnlinePlayDependencies.Client.AddUser(new User { Id = id }, true); + SpectatorClient.StartPlay(id, beatmapId ?? importedBeatmapId); playingUserIds.Add(id); } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs index 0e368b59dd..8121492a0b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs @@ -20,6 +20,7 @@ using osu.Game.Scoring; using osu.Game.Screens.Play.HUD; using osu.Game.Tests.Visual.OnlinePlay; using osu.Game.Tests.Visual.Spectator; +using osu.Game.Users; namespace osu.Game.Tests.Visual.Multiplayer { @@ -53,10 +54,10 @@ namespace osu.Game.Tests.Visual.Multiplayer var playable = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value); foreach (var user in users) + { SpectatorClient.StartPlay(user, Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0); - - // Todo: This is REALLY bad. - Client.CurrentMatchPlayingUserIds.AddRange(users); + OnlinePlayDependencies.Client.AddUser(new User { Id = user }, true); + } Children = new Drawable[] { diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index bffb2d341a..14beb38cde 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -62,7 +62,9 @@ namespace osu.Game.Online.Multiplayer /// /// The users in the joined which are participating in the current gameplay loop. /// - public readonly BindableList CurrentMatchPlayingUserIds = new BindableList(); + public IBindableList CurrentMatchPlayingUserIds => PlayingUserIds; + + protected readonly BindableList PlayingUserIds = new BindableList(); public readonly Bindable CurrentMatchPlayingItem = new Bindable(); @@ -179,7 +181,7 @@ namespace osu.Game.Online.Multiplayer { APIRoom = null; Room = null; - CurrentMatchPlayingUserIds.Clear(); + PlayingUserIds.Clear(); RoomUpdated?.Invoke(); }); @@ -376,7 +378,7 @@ namespace osu.Game.Online.Multiplayer return; Room.Users.Remove(user); - CurrentMatchPlayingUserIds.Remove(user.UserID); + PlayingUserIds.Remove(user.UserID); RoomUpdated?.Invoke(); }, false); @@ -659,16 +661,16 @@ namespace osu.Game.Online.Multiplayer /// The new state of the user. private void updateUserPlayingState(int userId, MultiplayerUserState state) { - bool wasPlaying = CurrentMatchPlayingUserIds.Contains(userId); + bool wasPlaying = PlayingUserIds.Contains(userId); bool isPlaying = state >= MultiplayerUserState.WaitingForLoad && state <= MultiplayerUserState.FinishedPlay; if (isPlaying == wasPlaying) return; if (isPlaying) - CurrentMatchPlayingUserIds.Add(userId); + PlayingUserIds.Add(userId); else - CurrentMatchPlayingUserIds.Remove(userId); + PlayingUserIds.Remove(userId); } private Task scheduleAsync(Action action, CancellationToken cancellationToken = default) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index a10c16fcd5..7ee77759b0 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -31,7 +31,7 @@ namespace osu.Game.Screens.Play.HUD private UserLookupCache userLookupCache { get; set; } private readonly ScoreProcessor scoreProcessor; - private readonly BindableList playingUsers; + private readonly IBindableList playingUsers; private Bindable scoringMode; /// diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 43aadf5acb..cffaea5c94 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -50,7 +50,16 @@ namespace osu.Game.Tests.Visual.Multiplayer public void Disconnect() => isConnected.Value = false; - public void AddUser(User user) => ((IMultiplayerClient)this).UserJoined(new MultiplayerRoomUser(user.Id) { User = user }); + public MultiplayerRoomUser AddUser(User user, bool markAsPlaying = false) + { + var roomUser = new MultiplayerRoomUser(user.Id) { User = user }; + ((IMultiplayerClient)this).UserJoined(roomUser); + + if (markAsPlaying) + PlayingUserIds.Add(user.Id); + + return roomUser; + } public void AddNullUser(int userId) => ((IMultiplayerClient)this).UserJoined(new MultiplayerRoomUser(userId)); From 490f9e18486ba5abd03c2300b802f0c193b14df4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Aug 2021 19:45:16 +0900 Subject: [PATCH 0939/2442] Fix overlap in spectator view --- osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs index d25ceb948e..837d8bfde7 100644 --- a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs +++ b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs @@ -17,6 +17,7 @@ namespace osu.Game.Screens.Play.HUD public class MatchScoreDisplay : CompositeDrawable { private const float bar_height = 18; + private const float font_size = 50; public BindableInt Team1Score = new BindableInt(); public BindableInt Team2Score = new BindableInt(); @@ -78,7 +79,7 @@ namespace osu.Game.Screens.Play.HUD new Container { RelativeSizeAxes = Axes.X, - Height = 50, + Height = font_size + bar_height, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Children = new Drawable[] @@ -156,8 +157,8 @@ namespace osu.Game.Screens.Play.HUD private void updateFont(bool winning) => displayedSpriteText.Font = winning - ? OsuFont.Torus.With(weight: FontWeight.Bold, size: 50, fixedWidth: true) - : OsuFont.Torus.With(weight: FontWeight.Regular, size: 40, fixedWidth: true); + ? OsuFont.Torus.With(weight: FontWeight.Bold, size: font_size, fixedWidth: true) + : OsuFont.Torus.With(weight: FontWeight.Regular, size: font_size * 0.8f, fixedWidth: true); } } } From 58714dbe719ee3b5544166e3791d962046fa21b8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Aug 2021 19:48:53 +0900 Subject: [PATCH 0940/2442] Fix ordering of teams to match colours --- osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 4968dc0706..013d266ac2 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -26,7 +26,7 @@ namespace osu.Game.Screens.Play.HUD { protected readonly Dictionary UserScores = new Dictionary(); - public readonly Dictionary TeamScores = new Dictionary(); + public readonly SortedDictionary TeamScores = new SortedDictionary(); [Resolved] private OsuColour colours { get; set; } From 76e5a40b8ed15dfba6bf5812743a5bbf1f15a542 Mon Sep 17 00:00:00 2001 From: TheOmyNomy Date: Mon, 9 Aug 2021 20:53:02 +1000 Subject: [PATCH 0941/2442] Remove unnecessary "in" keyword --- osu.Game/Utils/LegacyUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Utils/LegacyUtils.cs b/osu.Game/Utils/LegacyUtils.cs index 9351125acd..64306adf50 100644 --- a/osu.Game/Utils/LegacyUtils.cs +++ b/osu.Game/Utils/LegacyUtils.cs @@ -42,7 +42,7 @@ namespace osu.Game.Utils /// /// Interpolates between two sRGB s directly in sRGB space. /// - public static Colour4 InterpolateNonLinear(double time, Colour4 startColour, Colour4 endColour, double startTime, double endTime, in TEasing easing) where TEasing : IEasingFunction + public static Colour4 InterpolateNonLinear(double time, Colour4 startColour, Colour4 endColour, double startTime, double endTime, TEasing easing) where TEasing : IEasingFunction { if (startColour == endColour) return startColour; From c5b490c4414d64a9dc80353850cad62d707aa85d Mon Sep 17 00:00:00 2001 From: TheOmyNomy Date: Tue, 10 Aug 2021 11:29:31 +1000 Subject: [PATCH 0942/2442] Use non linear colour interpolation for legacy health display --- osu.Game/Skinning/LegacyHealthDisplay.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/LegacyHealthDisplay.cs b/osu.Game/Skinning/LegacyHealthDisplay.cs index 67280e4acd..b1cd1f86c0 100644 --- a/osu.Game/Skinning/LegacyHealthDisplay.cs +++ b/osu.Game/Skinning/LegacyHealthDisplay.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Utils; using osu.Game.Rulesets.Judgements; using osu.Game.Screens.Play.HUD; +using osu.Game.Utils; using osuTK; using osuTK.Graphics; @@ -83,10 +84,10 @@ namespace osu.Game.Skinning private static Color4 getFillColour(double hp) { if (hp < 0.2) - return Interpolation.ValueAt(0.2 - hp, Color4.Black, Color4.Red, 0, 0.2); + return LegacyUtils.InterpolateNonLinear(0.2 - hp, Color4.Black, Color4.Red, 0, 0.2); if (hp < epic_cutoff) - return Interpolation.ValueAt(0.5 - hp, Color4.White, Color4.Black, 0, 0.5); + return LegacyUtils.InterpolateNonLinear(0.5 - hp, Color4.White, Color4.Black, 0, 0.5); return Color4.White; } From 0b41731d0bb5ffef7219d3d7e64011e3fd26dc62 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 10 Aug 2021 11:43:11 +0700 Subject: [PATCH 0943/2442] initial changelog supporter promo section --- .../Changelog/ChangelogSingleBuild.cs | 1 + .../Changelog/ChangelogSupporterPromo.cs | 46 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs diff --git a/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs b/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs index 8b89d63aab..25d8eaf0db 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs @@ -71,6 +71,7 @@ namespace osu.Game.Overlays.Changelog Colour = colourProvider.Background6, Margin = new MarginPadding { Top = 30 }, }, + new ChangelogSupporterPromo(), comments = new CommentsContainer() }; diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs new file mode 100644 index 0000000000..98b3cc2002 --- /dev/null +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -0,0 +1,46 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Changelog +{ + public class ChangelogSupporterPromo : CompositeDrawable + { + public ChangelogSupporterPromo() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Padding = new MarginPadding + { + Vertical = 20, + Horizontal = 50, + }; + InternalChildren = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0.3f), + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + Height = 200, + }, + } + }, + }; + } + } +} From bdf6c2a5edf61d89dcb095acd86672c1367e07e2 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 10 Aug 2021 12:00:26 +0700 Subject: [PATCH 0944/2442] add changelog supporter promo test scene --- .../TestSceneChangelogSupporterPromo.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneChangelogSupporterPromo.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogSupporterPromo.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogSupporterPromo.cs new file mode 100644 index 0000000000..22220a7d9c --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogSupporterPromo.cs @@ -0,0 +1,35 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Overlays; +using osu.Game.Overlays.Changelog; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneChangelogSupporterPromo : OsuTestScene + { + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + + public TestSceneChangelogSupporterPromo() + { + Child = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background4, + }, + new ChangelogSupporterPromo(), + } + }; + } + } +} From a2b4f05ebe311527ec77351c293f5a111e5535a7 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 10 Aug 2021 12:11:21 +0700 Subject: [PATCH 0945/2442] add border radius and shadow --- .../Overlays/Changelog/ChangelogSupporterPromo.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index 98b3cc2002..0775ba4494 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -4,7 +4,9 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; +using osuTK; using osuTK.Graphics; namespace osu.Game.Overlays.Changelog @@ -26,6 +28,15 @@ namespace osu.Game.Overlays.Changelog { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, + Masking = true, + CornerRadius = 6, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.25f), + Offset = new Vector2(0, 1), + Radius = 3, + }, Children = new Drawable[] { new Box From f4ceb170642f802a2420b7e39d9b6887292ad270 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Tue, 10 Aug 2021 16:06:20 +1000 Subject: [PATCH 0946/2442] Cleanup of code --- .../Difficulty/OsuPerformanceCalculator.cs | 3 ++- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 3634374f50..267e332372 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -192,7 +192,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty { double flashlightValue = 0.0; - if (mods.Any(h => h is OsuModFlashlight)) { + if (mods.Any(h => h is OsuModFlashlight)) + { flashlightValue = Math.Pow(Attributes.FlashlightStrain, 2.0) * 25.0; // Add an additional bonus for HDFL. diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index fd771ab768..b8a96b3310 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -40,19 +40,20 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { double cumulativeStrainTime = 0.0; - for (int i = 0; i < Previous.Count; i++) { + for (int i = 0; i < Previous.Count; i++) + { var osuPrevious = (OsuDifficultyHitObject)Previous[i]; var osuPreviousHitObject = (OsuHitObject)(osuPrevious.BaseObject); - if (!(osuPrevious.BaseObject is Spinner)) { + if (!(osuPrevious.BaseObject is Spinner)) + { double jumpDistance = (osuHitObject.StackedPosition - osuPreviousHitObject.EndPosition).Length; cumulativeStrainTime += osuPrevious.StrainTime; // We want to nerf objects that can be easily seen within the Flashlight circle radius. - if (i == 0 && jumpDistance < 50.0) { + if (i == 0 && jumpDistance < 50.0) smallDistNerf = jumpDistance / 50.0; - } result += Math.Pow(0.8, i) * scalingFactor * jumpDistance / cumulativeStrainTime; } From 3aa72163f234ab22dff7a350b1aeba9cafdb76c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Aug 2021 15:14:43 +0900 Subject: [PATCH 0947/2442] Add simple download progress display to download buttons on playlist items --- osu.Game/Graphics/UserInterface/GrayButton.cs | 4 ++-- .../BeatmapListing/Panels/BeatmapPanelDownloadButton.cs | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/GrayButton.cs b/osu.Game/Graphics/UserInterface/GrayButton.cs index 88c46f29e0..0a2c83d5a8 100644 --- a/osu.Game/Graphics/UserInterface/GrayButton.cs +++ b/osu.Game/Graphics/UserInterface/GrayButton.cs @@ -27,7 +27,7 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load() { - Children = new Drawable[] + AddRange(new Drawable[] { Background = new Box { @@ -42,7 +42,7 @@ namespace osu.Game.Graphics.UserInterface Size = new Vector2(13), Icon = icon, }, - }; + }); } } } diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs index cec1a5ac12..47b477ef9a 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs @@ -37,6 +37,13 @@ namespace osu.Game.Overlays.BeatmapListing.Panels RelativeSizeAxes = Axes.Both, }, }; + + button.Add(new DownloadProgressBar(beatmapSet) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Depth = -1, + }); } protected override void LoadComplete() From 7c8df571090813a641c86b0ca215b234a6b9acf6 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 10 Aug 2021 13:21:23 +0700 Subject: [PATCH 0948/2442] add description text --- .../Changelog/ChangelogSupporterPromo.cs | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index 0775ba4494..ba5cac731b 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -1,11 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; using osuTK; using osuTK.Graphics; @@ -13,6 +19,9 @@ namespace osu.Game.Overlays.Changelog { public class ChangelogSupporterPromo : CompositeDrawable { + private readonly LinkFlowContainer supportLinkText; + private readonly TextFlowContainer supportNoteText; + public ChangelogSupporterPromo() { RelativeSizeAxes = Axes.X; @@ -22,6 +31,7 @@ namespace osu.Game.Overlays.Changelog Vertical = 20, Horizontal = 50, }; + InternalChildren = new Drawable[] { new Container @@ -48,10 +58,67 @@ namespace osu.Game.Overlays.Changelog { RelativeSizeAxes = Axes.X, Height = 200, + Padding = new MarginPadding { Horizontal = 75 }, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = ChangelogStrings.SupportHeading, + Font = OsuFont.GetFont(size: 22, weight: FontWeight.Light), + Margin = new MarginPadding { Bottom = 20 }, + }, + supportLinkText = new LinkFlowContainer(t => + { + t.Font = t.Font.With(size: 17.5f); + }) + { + AutoSizeAxes = Axes.Both, + }, + supportNoteText = new TextFlowContainer(t => + { + t.Font = t.Font.With(size: 15); + }) + { + Margin = new MarginPadding { Top = 10 }, + AutoSizeAxes = Axes.Both, + } + }, + }, + } }, } }, }; } + + [BackgroundDependencyLoader] + private void load(OsuColour colour) + { + void fontPinkColour(SpriteText t) => t.Colour = colour.PinkLighter; + + supportLinkText.AddText("Support further development of osu! and ", fontPinkColour); + supportLinkText.AddLink("become an osu!supporter", "https://osu.ppy.sh/home/support", t => + { + t.Colour = colour.PinkDark; + t.Font = t.Font.With(weight: FontWeight.Bold); + }); + supportLinkText.AddText(" today!", fontPinkColour); + + supportNoteText.AddText(new OsuSpriteText + { + Text = ChangelogStrings.SupportText2, + Colour = colour.PinkLighter, + }); + } } } From 49de8ce1df4254c30d8d0de05c2298c10523f56a Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 10 Aug 2021 13:53:06 +0700 Subject: [PATCH 0949/2442] fix up some layouting --- .../Changelog/ChangelogSupporterPromo.cs | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index ba5cac731b..b90e158f58 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -19,6 +19,8 @@ namespace osu.Game.Overlays.Changelog { public class ChangelogSupporterPromo : CompositeDrawable { + private const float image_width = 164; + private readonly LinkFlowContainer supportLinkText; private readonly TextFlowContainer supportNoteText; @@ -54,12 +56,11 @@ namespace osu.Game.Overlays.Changelog RelativeSizeAxes = Axes.Both, Colour = Color4.Black.Opacity(0.3f), }, - new FillFlowContainer + new Container { RelativeSizeAxes = Axes.X, Height = 200, Padding = new MarginPadding { Horizontal = 75 }, - Direction = FillDirection.Horizontal, Children = new Drawable[] { new FillFlowContainer @@ -69,6 +70,7 @@ namespace osu.Game.Overlays.Changelog Direction = FillDirection.Vertical, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, + Padding = new MarginPadding { Right = 50 + image_width }, Children = new Drawable[] { new OsuSpriteText @@ -82,7 +84,8 @@ namespace osu.Game.Overlays.Changelog t.Font = t.Font.With(size: 17.5f); }) { - AutoSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, }, supportNoteText = new TextFlowContainer(t => { @@ -90,10 +93,18 @@ namespace osu.Game.Overlays.Changelog }) { Margin = new MarginPadding { Top = 10 }, - AutoSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, } }, }, + new Container + { + RelativeSizeAxes = Axes.Y, + Width = image_width, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + } } }, } @@ -114,11 +125,7 @@ namespace osu.Game.Overlays.Changelog }); supportLinkText.AddText(" today!", fontPinkColour); - supportNoteText.AddText(new OsuSpriteText - { - Text = ChangelogStrings.SupportText2, - Colour = colour.PinkLighter, - }); + supportNoteText.AddText("Not only will you help speed development, but you will also get some extra features and customisations!", fontPinkColour); } } } From b3fbf52571a0ded416210174d901d61bdeb0a218 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 10 Aug 2021 14:07:20 +0700 Subject: [PATCH 0950/2442] add pippi and heart texture --- .../Changelog/ChangelogSupporterPromo.cs | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index b90e158f58..03373c4810 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -19,10 +20,11 @@ namespace osu.Game.Overlays.Changelog { public class ChangelogSupporterPromo : CompositeDrawable { - private const float image_width = 164; + private const float image_container_width = 164; private readonly LinkFlowContainer supportLinkText; private readonly TextFlowContainer supportNoteText; + private readonly Container imageContainer; public ChangelogSupporterPromo() { @@ -70,7 +72,7 @@ namespace osu.Game.Overlays.Changelog Direction = FillDirection.Vertical, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Padding = new MarginPadding { Right = 50 + image_width }, + Padding = new MarginPadding { Right = 50 + image_container_width }, Children = new Drawable[] { new OsuSpriteText @@ -98,10 +100,10 @@ namespace osu.Game.Overlays.Changelog } }, }, - new Container + imageContainer = new Container { RelativeSizeAxes = Axes.Y, - Width = image_width, + Width = image_container_width, Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, } @@ -113,7 +115,7 @@ namespace osu.Game.Overlays.Changelog } [BackgroundDependencyLoader] - private void load(OsuColour colour) + private void load(OsuColour colour, TextureStore textures) { void fontPinkColour(SpriteText t) => t.Colour = colour.PinkLighter; @@ -126,6 +128,27 @@ namespace osu.Game.Overlays.Changelog supportLinkText.AddText(" today!", fontPinkColour); supportNoteText.AddText("Not only will you help speed development, but you will also get some extra features and customisations!", fontPinkColour); + + imageContainer.Children = new Drawable[] + { + new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fill, + Texture = textures.Get(@"Online/supporter-pippi"), + }, + new Sprite + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Width = 75, + Height = 75, + Margin = new MarginPadding { Top = 70 }, + Texture = textures.Get(@"Online/supporter-heart"), + }, + }; } } } From f7a02219b80e528c90ca49283d6647599a74f81b Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 10 Aug 2021 14:23:15 +0700 Subject: [PATCH 0951/2442] fix font size description --- osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index 03373c4810..f5638293bb 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -78,12 +78,12 @@ namespace osu.Game.Overlays.Changelog new OsuSpriteText { Text = ChangelogStrings.SupportHeading, - Font = OsuFont.GetFont(size: 22, weight: FontWeight.Light), + Font = OsuFont.GetFont(size: 20, weight: FontWeight.Light), Margin = new MarginPadding { Bottom = 20 }, }, supportLinkText = new LinkFlowContainer(t => { - t.Font = t.Font.With(size: 17.5f); + t.Font = t.Font.With(size: 14); }) { RelativeSizeAxes = Axes.X, @@ -91,7 +91,7 @@ namespace osu.Game.Overlays.Changelog }, supportNoteText = new TextFlowContainer(t => { - t.Font = t.Font.With(size: 15); + t.Font = t.Font.With(size: 12); }) { Margin = new MarginPadding { Top = 10 }, From 5ea7909eeae16e20c265c95889f459cd0b7e64ae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Aug 2021 16:18:57 +0900 Subject: [PATCH 0952/2442] Fix incorrectly failing test --- .../Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index dfb78a235b..5e30cb86cc 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -15,6 +15,7 @@ using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics.Containers; using osu.Game.Online.Rooms; using osu.Game.Overlays; +using osu.Game.Overlays.BeatmapListing.Panels; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; @@ -215,7 +216,7 @@ namespace osu.Game.Tests.Visual.Multiplayer assertDownloadButtonVisible(false); void assertDownloadButtonVisible(bool visible) => AddUntilStep($"download button {(visible ? "shown" : "hidden")}", - () => playlist.ChildrenOfType().Single().Alpha == (visible ? 1 : 0)); + () => playlist.ChildrenOfType().Single().Alpha == (visible ? 1 : 0)); } [Test] @@ -229,7 +230,7 @@ namespace osu.Game.Tests.Visual.Multiplayer createPlaylist(byOnlineId, byChecksum); - AddAssert("download buttons shown", () => playlist.ChildrenOfType().All(d => d.IsPresent)); + AddAssert("download buttons shown", () => playlist.ChildrenOfType().All(d => d.IsPresent)); } [Test] From c63dfa21e130ebf85bf6f860a5fbb427f0eb2f26 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 10 Aug 2021 16:34:38 +0900 Subject: [PATCH 0953/2442] Always initialize DHO transforms on LoadComplete With the previous commit, the transform application is skipped when the state is already changed. But it turns out the previous commit breaks slider animation in the standard editor. This is probably due to the transforms are applied before nested hit objects are added. --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index ff6168ee37..29d8a475ef 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -190,9 +190,8 @@ namespace osu.Game.Rulesets.Objects.Drawables comboIndexBindable.BindValueChanged(_ => UpdateComboColour()); comboIndexWithOffsetsBindable.BindValueChanged(_ => UpdateComboColour(), true); - // If the state is changed, transforms are already initialized. - if (state.Value == ArmedState.Idle) - updateState(ArmedState.Idle, true); + // Apply transforms + updateState(State.Value, true); } /// From 2b9168157db3e99b2ee759cd71d5114c9642faa2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Aug 2021 16:50:39 +0900 Subject: [PATCH 0954/2442] Fix `CurrentMatchPlayingItem` not being reset on leaving a multiplayer room --- .../NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs | 4 ++++ osu.Game/Online/Multiplayer/MultiplayerClient.cs | 1 + 2 files changed, 5 insertions(+) diff --git a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs index 0983b806e2..07ec86b0e7 100644 --- a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs +++ b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs @@ -24,6 +24,8 @@ namespace osu.Game.Tests.NonVisual.Multiplayer AddRepeatStep("add some users", () => Client.AddUser(new User { Id = id++ }), 5); checkPlayingUserCount(0); + AddAssert("playlist item is available", () => Client.CurrentMatchPlayingItem.Value != null); + changeState(3, MultiplayerUserState.WaitingForLoad); checkPlayingUserCount(3); @@ -41,6 +43,8 @@ namespace osu.Game.Tests.NonVisual.Multiplayer AddStep("leave room", () => Client.LeaveRoom()); checkPlayingUserCount(0); + + AddAssert("playlist item is null", () => Client.CurrentMatchPlayingItem.Value == null); } [Test] diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 14beb38cde..dafc737ba2 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -181,6 +181,7 @@ namespace osu.Game.Online.Multiplayer { APIRoom = null; Room = null; + CurrentMatchPlayingItem.Value = null; PlayingUserIds.Clear(); RoomUpdated?.Invoke(); From 3fb2ca4f4a96acea47b9c17b2713afbec0d576fa Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 10 Aug 2021 14:55:57 +0700 Subject: [PATCH 0955/2442] add border bottom --- osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs b/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs index 25d8eaf0db..1f1a4a43de 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs @@ -72,6 +72,12 @@ namespace osu.Game.Overlays.Changelog Margin = new MarginPadding { Top = 30 }, }, new ChangelogSupporterPromo(), + new Box + { + RelativeSizeAxes = Axes.X, + Height = 2, + Colour = colourProvider.Background6, + }, comments = new CommentsContainer() }; From 93408c636bbdd7b8bb5c92b3731c919d0687d620 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 10 Aug 2021 14:56:54 +0700 Subject: [PATCH 0956/2442] hide supporter promo section for supporter --- osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs b/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs index 1f1a4a43de..93486274fc 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSingleBuild.cs @@ -71,12 +71,16 @@ namespace osu.Game.Overlays.Changelog Colour = colourProvider.Background6, Margin = new MarginPadding { Top = 30 }, }, - new ChangelogSupporterPromo(), + new ChangelogSupporterPromo + { + Alpha = api.LocalUser.Value.IsSupporter ? 0 : 1, + }, new Box { RelativeSizeAxes = Axes.X, Height = 2, Colour = colourProvider.Background6, + Alpha = api.LocalUser.Value.IsSupporter ? 0 : 1, }, comments = new CommentsContainer() }; From 595763ae3453d1fbb979a299798809cf672e801c Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 10 Aug 2021 14:58:11 +0700 Subject: [PATCH 0957/2442] add test case for supporter and non supporter --- osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs index 8818ac75b1..159eb9912c 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using Humanizer; using NUnit.Framework; +using osu.Framework.Testing; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; @@ -95,9 +96,11 @@ namespace osu.Game.Tests.Visual.Online AddAssert(@"no stream selected", () => changelog.Header.Streams.Current.Value == null); } - [Test] - public void ShowWithBuild() + [TestCase(false)] + [TestCase(true)] + public void ShowWithBuild(bool isSupporter) { + AddStep(@"set supporter", () => dummyAPI.LocalUser.Value.IsSupporter = isSupporter); showBuild(() => new APIChangelogBuild { Version = "2018.712.0", @@ -155,6 +158,7 @@ namespace osu.Game.Tests.Visual.Online AddUntilStep(@"wait for streams", () => changelog.Streams?.Count > 0); AddAssert(@"correct build displayed", () => changelog.Current.Value.Version == "2018.712.0"); AddAssert(@"correct stream selected", () => changelog.Header.Streams.Current.Value.Id == 5); + AddAssert(@"supporter promo showed", () => changelog.ChildrenOfType().First().Alpha == (isSupporter ? 0 : 1)); } [Test] From e0e0d5deb0f7debb5ffce87075a847304bdf759c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 10 Aug 2021 17:25:10 +0900 Subject: [PATCH 0958/2442] Remove unused using --- .../Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index 5e30cb86cc..93bdbb79f4 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -14,7 +14,6 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics.Containers; using osu.Game.Online.Rooms; -using osu.Game.Overlays; using osu.Game.Overlays.BeatmapListing.Panels; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; From d9b5f235d88a28ba1577a94f479016b1ef81d397 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Aug 2021 17:36:58 +0900 Subject: [PATCH 0959/2442] Add xmldoc explaining thread safety limitations of `IModelManager` "events" --- osu.Game/Database/IModelManager.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Database/IModelManager.cs b/osu.Game/Database/IModelManager.cs index 7f7e5565f1..8c314f1617 100644 --- a/osu.Game/Database/IModelManager.cs +++ b/osu.Game/Database/IModelManager.cs @@ -13,8 +13,16 @@ namespace osu.Game.Database public interface IModelManager where TModel : class { + /// + /// A bindable which contains a weak reference to the last item that was updated. + /// This is not thread-safe and should be scheduled locally if consumed from a drawable component. + /// IBindable> ItemUpdated { get; } + /// + /// A bindable which contains a weak reference to the last item that was removed. + /// This is not thread-safe and should be scheduled locally if consumed from a drawable component. + /// IBindable> ItemRemoved { get; } } } From b121d95400727bedb1c6d929d8ed5a741e904399 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Aug 2021 17:37:24 +0900 Subject: [PATCH 0960/2442] Avoid potential null reference exception in `OnlinePlayBeatmapAvailabilityTracker` --- osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs index 72ea84d4a8..86879ba245 100644 --- a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs @@ -59,8 +59,8 @@ namespace osu.Game.Online.Rooms protected override bool VerifyDatabasedModel(BeatmapSetInfo databasedSet) { - int? beatmapId = SelectedItem.Value.Beatmap.Value.OnlineBeatmapID; - string checksum = SelectedItem.Value.Beatmap.Value.MD5Hash; + int? beatmapId = SelectedItem.Value?.Beatmap.Value.OnlineBeatmapID; + string checksum = SelectedItem.Value?.Beatmap.Value.MD5Hash; var matchingBeatmap = databasedSet.Beatmaps.FirstOrDefault(b => b.OnlineBeatmapID == beatmapId && b.MD5Hash == checksum); From f86ef54e93dc1ea0e346554bfc5ddbec8f2312f0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 10 Aug 2021 17:36:40 +0900 Subject: [PATCH 0961/2442] Fix incorrect legacy slider body alpha --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs index 92941665e0..1c8dfeac52 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy // Roughly matches osu!stable's slider border portions. => base.CalculatedBorderPortion * 0.77f; - public new Color4 AccentColour => new Color4(base.AccentColour.R, base.AccentColour.G, base.AccentColour.B, base.AccentColour.A * 0.70f); + public new Color4 AccentColour => new Color4(base.AccentColour.R, base.AccentColour.G, base.AccentColour.B, 0.7f); protected override Color4 ColourAt(float position) { From 53d03745e046327870484b4ea38122aef357c68e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Aug 2021 17:48:47 +0900 Subject: [PATCH 0962/2442] Remove unused test scene --- .../Multiplayer/TestMultiplayerGameplay.cs | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Multiplayer/TestMultiplayerGameplay.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestMultiplayerGameplay.cs b/osu.Game.Tests/Visual/Multiplayer/TestMultiplayerGameplay.cs deleted file mode 100644 index 4b75121575..0000000000 --- a/osu.Game.Tests/Visual/Multiplayer/TestMultiplayerGameplay.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Linq; -using NUnit.Framework; -using osu.Game.Screens.OnlinePlay.Multiplayer; - -namespace osu.Game.Tests.Visual.Multiplayer -{ - public class TestMultiplayerGameplay : MultiplayerTestScene - { - [Test] - public void TestBasic() - { - AddStep("load screen", () => - LoadScreen(new MultiplayerPlayer(Client.CurrentMatchPlayingItem.Value, Client.Room?.Users.Select(u => u.UserID).ToArray()))); - } - } -} From a503274e1d79e40bea8a85e9c4e8f45529822b61 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Aug 2021 18:39:20 +0900 Subject: [PATCH 0963/2442] Pass through `MultiplayerRoomUser`s instead of `int`s to avoid re-retrieval --- .../TestSceneMultiSpectatorLeaderboard.cs | 3 +- .../TestSceneMultiSpectatorScreen.cs | 17 +++++----- ...TestSceneMultiplayerGameplayLeaderboard.cs | 2 +- ...ceneMultiplayerGameplayLeaderboardTeams.cs | 2 +- .../Multiplayer/MultiplayerMatchSubScreen.cs | 6 ++-- .../Multiplayer/MultiplayerPlayer.cs | 10 +++--- .../Spectate/MultiSpectatorLeaderboard.cs | 4 +-- .../Spectate/MultiSpectatorScreen.cs | 19 +++++++---- .../HUD/MultiplayerGameplayLeaderboard.cs | 33 +++++++++---------- osu.Game/Screens/Spectate/SpectatorScreen.cs | 14 ++++---- 10 files changed, 59 insertions(+), 51 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs index 22543b7b26..ade24b8740 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs @@ -6,6 +6,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Testing; using osu.Framework.Timing; +using osu.Game.Online.Multiplayer; using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate; using osu.Game.Screens.Play.HUD; @@ -45,7 +46,7 @@ namespace osu.Game.Tests.Visual.Multiplayer var scoreProcessor = new OsuScoreProcessor(); scoreProcessor.ApplyBeatmap(playable); - LoadComponentAsync(leaderboard = new MultiSpectatorLeaderboard(scoreProcessor, clocks.Keys.ToArray()) { Expanded = { Value = true } }, Add); + LoadComponentAsync(leaderboard = new MultiSpectatorLeaderboard(scoreProcessor, clocks.Keys.Select(id => new MultiplayerRoomUser(id)).ToArray()) { Expanded = { Value = true } }, Add); }); AddUntilStep("wait for load", () => leaderboard.IsLoaded); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index 116349c71e..3118a23fd5 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -8,6 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; using osu.Game.Rulesets.UI; using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate; @@ -27,7 +28,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private MultiSpectatorScreen spectatorScreen; - private readonly List playingUserIds = new List(); + private readonly List playingUsers = new List(); private BeatmapSetInfo importedSet; private BeatmapInfo importedBeatmap; @@ -42,7 +43,7 @@ namespace osu.Game.Tests.Visual.Multiplayer } [SetUp] - public new void Setup() => Schedule(() => playingUserIds.Clear()); + public new void Setup() => Schedule(() => playingUsers.Clear()); [Test] public void TestDelayedStart() @@ -52,8 +53,8 @@ namespace osu.Game.Tests.Visual.Multiplayer OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_1_ID }, true); OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_2_ID }, true); - playingUserIds.Add(PLAYER_1_ID); - playingUserIds.Add(PLAYER_2_ID); + playingUsers.Add(new MultiplayerRoomUser(PLAYER_1_ID)); + playingUsers.Add(new MultiplayerRoomUser(PLAYER_2_ID)); }); loadSpectateScreen(false); @@ -99,8 +100,8 @@ namespace osu.Game.Tests.Visual.Multiplayer SpectatorClient.StartPlay(PLAYER_1_ID, importedBeatmapId); SpectatorClient.StartPlay(PLAYER_2_ID, importedBeatmapId); - playingUserIds.Add(PLAYER_1_ID); - playingUserIds.Add(PLAYER_2_ID); + playingUsers.Add(new MultiplayerRoomUser(PLAYER_1_ID)); + playingUsers.Add(new MultiplayerRoomUser(PLAYER_2_ID)); }); loadSpectateScreen(); @@ -287,7 +288,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Beatmap.Value = beatmapManager.GetWorkingBeatmap(importedBeatmap); Ruleset.Value = importedBeatmap.Ruleset; - LoadScreen(spectatorScreen = new MultiSpectatorScreen(playingUserIds.ToArray())); + LoadScreen(spectatorScreen = new MultiSpectatorScreen(playingUsers.ToArray())); }); AddUntilStep("wait for screen load", () => spectatorScreen.LoadState == LoadState.Loaded && (!waitForPlayerLoad || spectatorScreen.AllPlayersLoaded)); @@ -302,7 +303,7 @@ namespace osu.Game.Tests.Visual.Multiplayer OnlinePlayDependencies.Client.AddUser(new User { Id = id }, true); SpectatorClient.StartPlay(id, beatmapId ?? importedBeatmapId); - playingUserIds.Add(id); + playingUsers.Add(new MultiplayerRoomUser(id)); } }); } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs index 8121492a0b..0997de478b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs @@ -66,7 +66,7 @@ namespace osu.Game.Tests.Visual.Multiplayer scoreProcessor.ApplyBeatmap(playable); - LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(scoreProcessor, users.ToArray()) + LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(scoreProcessor, Client.Room?.Users.ToArray()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs index d363c6eed4..274c9ea4b2 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs @@ -75,7 +75,7 @@ namespace osu.Game.Tests.Visual.Multiplayer scoreProcessor.ApplyBeatmap(playable); - LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(scoreProcessor, users.ToArray()) + LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(scoreProcessor, Client.Room?.Users.ToArray()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 561fa220c8..9fa19aaf21 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -475,16 +475,18 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override Screen CreateGameplayScreen() { Debug.Assert(client.LocalUser != null); + Debug.Assert(client.Room != null); int[] userIds = client.CurrentMatchPlayingUserIds.ToArray(); + MultiplayerRoomUser[] users = userIds.Select(id => client.Room.Users.First(u => u.UserID == id)).ToArray(); switch (client.LocalUser.State) { case MultiplayerUserState.Spectating: - return new MultiSpectatorScreen(userIds); + return new MultiSpectatorScreen(users.Take(PlayerGrid.MAX_PLAYERS).ToArray()); default: - return new PlayerLoader(() => new MultiplayerPlayer(SelectedItem.Value, userIds)); + return new PlayerLoader(() => new MultiplayerPlayer(SelectedItem.Value, users)); } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index 0ff7d70c11..3ba7b8b982 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -37,7 +37,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private MultiplayerGameplayLeaderboard leaderboard; - private readonly int[] userIds; + private readonly MultiplayerRoomUser[] users; private LoadingLayer loadingDisplay; private FillFlowContainer leaderboardFlow; @@ -46,8 +46,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer /// Construct a multiplayer player. /// /// The playlist item to be played. - /// The users which are participating in this game. - public MultiplayerPlayer(PlaylistItem playlistItem, int[] userIds) + /// The users which are participating in this game. + public MultiplayerPlayer(PlaylistItem playlistItem, MultiplayerRoomUser[] users) : base(playlistItem, new PlayerConfiguration { AllowPause = false, @@ -55,7 +55,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer AllowSkipping = false, }) { - this.userIds = userIds; + this.users = users; } [BackgroundDependencyLoader] @@ -71,7 +71,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer }); // todo: this should be implemented via a custom HUD implementation, and correctly masked to the main content area. - LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(ScoreProcessor, userIds), l => + LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(ScoreProcessor, users), l => { if (!LoadedBeatmapSuccessfully) return; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorLeaderboard.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorLeaderboard.cs index 95f9fa27f1..1614828a78 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorLeaderboard.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorLeaderboard.cs @@ -12,8 +12,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate { public class MultiSpectatorLeaderboard : MultiplayerGameplayLeaderboard { - public MultiSpectatorLeaderboard([NotNull] ScoreProcessor scoreProcessor, int[] userIds) - : base(scoreProcessor, userIds) + public MultiSpectatorLeaderboard([NotNull] ScoreProcessor scoreProcessor, MultiplayerRoomUser[] users) + : base(scoreProcessor, users) { } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs index 9923c42583..f77303eec7 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs @@ -46,14 +46,19 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate private PlayerArea currentAudioSource; private bool canStartMasterClock; + private readonly MultiplayerRoomUser[] users; + /// /// Creates a new . /// - /// The players to spectate. - public MultiSpectatorScreen(int[] userIds) - : base(userIds.Take(PlayerGrid.MAX_PLAYERS).ToArray()) + /// The players to spectate. + public MultiSpectatorScreen(MultiplayerRoomUser[] users) + : base(users.Select(u => u.UserID).ToArray()) { - instances = new PlayerArea[UserIds.Count]; + // todo: this is a bit ugly, but not sure on a better way to handle. + this.users = users; + + instances = new PlayerArea[Users.Count]; } [BackgroundDependencyLoader] @@ -105,9 +110,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate }) }; - for (int i = 0; i < UserIds.Count; i++) + for (int i = 0; i < Users.Count; i++) { - grid.Add(instances[i] = new PlayerArea(UserIds[i], masterClockContainer.GameplayClock)); + grid.Add(instances[i] = new PlayerArea(Users[i], masterClockContainer.GameplayClock)); syncManager.AddPlayerClock(instances[i].GameplayClock); } @@ -116,7 +121,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate var scoreProcessor = Ruleset.Value.CreateInstance().CreateScoreProcessor(); scoreProcessor.ApplyBeatmap(playableBeatmap); - LoadComponentAsync(leaderboard = new MultiSpectatorLeaderboard(scoreProcessor, UserIds.ToArray()) + LoadComponentAsync(leaderboard = new MultiSpectatorLeaderboard(scoreProcessor, users) { Expanded = { Value = true }, Anchor = Anchor.CentreLeft, diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 013d266ac2..3f9258930e 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -41,23 +41,24 @@ namespace osu.Game.Screens.Play.HUD private UserLookupCache userLookupCache { get; set; } private readonly ScoreProcessor scoreProcessor; - private readonly IBindableList playingUsers; + private readonly MultiplayerRoomUser[] playingUsers; private Bindable scoringMode; + private readonly IBindableList playingUserIds = new BindableList(); + private bool hasTeams => TeamScores.Count > 0; /// /// Construct a new leaderboard. /// /// A score processor instance to handle score calculation for scores of users in the match. - /// IDs of all users in this match. - public MultiplayerGameplayLeaderboard(ScoreProcessor scoreProcessor, int[] userIds) + /// IDs of all users in this match. + public MultiplayerGameplayLeaderboard(ScoreProcessor scoreProcessor, MultiplayerRoomUser[] users) { // todo: this will eventually need to be created per user to support different mod combinations. this.scoreProcessor = scoreProcessor; - // todo: this will likely be passed in as User instances. - playingUsers = new BindableList(userIds); + playingUsers = users; } [BackgroundDependencyLoader] @@ -65,19 +66,17 @@ namespace osu.Game.Screens.Play.HUD { scoringMode = config.GetBindable(OsuSetting.ScoreDisplayMode); - foreach (var userId in playingUsers) + foreach (var user in playingUsers) { - var user = multiplayerClient.Room?.Users.FirstOrDefault(u => u.UserID == userId); - var trackedUser = CreateUserData(user, scoreProcessor); trackedUser.ScoringMode.BindTo(scoringMode); - UserScores[userId] = trackedUser; + UserScores[user.UserID] = trackedUser; if (trackedUser.Team is int team && !TeamScores.ContainsKey(team)) TeamScores.Add(team, new BindableInt()); } - userLookupCache.GetUsersAsync(playingUsers.ToArray()).ContinueWith(users => Schedule(() => + userLookupCache.GetUsersAsync(playingUsers.Select(u => u.UserID).ToArray()).ContinueWith(users => Schedule(() => { foreach (var user in users.Result) { @@ -100,18 +99,18 @@ namespace osu.Game.Screens.Play.HUD base.LoadComplete(); // BindableList handles binding in a really bad way (Clear then AddRange) so we need to do this manually.. - foreach (int userId in playingUsers) + foreach (var user in playingUsers) { - spectatorClient.WatchUser(userId); + spectatorClient.WatchUser(user.UserID); - if (!multiplayerClient.CurrentMatchPlayingUserIds.Contains(userId)) - usersChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new[] { userId })); + if (!multiplayerClient.CurrentMatchPlayingUserIds.Contains(user.UserID)) + usersChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new[] { user.UserID })); } // bind here is to support players leaving the match. // new players are not supported. - playingUsers.BindTo(multiplayerClient.CurrentMatchPlayingUserIds); - playingUsers.BindCollectionChanged(usersChanged); + playingUserIds.BindTo(multiplayerClient.CurrentMatchPlayingUserIds); + playingUserIds.BindCollectionChanged(usersChanged); // this leaderboard should be guaranteed to be completely loaded before the gameplay starts (is a prerequisite in MultiplayerPlayer). spectatorClient.OnNewFrames += handleIncomingFrames; @@ -197,7 +196,7 @@ namespace osu.Game.Screens.Play.HUD { foreach (var user in playingUsers) { - spectatorClient.StopWatchingUser(user); + spectatorClient.StopWatchingUser(user.UserID); } spectatorClient.OnNewFrames -= handleIncomingFrames; diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs index b6eafe496f..f0a68ea078 100644 --- a/osu.Game/Screens/Spectate/SpectatorScreen.cs +++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs @@ -24,9 +24,9 @@ namespace osu.Game.Screens.Spectate /// public abstract class SpectatorScreen : OsuScreen { - protected IReadOnlyList UserIds => userIds; + protected IReadOnlyList Users => users; - private readonly List userIds = new List(); + private readonly List users = new List(); [Resolved] private BeatmapManager beatmaps { get; set; } @@ -50,17 +50,17 @@ namespace osu.Game.Screens.Spectate /// /// Creates a new . /// - /// The users to spectate. - protected SpectatorScreen(params int[] userIds) + /// The users to spectate. + protected SpectatorScreen(params int[] users) { - this.userIds.AddRange(userIds); + this.users.AddRange(users); } protected override void LoadComplete() { base.LoadComplete(); - userLookupCache.GetUsersAsync(userIds.ToArray()).ContinueWith(users => Schedule(() => + userLookupCache.GetUsersAsync(users.ToArray()).ContinueWith(users => Schedule(() => { foreach (var u in users.Result) { @@ -207,7 +207,7 @@ namespace osu.Game.Screens.Spectate { onUserStateRemoved(userId); - userIds.Remove(userId); + users.Remove(userId); userMap.Remove(userId); spectatorClient.StopWatchingUser(userId); From 60302e3daae38014ed9577ffd0c7d172482a05a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Aug 2021 18:46:20 +0900 Subject: [PATCH 0964/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index f757847c10..c1eddfd428 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3a840296ac..55bac93f30 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 217ec6089a..5a0b21f51d 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 58db6b758d07150acbd2b58a92e3b7bea9120c17 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Aug 2021 18:47:39 +0900 Subject: [PATCH 0965/2442] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index c1eddfd428..8de516240f 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 55bac93f30..59283084db 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 5a0b21f51d..c8d3d150db 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From 4268e4d75028d5ca43cf9786bb0a5efdcfb950a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 7 Aug 2021 14:19:05 +0200 Subject: [PATCH 0966/2442] Fix nested menus layering close samples if multiple menu levels are closed --- .../Cursor/OsuContextMenuContainer.cs | 9 +++++ .../Graphics/UserInterface/OsuContextMenu.cs | 29 +++++---------- .../UserInterface/OsuContextMenuSamples.cs | 35 +++++++++++++++++++ 3 files changed, 52 insertions(+), 21 deletions(-) create mode 100644 osu.Game/Graphics/UserInterface/OsuContextMenuSamples.cs diff --git a/osu.Game/Graphics/Cursor/OsuContextMenuContainer.cs b/osu.Game/Graphics/Cursor/OsuContextMenuContainer.cs index e64d2d98f4..171ad4ee65 100644 --- a/osu.Game/Graphics/Cursor/OsuContextMenuContainer.cs +++ b/osu.Game/Graphics/Cursor/OsuContextMenuContainer.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.UserInterface; @@ -9,6 +10,14 @@ namespace osu.Game.Graphics.Cursor { public class OsuContextMenuContainer : ContextMenuContainer { + [Cached] + private OsuContextMenuSamples samples = new OsuContextMenuSamples(); + + public OsuContextMenuContainer() + { + AddInternal(samples); + } + protected override Menu CreateMenu() => new OsuContextMenu(true); } } diff --git a/osu.Game/Graphics/UserInterface/OsuContextMenu.cs b/osu.Game/Graphics/UserInterface/OsuContextMenu.cs index 7cf8b3eca8..cf201b18b4 100644 --- a/osu.Game/Graphics/UserInterface/OsuContextMenu.cs +++ b/osu.Game/Graphics/UserInterface/OsuContextMenu.cs @@ -4,8 +4,6 @@ using osuTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Audio; -using osu.Framework.Audio.Sample; -using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Effects; @@ -16,16 +14,15 @@ namespace osu.Game.Graphics.UserInterface public class OsuContextMenu : OsuMenu { private const int fade_duration = 250; - private Sample sampleOpen; - private Sample sampleClose; - private Sample sampleClick; + + [Resolved] + private OsuContextMenuSamples samples { get; set; } // todo: this shouldn't be required after https://github.com/ppy/osu-framework/issues/4519 is fixed. private bool wasOpened; private readonly bool playClickSample; - private readonly Menu parentMenu; - public OsuContextMenu(bool playClickSample = false, Menu parentMenu = null) + public OsuContextMenu(bool playClickSample = false) : base(Direction.Vertical) { MaskingContainer.CornerRadius = 5; @@ -41,16 +38,12 @@ namespace osu.Game.Graphics.UserInterface MaxHeight = 250; this.playClickSample = playClickSample; - this.parentMenu = parentMenu; } [BackgroundDependencyLoader] private void load(OsuColour colours, AudioManager audio) { BackgroundColour = colours.ContextMenuGray; - sampleClick = audio.Samples.Get($"UI/{HoverSampleSet.Default.GetDescription()}-select"); - sampleOpen = audio.Samples.Get(@"UI/dropdown-open"); - sampleClose = audio.Samples.Get(@"UI/dropdown-close"); } protected override void AnimateOpen() @@ -58,10 +51,10 @@ namespace osu.Game.Graphics.UserInterface this.FadeIn(fade_duration, Easing.OutQuint); if (playClickSample) - sampleClick?.Play(); + samples.PlayClickSample(); if (!wasOpened) - sampleOpen?.Play(); + samples.PlayOpenSample(); wasOpened = true; } @@ -70,18 +63,12 @@ namespace osu.Game.Graphics.UserInterface { this.FadeOut(fade_duration, Easing.OutQuint); - if (parentMenu?.State == MenuState.Closed) - { - wasOpened = false; - return; - } - if (wasOpened) - sampleClose?.Play(); + samples.PlayCloseSample(); wasOpened = false; } - protected override Menu CreateSubMenu() => new OsuContextMenu(false, this); + protected override Menu CreateSubMenu() => new OsuContextMenu(); } } diff --git a/osu.Game/Graphics/UserInterface/OsuContextMenuSamples.cs b/osu.Game/Graphics/UserInterface/OsuContextMenuSamples.cs new file mode 100644 index 0000000000..d67ea499e5 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/OsuContextMenuSamples.cs @@ -0,0 +1,35 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Extensions; +using osu.Framework.Graphics; + +namespace osu.Game.Graphics.UserInterface +{ + public class OsuContextMenuSamples : Component + { + private Sample sampleClick; + private Sample sampleOpen; + private Sample sampleClose; + + [BackgroundDependencyLoader] + private void load(OsuColour colours, AudioManager audio) + { + sampleClick = audio.Samples.Get($@"UI/{HoverSampleSet.Default.GetDescription()}-select"); + sampleOpen = audio.Samples.Get(@"UI/dropdown-open"); + sampleClose = audio.Samples.Get(@"UI/dropdown-close"); + } + + public void PlayClickSample() => Scheduler.AddOnce(playClickSample); + private void playClickSample() => sampleClick.Play(); + + public void PlayOpenSample() => Scheduler.AddOnce(playOpenSample); + private void playOpenSample() => sampleOpen.Play(); + + public void PlayCloseSample() => Scheduler.AddOnce(playCloseSample); + private void playCloseSample() => sampleClose.Play(); + } +} From cee69eaad0c50c35031e74c59b99db52793e926c Mon Sep 17 00:00:00 2001 From: MBmasher Date: Wed, 11 Aug 2021 06:14:38 +1000 Subject: [PATCH 0967/2442] Add a nerf to FL for TD plays --- .../Difficulty/OsuPerformanceCalculator.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 267e332372..2d4e4cf551 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -194,7 +194,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(h => h is OsuModFlashlight)) { - flashlightValue = Math.Pow(Attributes.FlashlightStrain, 2.0) * 25.0; + double rawFlashlight = Attributes.FlashlightStrain; + + if (mods.Any(m => m is OsuModTouchDevice)) + rawFlashlight = Math.Pow(rawFlashlight, 0.8); + + flashlightValue = Math.Pow(rawFlashlight, 2.0) * 25.0; // Add an additional bonus for HDFL. if (mods.Any(h => h is OsuModHidden)) From 30cb4280a450664dd04fc13b31c80ec8f96798c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 Aug 2021 22:21:54 +0200 Subject: [PATCH 0968/2442] Add test for catch beatmap mirroring --- .../Mods/CatchModMirrorTest.cs | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 osu.Game.Rulesets.Catch.Tests/Mods/CatchModMirrorTest.cs diff --git a/osu.Game.Rulesets.Catch.Tests/Mods/CatchModMirrorTest.cs b/osu.Game.Rulesets.Catch.Tests/Mods/CatchModMirrorTest.cs new file mode 100644 index 0000000000..8e66284a23 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Mods/CatchModMirrorTest.cs @@ -0,0 +1,122 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Utils; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Catch.Beatmaps; +using osu.Game.Rulesets.Catch.Mods; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Objects; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Tests.Mods +{ + [TestFixture] + public class CatchModMirrorTest + { + [Test] + public void TestModMirror() + { + IBeatmap original = createBeatmap(false); + IBeatmap mirrored = createBeatmap(true); + + assertEffectivePositionsMirrored(original, mirrored); + } + + private static IBeatmap createBeatmap(bool withMirrorMod) + { + var beatmap = createRawBeatmap(); + var mirrorMod = new CatchModMirror(); + + var beatmapProcessor = new CatchBeatmapProcessor(beatmap); + beatmapProcessor.PreProcess(); + + foreach (var hitObject in beatmap.HitObjects) + { + hitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + + if (withMirrorMod) + mirrorMod.ApplyToHitObject(hitObject); + } + + beatmapProcessor.PostProcess(); + + return beatmap; + } + + private static IBeatmap createRawBeatmap() => new Beatmap + { + HitObjects = new List + { + new Fruit + { + OriginalX = 150, + StartTime = 0 + }, + new Fruit + { + OriginalX = 450, + StartTime = 500 + }, + new JuiceStream + { + OriginalX = 250, + Path = new SliderPath + { + ControlPoints = + { + new PathControlPoint(new Vector2(-100, 1)), + new PathControlPoint(new Vector2(0, 2)), + new PathControlPoint(new Vector2(100, 3)), + new PathControlPoint(new Vector2(0, 4)) + } + }, + StartTime = 1000, + }, + new BananaShower + { + StartTime = 5000, + Duration = 5000 + } + } + }; + + private static void assertEffectivePositionsMirrored(IBeatmap original, IBeatmap mirrored) + { + if (original.HitObjects.Count != mirrored.HitObjects.Count) + Assert.Fail($"Top-level object count mismatch (original: {original.HitObjects.Count}, mirrored: {mirrored.HitObjects.Count})"); + + for (int i = 0; i < original.HitObjects.Count; ++i) + { + var originalObject = (CatchHitObject)original.HitObjects[i]; + var mirroredObject = (CatchHitObject)mirrored.HitObjects[i]; + + // banana showers themselves are exempt, as we only really care about their nested bananas' positions. + if (!effectivePositionMirrored(originalObject, mirroredObject) && !(originalObject is BananaShower)) + Assert.Fail($"{originalObject.GetType().Name} at time {originalObject.StartTime} is not mirrored ({printEffectivePositions(originalObject, mirroredObject)})"); + + if (originalObject.NestedHitObjects.Count != mirroredObject.NestedHitObjects.Count) + Assert.Fail($"{originalObject.GetType().Name} nested object count mismatch (original: {originalObject.NestedHitObjects.Count}, mirrored: {mirroredObject.NestedHitObjects.Count})"); + + for (int j = 0; j < originalObject.NestedHitObjects.Count; ++j) + { + var originalNested = (CatchHitObject)originalObject.NestedHitObjects[j]; + var mirroredNested = (CatchHitObject)mirroredObject.NestedHitObjects[j]; + + if (!effectivePositionMirrored(originalNested, mirroredNested)) + Assert.Fail($"{originalObject.GetType().Name}'s nested {originalNested.GetType().Name} at time {originalObject.StartTime} is not mirrored ({printEffectivePositions(originalNested, mirroredNested)})"); + } + } + } + + private static string printEffectivePositions(CatchHitObject original, CatchHitObject mirrored) + => $"original X: {original.EffectiveX}, mirrored X is: {mirrored.EffectiveX}, mirrored X should be: {CatchPlayfield.WIDTH - original.EffectiveX}"; + + private static bool effectivePositionMirrored(CatchHitObject original, CatchHitObject mirrored) + => Precision.AlmostEquals(original.EffectiveX, CatchPlayfield.WIDTH - mirrored.EffectiveX); + } +} From 637e5cb6b713bd942c6dd58d6f1a56c0ead646b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 Aug 2021 21:42:41 +0200 Subject: [PATCH 0969/2442] Fix offsets not being mirrored --- .../Mods/CatchModMirrorTest.cs | 8 +++---- .../Mods/CatchModMirror.cs | 21 +++++++++++++++++-- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Mods/CatchModMirrorTest.cs b/osu.Game.Rulesets.Catch.Tests/Mods/CatchModMirrorTest.cs index 8e66284a23..fbbfee6b60 100644 --- a/osu.Game.Rulesets.Catch.Tests/Mods/CatchModMirrorTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/Mods/CatchModMirrorTest.cs @@ -36,15 +36,13 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods beatmapProcessor.PreProcess(); foreach (var hitObject in beatmap.HitObjects) - { hitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - if (withMirrorMod) - mirrorMod.ApplyToHitObject(hitObject); - } - beatmapProcessor.PostProcess(); + if (withMirrorMod) + mirrorMod.ApplyToBeatmap(beatmap); + return beatmap; } diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs index 7fe7e70fd0..3aa8862a0a 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Catch.UI; @@ -10,11 +12,22 @@ using osuTK; namespace osu.Game.Rulesets.Catch.Mods { - public class CatchModMirror : ModMirror, IApplicableToHitObject + public class CatchModMirror : ModMirror, IApplicableToBeatmap { public override string Description => "Fruits are flipped horizontally."; - public void ApplyToHitObject(HitObject hitObject) + /// + /// is used instead of , + /// as applies offsets in . + /// runs after post-processing, while runs before it. + /// + public void ApplyToBeatmap(IBeatmap beatmap) + { + foreach (var hitObject in beatmap.HitObjects) + applyToHitObject(hitObject); + } + + private void applyToHitObject(HitObject hitObject) { if (hitObject is BananaShower) return; @@ -22,9 +35,13 @@ namespace osu.Game.Rulesets.Catch.Mods var catchObject = (CatchHitObject)hitObject; catchObject.OriginalX = CatchPlayfield.WIDTH - catchObject.OriginalX; + catchObject.XOffset = -catchObject.XOffset; foreach (var nested in catchObject.NestedHitObjects.Cast()) + { nested.OriginalX = CatchPlayfield.WIDTH - nested.OriginalX; + nested.XOffset = -nested.XOffset; + } if (catchObject is JuiceStream juiceStream) { From a9e53107d146ac1fad598136b439f9296a1c51a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 Aug 2021 21:51:46 +0200 Subject: [PATCH 0970/2442] Also mirror banana showers --- .../Mods/CatchModMirror.cs | 51 +++++++++++++++---- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs index 3aa8862a0a..932c8cad85 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs @@ -29,11 +29,30 @@ namespace osu.Game.Rulesets.Catch.Mods private void applyToHitObject(HitObject hitObject) { - if (hitObject is BananaShower) - return; - var catchObject = (CatchHitObject)hitObject; + switch (catchObject) + { + case Fruit fruit: + mirrorEffectiveX(fruit); + break; + + case JuiceStream juiceStream: + mirrorEffectiveX(juiceStream); + mirrorJuiceStreamPath(juiceStream); + break; + + case BananaShower bananaShower: + mirrorBananaShower(bananaShower); + break; + } + } + + /// + /// Mirrors the effective X position of and its nested hit objects. + /// + private static void mirrorEffectiveX(CatchHitObject catchObject) + { catchObject.OriginalX = CatchPlayfield.WIDTH - catchObject.OriginalX; catchObject.XOffset = -catchObject.XOffset; @@ -42,15 +61,27 @@ namespace osu.Game.Rulesets.Catch.Mods nested.OriginalX = CatchPlayfield.WIDTH - nested.OriginalX; nested.XOffset = -nested.XOffset; } + } - if (catchObject is JuiceStream juiceStream) - { - var controlPoints = juiceStream.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); - foreach (var point in controlPoints) - point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y); + /// + /// Mirrors the path of the . + /// + private static void mirrorJuiceStreamPath(JuiceStream juiceStream) + { + var controlPoints = juiceStream.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); + foreach (var point in controlPoints) + point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y); - juiceStream.Path = new SliderPath(controlPoints, juiceStream.Path.ExpectedDistance.Value); - } + juiceStream.Path = new SliderPath(controlPoints, juiceStream.Path.ExpectedDistance.Value); + } + + /// + /// Mirrors X positions of all bananas in the . + /// + private static void mirrorBananaShower(BananaShower bananaShower) + { + foreach (var banana in bananaShower.NestedHitObjects.OfType()) + banana.XOffset = CatchPlayfield.WIDTH - banana.XOffset; } } } From b1d25346a2e646200c759f1763f6b1da7367bfa9 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Wed, 11 Aug 2021 13:30:40 +1000 Subject: [PATCH 0971/2442] Move HistoryLength override from OsuStrainSkill to Flashlight --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 1 + osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index b8a96b3310..dc60ab4041 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -22,6 +22,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override double SkillMultiplier => 0.065; protected override double StrainDecayBase => 0.15; protected override double DecayWeight => 1.0; + protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. protected override double StrainValueOf(DifficultyHitObject current) { diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index f74298cdca..e47edc37cc 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -28,8 +28,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// protected virtual double DifficultyMultiplier => 1.06; - protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. - protected OsuStrainSkill(Mod[] mods) : base(mods) { From a3a9d0579f13166a975d75dec3cdf8cc40d9c511 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Aug 2021 12:33:14 +0900 Subject: [PATCH 0972/2442] Adjust checkbox / sliderbar animation speeds to match sound effects better --- osu.Game/Graphics/UserInterface/Nub.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/Nub.cs b/osu.Game/Graphics/UserInterface/Nub.cs index 18d8b880ea..8d686e8c2f 100644 --- a/osu.Game/Graphics/UserInterface/Nub.cs +++ b/osu.Game/Graphics/UserInterface/Nub.cs @@ -21,6 +21,9 @@ namespace osu.Game.Graphics.UserInterface private const float border_width = 3; + private const double animate_in_duration = 150; + private const double animate_out_duration = 500; + public Nub() { Box fill; @@ -77,20 +80,26 @@ namespace osu.Game.Graphics.UserInterface if (value) { - this.FadeColour(GlowingAccentColour, 500, Easing.OutQuint); - FadeEdgeEffectTo(1, 500, Easing.OutQuint); + this.FadeColour(GlowingAccentColour, animate_in_duration, Easing.OutQuint); + FadeEdgeEffectTo(1, animate_in_duration, Easing.OutQuint); } else { - FadeEdgeEffectTo(0, 500); - this.FadeColour(AccentColour, 500); + FadeEdgeEffectTo(0, animate_out_duration); + this.FadeColour(AccentColour, animate_out_duration); } } } public bool Expanded { - set => this.ResizeTo(new Vector2(value ? EXPANDED_SIZE : COLLAPSED_SIZE, 12), 500, Easing.OutQuint); + set + { + if (value) + this.ResizeTo(new Vector2(EXPANDED_SIZE, 12), animate_in_duration, Easing.OutQuint); + else + this.ResizeTo(new Vector2(COLLAPSED_SIZE, 12), animate_out_duration, Easing.OutQuint); + } } private readonly Bindable current = new Bindable(); From 0d2ab550dd8c1285d94ae8991e040f06ea9a970a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 11 Aug 2021 13:04:32 +0900 Subject: [PATCH 0973/2442] Update SR values in tests --- .../OsuDifficultyCalculatorTest.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index 32fba0b70c..19881b5c33 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -15,13 +15,13 @@ namespace osu.Game.Rulesets.Osu.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; - [TestCase(6.4164199087433484d, "diffcalc-test")] - [TestCase(1.0028132594779837d, "zero-length-sliders")] + [TestCase(6.6634445062299665d, "diffcalc-test")] + [TestCase(1.0414203870195022d, "zero-length-sliders")] public void Test(double expected, string name) => base.Test(expected, name); - [TestCase(8.0749334911818469d, "diffcalc-test")] - [TestCase(1.2251606765323657d, "zero-length-sliders")] + [TestCase(8.3858089051603368d, "diffcalc-test")] + [TestCase(1.2723279173428435d, "zero-length-sliders")] public void TestClockRateAdjusted(double expected, string name) => Test(expected, name, new OsuModDoubleTime()); From f2f84a2e239ca8f03a899a81c6ec551c403170ba Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Wed, 11 Aug 2021 12:11:39 +0700 Subject: [PATCH 0974/2442] wait for content to load in test --- osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs index 159eb9912c..8f000afb91 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChangelogOverlay.cs @@ -158,6 +158,7 @@ namespace osu.Game.Tests.Visual.Online AddUntilStep(@"wait for streams", () => changelog.Streams?.Count > 0); AddAssert(@"correct build displayed", () => changelog.Current.Value.Version == "2018.712.0"); AddAssert(@"correct stream selected", () => changelog.Header.Streams.Current.Value.Id == 5); + AddUntilStep(@"wait for content load", () => changelog.ChildrenOfType().Any()); AddAssert(@"supporter promo showed", () => changelog.ChildrenOfType().First().Alpha == (isSupporter ? 0 : 1)); } From cf82bca09c56eed50dd4dc7b8e97e80af76825d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Aug 2021 14:44:13 +0900 Subject: [PATCH 0975/2442] Change logic to only handle the case of exactly two teams --- .../OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs index f77303eec7..2846fbec29 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs @@ -133,7 +133,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate leaderboardContainer.Add(leaderboard); - if (leaderboard.TeamScores.Count >= 2) + if (leaderboard.TeamScores.Count == 2) { LoadComponentAsync(new MatchScoreDisplay { From ee3b373e8a0db22c0119d1822ca2db468ba586fe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Aug 2021 14:48:37 +0900 Subject: [PATCH 0976/2442] Correctly handle tied scores --- .../Screens/Play/HUD/MatchScoreDisplay.cs | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs index 837d8bfde7..c77b872786 100644 --- a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs +++ b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs @@ -112,11 +112,23 @@ namespace osu.Game.Screens.Play.HUD Score1Text.Current.Value = Team1Score.Value; Score2Text.Current.Value = Team2Score.Value; - var winningText = Team1Score.Value > Team2Score.Value ? Score1Text : Score2Text; - var losingText = Team1Score.Value <= Team2Score.Value ? Score1Text : Score2Text; + int comparison = Team1Score.Value.CompareTo(Team2Score.Value); - winningText.Winning = true; - losingText.Winning = false; + if (comparison > 0) + { + Score1Text.Winning = true; + Score2Text.Winning = false; + } + else if (comparison < 0) + { + Score1Text.Winning = false; + Score2Text.Winning = true; + } + else + { + Score1Text.Winning = false; + Score2Text.Winning = false; + } var winningBar = Team1Score.Value > Team2Score.Value ? score1Bar : score2Bar; var losingBar = Team1Score.Value <= Team2Score.Value ? score1Bar : score2Bar; From fb7ed08bab60c42b68bb3d3816259cd8bbeb8c70 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Wed, 11 Aug 2021 12:49:22 +0700 Subject: [PATCH 0977/2442] move text creation to load method --- .../Changelog/ChangelogSupporterPromo.cs | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index f5638293bb..c0d78cc887 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -22,8 +22,7 @@ namespace osu.Game.Overlays.Changelog { private const float image_container_width = 164; - private readonly LinkFlowContainer supportLinkText; - private readonly TextFlowContainer supportNoteText; + private readonly FillFlowContainer textContainer; private readonly Container imageContainer; public ChangelogSupporterPromo() @@ -65,7 +64,7 @@ namespace osu.Game.Overlays.Changelog Padding = new MarginPadding { Horizontal = 75 }, Children = new Drawable[] { - new FillFlowContainer + textContainer = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -73,32 +72,6 @@ namespace osu.Game.Overlays.Changelog Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Padding = new MarginPadding { Right = 50 + image_container_width }, - Children = new Drawable[] - { - new OsuSpriteText - { - Text = ChangelogStrings.SupportHeading, - Font = OsuFont.GetFont(size: 20, weight: FontWeight.Light), - Margin = new MarginPadding { Bottom = 20 }, - }, - supportLinkText = new LinkFlowContainer(t => - { - t.Font = t.Font.With(size: 14); - }) - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - }, - supportNoteText = new TextFlowContainer(t => - { - t.Font = t.Font.With(size: 12); - }) - { - Margin = new MarginPadding { Top = 10 }, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - } - }, }, imageContainer = new Container { @@ -117,17 +90,44 @@ namespace osu.Game.Overlays.Changelog [BackgroundDependencyLoader] private void load(OsuColour colour, TextureStore textures) { - void fontPinkColour(SpriteText t) => t.Colour = colour.PinkLighter; + LinkFlowContainer supportLinkText; + textContainer.Children = new Drawable[] + { + new OsuSpriteText + { + Text = ChangelogStrings.SupportHeading, + Font = OsuFont.GetFont(size: 20, weight: FontWeight.Light), + Margin = new MarginPadding { Bottom = 20 }, + }, + supportLinkText = new LinkFlowContainer(t => + { + t.Font = t.Font.With(size: 14); + t.Colour = colour.PinkLighter; + }) + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }, + new TextFlowContainer(t => + { + t.Font = t.Font.With(size: 12); + t.Colour = colour.PinkLighter; + }) + { + Text = "Not only will you help speed development, but you will also get some extra features and customisations!", + Margin = new MarginPadding { Top = 10 }, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + } + }; - supportLinkText.AddText("Support further development of osu! and ", fontPinkColour); + supportLinkText.AddText("Support further development of osu! and "); supportLinkText.AddLink("become an osu!supporter", "https://osu.ppy.sh/home/support", t => { t.Colour = colour.PinkDark; t.Font = t.Font.With(weight: FontWeight.Bold); }); - supportLinkText.AddText(" today!", fontPinkColour); - - supportNoteText.AddText("Not only will you help speed development, but you will also get some extra features and customisations!", fontPinkColour); + supportLinkText.AddText(" today!"); imageContainer.Children = new Drawable[] { From 735c5085ddd29088ba5ae1a9fd817b1f335995d5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Aug 2021 14:43:01 +0900 Subject: [PATCH 0978/2442] Fix match score display not doing an initial value update --- osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs index c77b872786..607d08b8ed 100644 --- a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs +++ b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs @@ -104,7 +104,7 @@ namespace osu.Game.Screens.Play.HUD base.LoadComplete(); Team1Score.BindValueChanged(_ => updateScores()); - Team2Score.BindValueChanged(_ => updateScores()); + Team2Score.BindValueChanged(_ => updateScores(), true); } private void updateScores() From 1cadcb43d958d3e00e4941eff674b321b392db43 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Wed, 11 Aug 2021 15:54:30 +1000 Subject: [PATCH 0979/2442] Apply nerf to Flashlight skill on high star maps --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index dc60ab4041..d225486cc8 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { } - protected override double SkillMultiplier => 0.065; + protected override double SkillMultiplier => 0.13; protected override double StrainDecayBase => 0.15; protected override double DecayWeight => 1.0; protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } } - return Math.Pow(smallDistNerf * result, 2.5); + return Math.Pow(smallDistNerf * result, 2.0); } } } From 27918583e1143a2b8e717d9824998c0a28848ec4 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Wed, 11 Aug 2021 15:55:13 +1000 Subject: [PATCH 0980/2442] Increase the multiplier when hidden is applied on the Flashlight skill --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 2d4e4cf551..2ca5145c6a 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -203,7 +203,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty // Add an additional bonus for HDFL. if (mods.Any(h => h is OsuModHidden)) - flashlightValue *= 1.2; + flashlightValue *= 1.3; // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. if (countMiss > 0) From c376e652a4abdd44cc2e22ea06a45cd356e22b45 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Aug 2021 14:43:16 +0900 Subject: [PATCH 0981/2442] Convey and show team scores at the multiplayer results screen --- .../TestSceneMultiplayerTeamResults.cs | 88 +++++++++++++++++++ .../Multiplayer/MultiplayerPlayer.cs | 2 +- .../Multiplayer/MultiplayerResultsScreen.cs | 23 ++++- 3 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs new file mode 100644 index 0000000000..2b04955d5e --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs @@ -0,0 +1,88 @@ +// 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 NUnit.Framework; +using osu.Framework.Bindables; +using osu.Game.Online.Rooms; +using osu.Game.Rulesets.Osu; +using osu.Game.Scoring; +using osu.Game.Screens.OnlinePlay.Multiplayer; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneMultiplayerTeamResults : ScreenTestScene + { + [Test] + public void TestDisplayWithTeams() + { + AddStep("show results screen", () => + { + var rulesetInfo = new OsuRuleset().RulesetInfo; + var beatmapInfo = CreateBeatmap(rulesetInfo).BeatmapInfo; + + var score = new ScoreInfo + { + Rank = ScoreRank.B, + TotalScore = 987654, + Accuracy = 0.8, + MaxCombo = 500, + Combo = 250, + Beatmap = beatmapInfo, + User = new User { Username = "Test user" }, + Date = DateTimeOffset.Now, + OnlineScoreID = 12345, + Ruleset = rulesetInfo, + }; + + PlaylistItem playlistItem = new PlaylistItem + { + BeatmapID = beatmapInfo.ID, + }; + + SortedDictionary teamScores = new SortedDictionary + { + { 0, new BindableInt(7483253) }, + { 1, new BindableInt(1048576) } + }; + + Stack.Push(new MultiplayerResultsScreen(score, 1, playlistItem, teamScores)); + }); + } + + [Test] + public void TestDisplayWithoutTeams() + { + AddStep("show results screen", () => + { + var rulesetInfo = new OsuRuleset().RulesetInfo; + var beatmapInfo = CreateBeatmap(rulesetInfo).BeatmapInfo; + + var score = new ScoreInfo + { + Rank = ScoreRank.B, + TotalScore = 987654, + Accuracy = 0.8, + MaxCombo = 500, + Combo = 250, + Beatmap = beatmapInfo, + User = new User { Username = "Test user" }, + Date = DateTimeOffset.Now, + OnlineScoreID = 12345, + Ruleset = rulesetInfo, + }; + + PlaylistItem playlistItem = new PlaylistItem + { + BeatmapID = beatmapInfo.ID, + }; + + SortedDictionary teamScores = new SortedDictionary(); + + Stack.Push(new MultiplayerResultsScreen(score, 1, playlistItem, teamScores)); + }); + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index 3ba7b8b982..3c892d03df 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -181,7 +181,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override ResultsScreen CreateResults(ScoreInfo score) { Debug.Assert(RoomId.Value != null); - return new MultiplayerResultsScreen(score, RoomId.Value.Value, PlaylistItem); + return new MultiplayerResultsScreen(score, RoomId.Value.Value, PlaylistItem, leaderboard.TeamScores); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs index 140b3c45d8..66de572b77 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs @@ -1,17 +1,38 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Game.Online.Rooms; using osu.Game.Scoring; using osu.Game.Screens.OnlinePlay.Playlists; +using osu.Game.Screens.Play.HUD; namespace osu.Game.Screens.OnlinePlay.Multiplayer { public class MultiplayerResultsScreen : PlaylistsResultsScreen { - public MultiplayerResultsScreen(ScoreInfo score, long roomId, PlaylistItem playlistItem) + private readonly SortedDictionary teamScores; + + public MultiplayerResultsScreen(ScoreInfo score, long roomId, PlaylistItem playlistItem, SortedDictionary teamScores) : base(score, roomId, playlistItem, false, false) { + this.teamScores = teamScores; + } + + [BackgroundDependencyLoader] + private void load() + { + if (teamScores.Count == 2) + { + LoadComponentAsync(new MatchScoreDisplay + { + Team1Score = { BindTarget = teamScores.First().Value }, + Team2Score = { BindTarget = teamScores.Last().Value }, + }, AddInternal); + } } } } From 430a0e496caf02df211f55fcb0fee7fdb0bf5759 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Aug 2021 15:32:10 +0900 Subject: [PATCH 0982/2442] Add winner text --- .../MultiplayerResultsScreenStrings.cs | 19 +++++ .../Multiplayer/MultiplayerResultsScreen.cs | 72 +++++++++++++++++-- 2 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 osu.Game/Localisation/MultiplayerResultsScreenStrings.cs diff --git a/osu.Game/Localisation/MultiplayerResultsScreenStrings.cs b/osu.Game/Localisation/MultiplayerResultsScreenStrings.cs new file mode 100644 index 0000000000..e586faba66 --- /dev/null +++ b/osu.Game/Localisation/MultiplayerResultsScreenStrings.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class MultiplayerResultsScreenStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.MultiplayerResultsScreen"; + + /// + /// "Team {0} wins!" + /// + public static LocalisableString TeamWins(string winner) => new TranslatableString(getKey(@"team_wins"), @"Team {0} wins!", winner); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} \ No newline at end of file diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs index 66de572b77..61bc66de45 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs @@ -5,10 +5,18 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; using osu.Game.Online.Rooms; using osu.Game.Scoring; using osu.Game.Screens.OnlinePlay.Playlists; using osu.Game.Screens.Play.HUD; +using osu.Game.Localisation; +using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay.Multiplayer { @@ -16,22 +24,78 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { private readonly SortedDictionary teamScores; + private Drawable winnerText; + public MultiplayerResultsScreen(ScoreInfo score, long roomId, PlaylistItem playlistItem, SortedDictionary teamScores) : base(score, roomId, playlistItem, false, false) { this.teamScores = teamScores; } + [Resolved] + private OsuColour colours { get; set; } + [BackgroundDependencyLoader] private void load() { if (teamScores.Count == 2) { - LoadComponentAsync(new MatchScoreDisplay + var redScore = teamScores.First().Value; + var blueScore = teamScores.Last().Value; + + // eventually this will be replaced by team names coming from the multiplayer match state. + string winner = redScore.Value > blueScore.Value ? @"Red" : @"Blue"; + + var winnerColour = redScore.Value > blueScore.Value ? colours.TeamColourRed : colours.TeamColourBlue; + + AddRangeInternal(new Drawable[] { - Team1Score = { BindTarget = teamScores.First().Value }, - Team2Score = { BindTarget = teamScores.Last().Value }, - }, AddInternal); + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new MatchScoreDisplay + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Team1Score = { BindTarget = redScore }, + Team2Score = { BindTarget = blueScore }, + }, + (winnerText = new OsuSpriteText + { + Alpha = 0, + Font = OsuFont.Torus.With(size: 40, weight: FontWeight.Bold), + Text = MultiplayerResultsScreenStrings.TeamWins(winner) + }).WithEffect(new GlowEffect + { + Colour = winnerColour, + }).With(e => + { + e.Anchor = Anchor.TopCentre; + e.Origin = Anchor.TopCentre; + }) + } + }, + }); + } + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + using (BeginDelayedSequence(300)) + { + winnerText.FadeInFromZero(600, Easing.InQuint); + + winnerText + .ScaleTo(10) + .ScaleTo(1, 600, Easing.InQuad) + .Then() + .ScaleTo(1.02f, 1600, Easing.OutQuint); } } } From a223f111cb43633f53be512e16215c13b4f90721 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Aug 2021 15:50:01 +0900 Subject: [PATCH 0983/2442] Move text to avoid overlapping the results panel --- .../Multiplayer/MultiplayerResultsScreen.cs | 53 ++++++++----------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs index 61bc66de45..0e4655e336 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs @@ -5,18 +5,16 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Localisation; using osu.Game.Online.Rooms; using osu.Game.Scoring; using osu.Game.Screens.OnlinePlay.Playlists; using osu.Game.Screens.Play.HUD; -using osu.Game.Localisation; -using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay.Multiplayer { @@ -50,35 +48,27 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer AddRangeInternal(new Drawable[] { - new FillFlowContainer + new MatchScoreDisplay { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new MatchScoreDisplay - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Team1Score = { BindTarget = redScore }, - Team2Score = { BindTarget = blueScore }, - }, - (winnerText = new OsuSpriteText - { - Alpha = 0, - Font = OsuFont.Torus.With(size: 40, weight: FontWeight.Bold), - Text = MultiplayerResultsScreenStrings.TeamWins(winner) - }).WithEffect(new GlowEffect - { - Colour = winnerColour, - }).With(e => - { - e.Anchor = Anchor.TopCentre; - e.Origin = Anchor.TopCentre; - }) - } + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Team1Score = { BindTarget = redScore }, + Team2Score = { BindTarget = blueScore }, }, + (winnerText = new OsuSpriteText + { + Alpha = 0, + Font = OsuFont.Torus.With(size: 80, weight: FontWeight.Bold), + Text = MultiplayerResultsScreenStrings.TeamWins(winner), + Blending = BlendingParameters.Additive + }).WithEffect(new GlowEffect + { + Colour = winnerColour, + }).With(e => + { + e.Anchor = Anchor.Centre; + e.Origin = Anchor.Centre; + }) }); } } @@ -95,7 +85,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer .ScaleTo(10) .ScaleTo(1, 600, Easing.InQuad) .Then() - .ScaleTo(1.02f, 1600, Easing.OutQuint); + .ScaleTo(1.02f, 1600, Easing.OutQuint) + .FadeOut(5000, Easing.InQuad); } } } From f8683e2256d8e452da31d6429cdada61e92833b2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Aug 2021 16:17:21 +0900 Subject: [PATCH 0984/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 8de516240f..454bb46059 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 59283084db..e6219fcb85 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index c8d3d150db..9904946363 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 0ffe740ca1dcce1e2c7fa276f96f523b004b0fed Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Aug 2021 16:24:47 +0900 Subject: [PATCH 0985/2442] Bring back `TestSceneOsuGame` I marked this as headless to avoid it being "ungrouped", but it turns out this is quite useful to have around and I have searched for it on multiple occasions. --- osu.Game.Tests/Visual/{ => Navigation}/TestSceneOsuGame.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) rename osu.Game.Tests/Visual/{ => Navigation}/TestSceneOsuGame.cs (98%) diff --git a/osu.Game.Tests/Visual/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs similarity index 98% rename from osu.Game.Tests/Visual/TestSceneOsuGame.cs rename to osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs index c52d846a68..26641214b1 100644 --- a/osu.Game.Tests/Visual/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; using osu.Framework.Platform; -using osu.Framework.Testing; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Configuration; @@ -30,10 +29,9 @@ using osu.Game.Skinning; using osu.Game.Utils; using osuTK.Graphics; -namespace osu.Game.Tests.Visual +namespace osu.Game.Tests.Visual.Navigation { [TestFixture] - [HeadlessTest] public class TestSceneOsuGame : OsuTestScene { private IReadOnlyList requiredGameDependencies => new[] From 3f067e3a8d20ed33d8ab8f7f9434b3da6377fcc0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Aug 2021 16:59:42 +0900 Subject: [PATCH 0986/2442] Remove likely unnecessary score null check --- osu.Game/Screens/Play/Player.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 1692975210..09eaf1c543 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -962,7 +962,7 @@ namespace osu.Game.Screens.Play screenSuspension?.Expire(); // if arriving here and the results screen preparation task hasn't run, it's safe to say the user has not completed the beatmap. - if (Score != null && prepareScoreForDisplayTask == null) + if (prepareScoreForDisplayTask == null) { Score.ScoreInfo.Passed = false; // potentially should be ScoreRank.F instead? this is the best alternative for now. From e8ad0fba75f8bf6482a1a60e8ec18019c7adc30f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Aug 2021 17:20:41 +0900 Subject: [PATCH 0987/2442] Add required server methods for kicking users --- osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs | 8 ++++++++ osu.Game/Online/Multiplayer/MultiplayerClient.cs | 2 ++ osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs | 8 ++++++++ .../Tests/Visual/Multiplayer/TestMultiplayerClient.cs | 7 +++++++ 4 files changed, 25 insertions(+) diff --git a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs index b26c4d8201..da637c229f 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs @@ -27,6 +27,14 @@ namespace osu.Game.Online.Multiplayer /// If the user is not in a room. Task TransferHost(int userId); + /// + /// As the host, kick another user from the room. + /// + /// The user to kick.. + /// A user other than the current host is attempting to kick a user. + /// If the user is not in a room. + Task KickUser(int userId); + /// /// As the host, update the settings of the currently joined room. /// diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index dafc737ba2..4607211cdf 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -293,6 +293,8 @@ namespace osu.Game.Online.Multiplayer public abstract Task TransferHost(int userId); + public abstract Task KickUser(int userId); + public abstract Task ChangeSettings(MultiplayerRoomSettings settings); public abstract Task ChangeState(MultiplayerUserState newState); diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs index 8b8d10ce4f..55477a9fc7 100644 --- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs @@ -91,6 +91,14 @@ namespace osu.Game.Online.Multiplayer return connection.InvokeAsync(nameof(IMultiplayerServer.TransferHost), userId); } + public override Task KickUser(int userId) + { + if (!IsConnected.Value) + return Task.CompletedTask; + + return connection.InvokeAsync(nameof(IMultiplayerServer.KickUser), userId); + } + public override Task ChangeSettings(MultiplayerRoomSettings settings) { if (!IsConnected.Value) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index cffaea5c94..a28b4140ca 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -174,6 +174,13 @@ namespace osu.Game.Tests.Visual.Multiplayer public override Task TransferHost(int userId) => ((IMultiplayerClient)this).HostChanged(userId); + public override Task KickUser(int userId) + { + Debug.Assert(Room != null); + + return ((IMultiplayerClient)this).UserLeft(Room.Users.Single(u => u.UserID == userId)); + } + public override async Task ChangeSettings(MultiplayerRoomSettings settings) { Debug.Assert(Room != null); From 7aab8c32ecec37fe1c34e1f344bfd90eca3aa1ab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Aug 2021 17:20:47 +0900 Subject: [PATCH 0988/2442] Add kick button and hook up logic --- .../Participants/ParticipantPanel.cs | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs index 89431445d3..bfe99911b6 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; @@ -42,6 +43,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants private ModDisplay userModsDisplay; private StateDisplay userStateDisplay; + private IconButton kickButton; + public ParticipantPanel(MultiplayerRoomUser user) { User = user; @@ -64,7 +67,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants { new Dimension(GridSizeMode.Absolute, 18), new Dimension(GridSizeMode.AutoSize), - new Dimension() + new Dimension(), + new Dimension(GridSizeMode.AutoSize), }, Content = new[] { @@ -157,7 +161,20 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants Margin = new MarginPadding { Right = 10 }, } } - } + }, + kickButton = new KickButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Alpha = 0, + Margin = new MarginPadding(4), + Action = () => + { + Debug.Assert(user != null); + + Client.KickUser(user.Id); + } + }, }, } }; @@ -179,6 +196,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants userStateDisplay.UpdateStatus(User.State, User.BeatmapAvailability); + if (Client.LocalUser != null && Room.Host?.Equals(Client.LocalUser) == true) + kickButton.FadeIn(fade_time); + else + kickButton.FadeOut(fade_time); + if (Room.Host?.Equals(User) == true) crown.FadeIn(fade_time); else @@ -219,5 +241,19 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants }; } } + + public class KickButton : IconButton + { + public KickButton() + { + Icon = FontAwesome.Solid.UserTimes; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + IconHoverColour = colours.Red; + } + } } } From 50bda6023cf995258991598f8b02bc3ed9761767 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Aug 2021 17:31:37 +0900 Subject: [PATCH 0989/2442] Add test coverage --- .../TestSceneMultiplayerParticipantsList.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs index 6526f7eea7..b2fca61530 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs @@ -155,6 +155,42 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("second user crown visible", () => this.ChildrenOfType().ElementAt(1).ChildrenOfType().First().Alpha == 1); } + [Test] + public void TestKickButtonOnlyPresentWhenHost() + { + AddStep("add user", () => Client.AddUser(new User + { + Id = 3, + Username = "Second", + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", + })); + + AddUntilStep("kick buttons visible", () => this.ChildrenOfType().Count(d => d.IsPresent) == 2); + + AddStep("make second user host", () => Client.TransferHost(3)); + + AddUntilStep("kick buttons not visible", () => this.ChildrenOfType().Count(d => d.IsPresent) == 0); + + AddStep("make local user host again", () => Client.TransferHost(API.LocalUser.Value.Id)); + + AddUntilStep("kick buttons visible", () => this.ChildrenOfType().Count(d => d.IsPresent) == 2); + } + + [Test] + public void TestKickButtonKicks() + { + AddStep("add user", () => Client.AddUser(new User + { + Id = 3, + Username = "Second", + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", + })); + + AddStep("kick second user", () => this.ChildrenOfType().Last().TriggerClick()); + + AddAssert("second user kicked", () => Client.Room?.Users.Single().UserID == API.LocalUser.Value.Id); + } + [Test] public void TestManyUsers() { From bb51ebd0ef2c67dbe1978ac5edad128adc98c8e6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Aug 2021 17:39:23 +0900 Subject: [PATCH 0990/2442] Don't show button on self --- .../Multiplayer/TestSceneMultiplayerParticipantsList.cs | 6 +++--- .../OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs index b2fca61530..a3e6c8de3b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs @@ -165,7 +165,7 @@ namespace osu.Game.Tests.Visual.Multiplayer CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", })); - AddUntilStep("kick buttons visible", () => this.ChildrenOfType().Count(d => d.IsPresent) == 2); + AddUntilStep("kick buttons visible", () => this.ChildrenOfType().Count(d => d.IsPresent) == 1); AddStep("make second user host", () => Client.TransferHost(3)); @@ -173,7 +173,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("make local user host again", () => Client.TransferHost(API.LocalUser.Value.Id)); - AddUntilStep("kick buttons visible", () => this.ChildrenOfType().Count(d => d.IsPresent) == 2); + AddUntilStep("kick buttons visible", () => this.ChildrenOfType().Count(d => d.IsPresent) == 1); } [Test] @@ -186,7 +186,7 @@ namespace osu.Game.Tests.Visual.Multiplayer CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", })); - AddStep("kick second user", () => this.ChildrenOfType().Last().TriggerClick()); + AddStep("kick second user", () => this.ChildrenOfType().Single(d => d.IsPresent).TriggerClick()); AddAssert("second user kicked", () => Client.Room?.Users.Single().UserID == API.LocalUser.Value.Id); } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs index bfe99911b6..54baaceb36 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs @@ -184,7 +184,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants { base.OnRoomUpdated(); - if (Room == null) + if (Room == null || Client.LocalUser == null) return; const double fade_time = 50; @@ -196,7 +196,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants userStateDisplay.UpdateStatus(User.State, User.BeatmapAvailability); - if (Client.LocalUser != null && Room.Host?.Equals(Client.LocalUser) == true) + if (Client.IsHost && !User.Equals(Client.LocalUser)) kickButton.FadeIn(fade_time); else kickButton.FadeOut(fade_time); From d6352637d6e7ea99dbe02cbe1c920b7d14aa9a54 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Aug 2021 17:45:34 +0900 Subject: [PATCH 0991/2442] Also add tooltip and context menu item --- .../Multiplayer/Participants/ParticipantPanel.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs index 54baaceb36..1787480e1f 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs @@ -233,10 +233,18 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants new OsuMenuItem("Give host", MenuItemType.Standard, () => { // Ensure the local user is still host. - if (Room.Host?.UserID != api.LocalUser.Value.Id) + if (!Client.IsHost) return; Client.TransferHost(targetUser); + }), + new OsuMenuItem("Kick", MenuItemType.Destructive, () => + { + // Ensure the local user is still host. + if (!Client.IsHost) + return; + + Client.KickUser(targetUser); }) }; } @@ -247,6 +255,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants public KickButton() { Icon = FontAwesome.Solid.UserTimes; + TooltipText = "Kick"; } [BackgroundDependencyLoader] From eb59f3c59188d7ca86e42143a9d9f45802fdda53 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 11 Aug 2021 18:15:53 +0900 Subject: [PATCH 0992/2442] Revert "Buffer the entire star rating range to fix overlapping alpha" This reverts commit c680012523ff9f4a1e8ab10f788401a9f3b68986. --- .../Components/StarRatingRangeDisplay.cs | 57 +++++++++---------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs index 622643f5ea..01a21227df 100644 --- a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs @@ -33,41 +33,38 @@ namespace osu.Game.Screens.OnlinePlay.Components [BackgroundDependencyLoader] private void load() { - InternalChild = new BufferedContainer + InternalChildren = new Drawable[] { - AutoSizeAxes = Axes.Both, - Children = new Drawable[] + new Container { - new Container + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 1, + Children = new[] { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Vertical = 1 }, - Children = new[] + minBackground = new Box { - minBackground = new Box - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.5f), - }, - maxBackground = new Box - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.5f), - }, - } - }, - new FillFlowContainer + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.5f), + }, + maxBackground = new Box + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.5f), + }, + } + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Children = new Drawable[] { - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - minDisplay = new StarRatingDisplay(default), - maxDisplay = new StarRatingDisplay(default) - } + minDisplay = new StarRatingDisplay(default), + maxDisplay = new StarRatingDisplay(default) } } }; From 4002a1606eaf8db60d40370d63f2caf3ee3880a6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 11 Aug 2021 18:20:39 +0900 Subject: [PATCH 0993/2442] Round star ratings before comparing --- .../Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs index 01a21227df..a27b27b8ad 100644 --- a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs @@ -1,6 +1,7 @@ // 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.Specialized; using System.Linq; using osu.Framework.Allocation; @@ -86,7 +87,7 @@ namespace osu.Game.Screens.OnlinePlay.Components minDisplay.Current.Value = minDifficulty; maxDisplay.Current.Value = maxDifficulty; - maxDisplay.Alpha = Precision.AlmostEquals(minDifficulty.Stars, maxDifficulty.Stars) ? 0 : 1; + maxDisplay.Alpha = Precision.AlmostEquals(Math.Round(minDifficulty.Stars, 2), Math.Round(maxDifficulty.Stars, 2)) ? 0 : 1; minBackground.Colour = colours.ForStarDifficulty(minDifficulty.Stars); maxBackground.Colour = colours.ForStarDifficulty(maxDifficulty.Stars); From d9a4f018e65ce669a1fd329a3aa6880b5c94bdbd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Aug 2021 18:26:36 +0900 Subject: [PATCH 0994/2442] Add event flow for receiving kick commands --- osu.Game/Online/Multiplayer/IMultiplayerClient.cs | 9 +++++++++ osu.Game/Online/Multiplayer/MultiplayerClient.cs | 6 ++++++ osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs | 1 + 3 files changed, 16 insertions(+) diff --git a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs index 064065ab00..8f16d22c4c 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs @@ -31,6 +31,15 @@ namespace osu.Game.Online.Multiplayer /// The user. Task UserLeft(MultiplayerRoomUser user); + /// + /// Signals that a user has been kicked from the room. + /// + /// + /// This will also be sent to the user that was kicked. + /// + /// The user. + Task UserKicked(MultiplayerRoomUser user); + /// /// Signal that the host of the room has changed. /// diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 4607211cdf..7e964ed5fa 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -389,6 +389,12 @@ namespace osu.Game.Online.Multiplayer return Task.CompletedTask; } + Task IMultiplayerClient.UserKicked(MultiplayerRoomUser user) + { + // TODO: also inform users of the kick operation. + return ((IMultiplayerClient)this).UserLeft(user); + } + Task IMultiplayerClient.HostChanged(int userId) { if (Room == null) diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs index 55477a9fc7..c38a648a6a 100644 --- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs @@ -50,6 +50,7 @@ namespace osu.Game.Online.Multiplayer connection.On(nameof(IMultiplayerClient.RoomStateChanged), ((IMultiplayerClient)this).RoomStateChanged); connection.On(nameof(IMultiplayerClient.UserJoined), ((IMultiplayerClient)this).UserJoined); connection.On(nameof(IMultiplayerClient.UserLeft), ((IMultiplayerClient)this).UserLeft); + connection.On(nameof(IMultiplayerClient.UserKicked), ((IMultiplayerClient)this).UserKicked); connection.On(nameof(IMultiplayerClient.HostChanged), ((IMultiplayerClient)this).HostChanged); connection.On(nameof(IMultiplayerClient.SettingsChanged), ((IMultiplayerClient)this).SettingsChanged); connection.On(nameof(IMultiplayerClient.UserStateChanged), ((IMultiplayerClient)this).UserStateChanged); From 9b21ebd6d04410d2e5fcd363cf26b4ee88a0bfec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Aug 2021 18:31:04 +0900 Subject: [PATCH 0995/2442] Add client side handling on incoming kick --- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 7e964ed5fa..57dbfbb507 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -190,7 +190,8 @@ namespace osu.Game.Online.Multiplayer return joinOrLeaveTaskChain.Add(async () => { await scheduledReset.ConfigureAwait(false); - await LeaveRoomInternal().ConfigureAwait(false); + if (Room != null) + await LeaveRoomInternal().ConfigureAwait(false); }); } @@ -391,6 +392,12 @@ namespace osu.Game.Online.Multiplayer Task IMultiplayerClient.UserKicked(MultiplayerRoomUser user) { + if (LocalUser == null) + return Task.CompletedTask; + + if (user.Equals(LocalUser)) + LeaveRoom(); + // TODO: also inform users of the kick operation. return ((IMultiplayerClient)this).UserLeft(user); } From 31608a1bc6c4a691571967dd1e893a7ac4338ee9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Aug 2021 19:56:25 +0900 Subject: [PATCH 0996/2442] Leave the match screen when kicked --- .../OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 561fa220c8..57dde31b68 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -448,6 +448,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private void onRoomUpdated() { + // may happen if the client is kicked or otherwise removed from the room. + if (client.Room == null) + { + Schedule(this.Exit); + return; + } + Scheduler.AddOnce(UpdateMods); } From a1f50e39aa12d86456af684804c0894ec1ccf871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 25 Jul 2021 16:50:52 +0200 Subject: [PATCH 0997/2442] Add basic structure for skinning catch explosions --- .../TestSceneCatcherArea.cs | 1 + .../CatchSkinComponents.cs | 3 +- .../Skinning/Default/DefaultHitExplosion.cs | 145 ++++++++++++++++++ osu.Game.Rulesets.Catch/UI/HitExplosion.cs | 121 ++------------- .../UI/HitExplosionContainer.cs | 3 + 5 files changed, 168 insertions(+), 105 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Skinning/Default/DefaultHitExplosion.cs diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs index a3307c9224..6abfbdbe21 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs @@ -40,6 +40,7 @@ namespace osu.Game.Rulesets.Catch.Tests { AddSliderStep("circle size", 0, 8, 5, createCatcher); AddToggleStep("hyper dash", t => this.ChildrenOfType().ForEach(area => area.ToggleHyperDash(t))); + AddToggleStep("toggle hit lighting", lighting => config.SetValue(OsuSetting.HitLighting, lighting)); AddStep("catch centered fruit", () => attemptCatch(new Fruit())); AddStep("catch many random fruit", () => diff --git a/osu.Game.Rulesets.Catch/CatchSkinComponents.cs b/osu.Game.Rulesets.Catch/CatchSkinComponents.cs index e736d68740..371e901c69 100644 --- a/osu.Game.Rulesets.Catch/CatchSkinComponents.cs +++ b/osu.Game.Rulesets.Catch/CatchSkinComponents.cs @@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Catch Banana, Droplet, Catcher, - CatchComboCounter + CatchComboCounter, + HitExplosion } } diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/DefaultHitExplosion.cs b/osu.Game.Rulesets.Catch/Skinning/Default/DefaultHitExplosion.cs new file mode 100644 index 0000000000..41ba81d34c --- /dev/null +++ b/osu.Game.Rulesets.Catch/Skinning/Default/DefaultHitExplosion.cs @@ -0,0 +1,145 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; +using osu.Framework.Utils; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Utils; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Catch.Skinning.Default +{ + public class DefaultHitExplosion : CompositeDrawable + { + [Resolved] + private Bindable entryBindable { get; set; } + + private CircularContainer largeFaint; + private CircularContainer smallFaint; + private CircularContainer directionalGlow1; + private CircularContainer directionalGlow2; + + [BackgroundDependencyLoader] + private void load() + { + Size = new Vector2(20); + Anchor = Anchor.BottomCentre; + Origin = Anchor.BottomCentre; + + // scale roughly in-line with visual appearance of notes + const float initial_height = 10; + + InternalChildren = new Drawable[] + { + largeFaint = new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + Blending = BlendingParameters.Additive, + }, + smallFaint = new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + Blending = BlendingParameters.Additive, + }, + directionalGlow1 = new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + Size = new Vector2(0.01f, initial_height), + Blending = BlendingParameters.Additive, + }, + directionalGlow2 = new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + Size = new Vector2(0.01f, initial_height), + Blending = BlendingParameters.Additive, + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + entryBindable.BindValueChanged(entry => apply(entry.NewValue), true); + } + + private void apply(HitExplosionEntry entry) + { + if (entry == null) + return; + + X = entry.Position; + Scale = new Vector2(entry.Scale); + setColour(entry.ObjectColour); + + using (BeginAbsoluteSequence(entry.LifetimeStart)) + applyTransforms(entry.RNGSeed); + } + + private void applyTransforms(int randomSeed) + { + ClearTransforms(true); + + const double duration = 400; + + // we want our size to be very small so the glow dominates it. + largeFaint.Size = new Vector2(0.8f); + largeFaint + .ResizeTo(largeFaint.Size * new Vector2(5, 1), duration, Easing.OutQuint) + .FadeOut(duration * 2); + + const float angle_variangle = 15; // should be less than 45 + directionalGlow1.Rotation = StatelessRNG.NextSingle(-angle_variangle, angle_variangle, randomSeed, 4); + directionalGlow2.Rotation = StatelessRNG.NextSingle(-angle_variangle, angle_variangle, randomSeed, 5); + + this.FadeInFromZero(50).Then().FadeOut(duration, Easing.Out).Expire(); + } + + private void setColour(Color4 objectColour) + { + const float roundness = 100; + + largeFaint.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = Interpolation.ValueAt(0.1f, objectColour, Color4.White, 0, 1).Opacity(0.3f), + Roundness = 160, + Radius = 200, + }; + + smallFaint.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = Interpolation.ValueAt(0.6f, objectColour, Color4.White, 0, 1), + Roundness = 20, + Radius = 50, + }; + + directionalGlow1.EdgeEffect = directionalGlow2.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = Interpolation.ValueAt(0.4f, objectColour, Color4.White, 0, 1), + Roundness = roundness, + Radius = 40, + }; + } + } +} diff --git a/osu.Game.Rulesets.Catch/UI/HitExplosion.cs b/osu.Game.Rulesets.Catch/UI/HitExplosion.cs index d9ab428231..7f3b1619ce 100644 --- a/osu.Game.Rulesets.Catch/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Catch/UI/HitExplosion.cs @@ -1,129 +1,42 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Effects; -using osu.Framework.Utils; +using osu.Game.Rulesets.Catch.Skinning.Default; using osu.Game.Rulesets.Objects.Pooling; -using osu.Game.Utils; -using osuTK; -using osuTK.Graphics; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch.UI { public class HitExplosion : PoolableDrawableWithLifetime { - private readonly CircularContainer largeFaint; - private readonly CircularContainer smallFaint; - private readonly CircularContainer directionalGlow1; - private readonly CircularContainer directionalGlow2; + [Cached] + private Bindable bindableEntry { get; set; } = new Bindable(); public HitExplosion() { - Size = new Vector2(20); - Anchor = Anchor.TopCentre; + RelativeSizeAxes = Axes.Both; + Anchor = Anchor.BottomCentre; Origin = Anchor.BottomCentre; + } - // scale roughly in-line with visual appearance of notes - const float initial_height = 10; - - InternalChildren = new Drawable[] + [BackgroundDependencyLoader] + private void load() + { + InternalChild = new SkinnableDrawable(new CatchSkinComponent(CatchSkinComponents.HitExplosion), _ => new DefaultHitExplosion()) { - largeFaint = new CircularContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Masking = true, - Blending = BlendingParameters.Additive, - }, - smallFaint = new CircularContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Masking = true, - Blending = BlendingParameters.Additive, - }, - directionalGlow1 = new CircularContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Masking = true, - Size = new Vector2(0.01f, initial_height), - Blending = BlendingParameters.Additive, - }, - directionalGlow2 = new CircularContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Masking = true, - Size = new Vector2(0.01f, initial_height), - Blending = BlendingParameters.Additive, - } + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre }; } protected override void OnApply(HitExplosionEntry entry) { - X = entry.Position; - Scale = new Vector2(entry.Scale); - setColour(entry.ObjectColour); + base.OnApply(entry); - using (BeginAbsoluteSequence(entry.LifetimeStart)) - applyTransforms(entry.RNGSeed); - } - - private void applyTransforms(int randomSeed) - { - ClearTransforms(true); - - const double duration = 400; - - // we want our size to be very small so the glow dominates it. - largeFaint.Size = new Vector2(0.8f); - largeFaint - .ResizeTo(largeFaint.Size * new Vector2(5, 1), duration, Easing.OutQuint) - .FadeOut(duration * 2); - - const float angle_variangle = 15; // should be less than 45 - directionalGlow1.Rotation = StatelessRNG.NextSingle(-angle_variangle, angle_variangle, randomSeed, 4); - directionalGlow2.Rotation = StatelessRNG.NextSingle(-angle_variangle, angle_variangle, randomSeed, 5); - - this.FadeInFromZero(50).Then().FadeOut(duration, Easing.Out).Expire(); - } - - private void setColour(Color4 objectColour) - { - const float roundness = 100; - - largeFaint.EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = Interpolation.ValueAt(0.1f, objectColour, Color4.White, 0, 1).Opacity(0.3f), - Roundness = 160, - Radius = 200, - }; - - smallFaint.EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = Interpolation.ValueAt(0.6f, objectColour, Color4.White, 0, 1), - Roundness = 20, - Radius = 50, - }; - - directionalGlow1.EdgeEffect = directionalGlow2.EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = Interpolation.ValueAt(0.4f, objectColour, Color4.White, 0, 1), - Roundness = roundness, - Radius = 40, - }; + bindableEntry.Value = entry; } } } diff --git a/osu.Game.Rulesets.Catch/UI/HitExplosionContainer.cs b/osu.Game.Rulesets.Catch/UI/HitExplosionContainer.cs index 094d88243a..6df13e52ef 100644 --- a/osu.Game.Rulesets.Catch/UI/HitExplosionContainer.cs +++ b/osu.Game.Rulesets.Catch/UI/HitExplosionContainer.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Graphics; using osu.Framework.Graphics.Pooling; using osu.Game.Rulesets.Objects.Pooling; @@ -14,6 +15,8 @@ namespace osu.Game.Rulesets.Catch.UI public HitExplosionContainer() { + RelativeSizeAxes = Axes.Both; + AddInternal(pool = new DrawablePool(10)); } From 95a58ca3666dfe785034741caa6d4f5ac39b9170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 25 Jul 2021 17:19:51 +0200 Subject: [PATCH 0998/2442] Store judgement directly in hit explosion entry --- .../Skinning/Default/DefaultHitExplosion.cs | 4 +-- osu.Game.Rulesets.Catch/UI/Catcher.cs | 6 ++-- .../UI/HitExplosionEntry.cs | 30 ++++++++++++++----- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/DefaultHitExplosion.cs b/osu.Game.Rulesets.Catch/Skinning/Default/DefaultHitExplosion.cs index 41ba81d34c..f4b952c559 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/DefaultHitExplosion.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/DefaultHitExplosion.cs @@ -87,11 +87,11 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default return; X = entry.Position; - Scale = new Vector2(entry.Scale); + Scale = new Vector2(entry.HitObject.Scale); setColour(entry.ObjectColour); using (BeginAbsoluteSequence(entry.LifetimeStart)) - applyTransforms(entry.RNGSeed); + applyTransforms(entry.HitObject.RandomSeed); } private void applyTransforms(int randomSeed) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 9fd4610e6e..a1aa58f163 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -216,7 +216,7 @@ namespace osu.Game.Rulesets.Catch.UI placeCaughtObject(palpableObject, positionInStack); if (hitLighting.Value) - addLighting(hitObject, positionInStack.X, drawableObject.AccentColour.Value); + addLighting(result, drawableObject.AccentColour.Value, positionInStack.X); } // droplet doesn't affect the catcher state @@ -365,8 +365,8 @@ namespace osu.Game.Rulesets.Catch.UI return position; } - private void addLighting(CatchHitObject hitObject, float x, Color4 colour) => - hitExplosionContainer.Add(new HitExplosionEntry(Time.Current, x, hitObject.Scale, colour, hitObject.RandomSeed)); + private void addLighting(JudgementResult judgementResult, Color4 colour, float x) => + hitExplosionContainer.Add(new HitExplosionEntry(judgementResult, colour, x, Time.Current)); private CaughtObject getCaughtObject(PalpableCatchHitObject source) { diff --git a/osu.Game.Rulesets.Catch/UI/HitExplosionEntry.cs b/osu.Game.Rulesets.Catch/UI/HitExplosionEntry.cs index b142962a8a..749a448314 100644 --- a/osu.Game.Rulesets.Catch/UI/HitExplosionEntry.cs +++ b/osu.Game.Rulesets.Catch/UI/HitExplosionEntry.cs @@ -2,24 +2,40 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics.Performance; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Judgements; using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.UI { public class HitExplosionEntry : LifetimeEntry { - public readonly float Position; - public readonly float Scale; - public readonly Color4 ObjectColour; - public readonly int RNGSeed; + /// + /// The judgement result that triggered this explosion. + /// + public JudgementResult JudgementResult { get; } - public HitExplosionEntry(double startTime, float position, float scale, Color4 objectColour, int rngSeed) + /// + /// The hitobject which triggered this explosion. + /// + public CatchHitObject HitObject => (CatchHitObject)JudgementResult.HitObject; + + /// + /// The accent colour of the object caught. + /// + public Color4 ObjectColour { get; } + + /// + /// The position at which the object was caught. + /// + public float Position { get; } + + public HitExplosionEntry(JudgementResult judgementResult, Color4 objectColour, float position, double startTime) { LifetimeStart = startTime; Position = position; - Scale = scale; + JudgementResult = judgementResult; ObjectColour = objectColour; - RNGSeed = rngSeed; } } } From 8c8a64fe6eafc5452c2f63c3b0c464bc025389bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 25 Jul 2021 19:00:37 +0200 Subject: [PATCH 0999/2442] Add legacy hit lighting implementation --- .../Legacy/CatchLegacySkinTransformer.cs | 20 +++- .../Skinning/Legacy/LegacyHitExplosion.cs | 103 ++++++++++++++++++ osu.Game.Rulesets.Catch/UI/Catcher.cs | 7 +- osu.Game.Rulesets.Catch/UI/HitExplosion.cs | 1 + 4 files changed, 124 insertions(+), 7 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index 5e744ec001..10fc4e78b2 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -70,13 +70,11 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy if (version < 2.3m) { - if (GetTexture(@"fruit-ryuuta") != null || - GetTexture(@"fruit-ryuuta-0") != null) + if (hasOldStyleCatcherSprite()) return new LegacyCatcherOld(); } - if (GetTexture(@"fruit-catcher-idle") != null || - GetTexture(@"fruit-catcher-idle-0") != null) + if (hasNewStyleCatcherSprite()) return new LegacyCatcherNew(); return null; @@ -86,12 +84,26 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy return new LegacyCatchComboCounter(Skin); return null; + + case CatchSkinComponents.HitExplosion: + if (hasOldStyleCatcherSprite() || hasNewStyleCatcherSprite()) + return new LegacyHitExplosion(); + + return null; } } return base.GetDrawableComponent(component); } + private bool hasOldStyleCatcherSprite() => + GetTexture(@"fruit-ryuuta") != null + || GetTexture(@"fruit-ryuuta-0") != null; + + private bool hasNewStyleCatcherSprite() => + GetTexture(@"fruit-catcher-idle") != null + || GetTexture(@"fruit-catcher-idle-0") != null; + public override IBindable GetConfig(TLookup lookup) { switch (lookup) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs new file mode 100644 index 0000000000..339055c91d --- /dev/null +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs @@ -0,0 +1,103 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Skinning; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Skinning.Legacy +{ + public class LegacyHitExplosion : CompositeDrawable + { + [Resolved] + private Catcher catcher { get; set; } + + [Resolved] + private Bindable entryBindable { get; set; } + + private const float catch_margin = (1 - Catcher.ALLOWED_CATCH_RANGE) / 2; + + private readonly Sprite explosion1; + private readonly Sprite explosion2; + + public LegacyHitExplosion() + { + Anchor = Anchor.BottomCentre; + Origin = Anchor.BottomCentre; + RelativeSizeAxes = Axes.Both; + Scale = new Vector2(0.4f); + + InternalChildren = new[] + { + explosion1 = new Sprite + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.CentreLeft, + Blending = BlendingParameters.Additive, + Rotation = -90 + }, + explosion2 = new Sprite + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.CentreLeft, + Blending = BlendingParameters.Additive, + Rotation = -90 + } + }; + } + + [BackgroundDependencyLoader] + private void load(SkinManager skins) + { + var defaultLegacySkin = skins.DefaultLegacySkin; + + explosion1.Texture = defaultLegacySkin.GetTexture("scoreboard-explosion-2"); + explosion2.Texture = defaultLegacySkin.GetTexture("scoreboard-explosion-1"); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + entryBindable.BindValueChanged(entry => apply(entry.NewValue), true); + } + + private void apply(HitExplosionEntry entry) + { + if (entry == null) + return; + + Colour = entry.ObjectColour; + + using (BeginAbsoluteSequence(entry.LifetimeStart)) + { + float halfCatchWidth = catcher.CatchWidth / 2; + float explosionOffset = Math.Clamp(entry.Position, -halfCatchWidth + catch_margin * 3, halfCatchWidth - catch_margin * 3); + + if (!(entry.HitObject is Droplet)) + { + float scale = Math.Clamp(entry.JudgementResult.ComboAtJudgement / 200f, 0.35f, 1.125f); + + explosion1.Scale = new Vector2(1, 0.9f); + explosion1.Position = new Vector2(explosionOffset, 0); + + explosion1.FadeOut(300); + explosion1.ScaleTo(new Vector2(20 * scale, 1.1f), 160, Easing.Out); + } + + explosion2.Scale = new Vector2(0.9f, 1); + explosion2.Position = new Vector2(explosionOffset, 0); + + explosion2.FadeOut(700); + explosion2.ScaleTo(new Vector2(0.9f, 1.3f), 500, Easing.Out); + } + } + } +} diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index a1aa58f163..aec8e752a7 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -23,6 +23,7 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.UI { + [Cached] public class Catcher : SkinReloadableDrawable { /// @@ -106,7 +107,7 @@ namespace osu.Game.Rulesets.Catch.UI /// /// Width of the area that can be used to attempt catches during gameplay. /// - private readonly float catchWidth; + public readonly float CatchWidth; private readonly SkinnableCatcher body; @@ -133,7 +134,7 @@ namespace osu.Game.Rulesets.Catch.UI if (difficulty != null) Scale = calculateScale(difficulty); - catchWidth = CalculateCatchWidth(Scale); + CatchWidth = CalculateCatchWidth(Scale); InternalChildren = new Drawable[] { @@ -193,7 +194,7 @@ namespace osu.Game.Rulesets.Catch.UI if (!(hitObject is PalpableCatchHitObject fruit)) return false; - float halfCatchWidth = catchWidth * 0.5f; + float halfCatchWidth = CatchWidth * 0.5f; return fruit.EffectiveX >= X - halfCatchWidth && fruit.EffectiveX <= X + halfCatchWidth; } diff --git a/osu.Game.Rulesets.Catch/UI/HitExplosion.cs b/osu.Game.Rulesets.Catch/UI/HitExplosion.cs index 7f3b1619ce..671b14640d 100644 --- a/osu.Game.Rulesets.Catch/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Catch/UI/HitExplosion.cs @@ -27,6 +27,7 @@ namespace osu.Game.Rulesets.Catch.UI { InternalChild = new SkinnableDrawable(new CatchSkinComponent(CatchSkinComponents.HitExplosion), _ => new DefaultHitExplosion()) { + CentreComponent = false, Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre }; From 4bcbe6ac90def30bda7da29052b748bc5a2b1359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 7 Aug 2021 18:19:29 +0200 Subject: [PATCH 1000/2442] Restructure explosion to ensure proper lifetime bounds --- .../Skinning/Default/DefaultHitExplosion.cs | 17 +++------------ .../Skinning/Legacy/LegacyHitExplosion.cs | 21 ++++++------------- osu.Game.Rulesets.Catch/UI/HitExplosion.cs | 12 ++++++----- osu.Game.Rulesets.Catch/UI/IHitExplosion.cs | 16 ++++++++++++++ 4 files changed, 32 insertions(+), 34 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/UI/IHitExplosion.cs diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/DefaultHitExplosion.cs b/osu.Game.Rulesets.Catch/Skinning/Default/DefaultHitExplosion.cs index f4b952c559..2660e0730d 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/DefaultHitExplosion.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/DefaultHitExplosion.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -15,11 +14,8 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Skinning.Default { - public class DefaultHitExplosion : CompositeDrawable + public class DefaultHitExplosion : CompositeDrawable, IHitExplosion { - [Resolved] - private Bindable entryBindable { get; set; } - private CircularContainer largeFaint; private CircularContainer smallFaint; private CircularContainer directionalGlow1; @@ -74,14 +70,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default }; } - protected override void LoadComplete() - { - base.LoadComplete(); - - entryBindable.BindValueChanged(entry => apply(entry.NewValue), true); - } - - private void apply(HitExplosionEntry entry) + public void Animate(HitExplosionEntry entry) { if (entry == null) return; @@ -110,7 +99,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default directionalGlow1.Rotation = StatelessRNG.NextSingle(-angle_variangle, angle_variangle, randomSeed, 4); directionalGlow2.Rotation = StatelessRNG.NextSingle(-angle_variangle, angle_variangle, randomSeed, 5); - this.FadeInFromZero(50).Then().FadeOut(duration, Easing.Out).Expire(); + this.FadeInFromZero(50).Then().FadeOut(duration, Easing.Out); } private void setColour(Color4 objectColour) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs index 339055c91d..6708989b89 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs @@ -3,7 +3,6 @@ using System; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -14,14 +13,11 @@ using osuTK; namespace osu.Game.Rulesets.Catch.Skinning.Legacy { - public class LegacyHitExplosion : CompositeDrawable + public class LegacyHitExplosion : CompositeDrawable, IHitExplosion { [Resolved] private Catcher catcher { get; set; } - [Resolved] - private Bindable entryBindable { get; set; } - private const float catch_margin = (1 - Catcher.ALLOWED_CATCH_RANGE) / 2; private readonly Sprite explosion1; @@ -62,14 +58,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy explosion2.Texture = defaultLegacySkin.GetTexture("scoreboard-explosion-1"); } - protected override void LoadComplete() - { - base.LoadComplete(); - - entryBindable.BindValueChanged(entry => apply(entry.NewValue), true); - } - - private void apply(HitExplosionEntry entry) + public void Animate(HitExplosionEntry entry) { if (entry == null) return; @@ -88,15 +77,17 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy explosion1.Scale = new Vector2(1, 0.9f); explosion1.Position = new Vector2(explosionOffset, 0); - explosion1.FadeOut(300); + explosion1.FadeOutFromOne(300); explosion1.ScaleTo(new Vector2(20 * scale, 1.1f), 160, Easing.Out); } explosion2.Scale = new Vector2(0.9f, 1); explosion2.Position = new Vector2(explosionOffset, 0); - explosion2.FadeOut(700); + explosion2.FadeOutFromOne(700); explosion2.ScaleTo(new Vector2(0.9f, 1.3f), 500, Easing.Out); + + this.Delay(700).FadeOutFromOne(); } } } diff --git a/osu.Game.Rulesets.Catch/UI/HitExplosion.cs b/osu.Game.Rulesets.Catch/UI/HitExplosion.cs index 671b14640d..0a5341cb67 100644 --- a/osu.Game.Rulesets.Catch/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Catch/UI/HitExplosion.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Rulesets.Catch.Skinning.Default; using osu.Game.Rulesets.Objects.Pooling; @@ -12,8 +11,7 @@ namespace osu.Game.Rulesets.Catch.UI { public class HitExplosion : PoolableDrawableWithLifetime { - [Cached] - private Bindable bindableEntry { get; set; } = new Bindable(); + private SkinnableDrawable skinnableExplosion; public HitExplosion() { @@ -25,7 +23,7 @@ namespace osu.Game.Rulesets.Catch.UI [BackgroundDependencyLoader] private void load() { - InternalChild = new SkinnableDrawable(new CatchSkinComponent(CatchSkinComponents.HitExplosion), _ => new DefaultHitExplosion()) + InternalChild = skinnableExplosion = new SkinnableDrawable(new CatchSkinComponent(CatchSkinComponents.HitExplosion), _ => new DefaultHitExplosion()) { CentreComponent = false, Anchor = Anchor.BottomCentre, @@ -37,7 +35,11 @@ namespace osu.Game.Rulesets.Catch.UI { base.OnApply(entry); - bindableEntry.Value = entry; + ApplyTransformsAt(double.MinValue, true); + ClearTransforms(true); + + (skinnableExplosion.Drawable as IHitExplosion)?.Animate(entry); + LifetimeEnd = skinnableExplosion.Drawable.LatestTransformEndTime; } } } diff --git a/osu.Game.Rulesets.Catch/UI/IHitExplosion.cs b/osu.Game.Rulesets.Catch/UI/IHitExplosion.cs new file mode 100644 index 0000000000..4a9d7e8ac0 --- /dev/null +++ b/osu.Game.Rulesets.Catch/UI/IHitExplosion.cs @@ -0,0 +1,16 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Catch.UI +{ + /// + /// Common interface for all hit explosion skinnables. + /// + public interface IHitExplosion + { + /// + /// Begins animating this . + /// + void Animate(HitExplosionEntry entry); + } +} From 2fb19210af31d7aba85348c4b8bc3f023f0e61da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 Aug 2021 22:36:27 +0200 Subject: [PATCH 1001/2442] Fix legacy explosion sprites incorrectly showing after skin change --- osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs index 6708989b89..78b0e1e327 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs @@ -36,6 +36,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy { Anchor = Anchor.BottomCentre, Origin = Anchor.CentreLeft, + Alpha = 0, Blending = BlendingParameters.Additive, Rotation = -90 }, @@ -43,6 +44,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy { Anchor = Anchor.BottomCentre, Origin = Anchor.CentreLeft, + Alpha = 0, Blending = BlendingParameters.Additive, Rotation = -90 } From 427a88940c56988484a17c0e0020613243be9083 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 Aug 2021 23:18:42 +0200 Subject: [PATCH 1002/2442] Remove duplicated `ClearTransforms()` call --- osu.Game.Rulesets.Catch/Skinning/Default/DefaultHitExplosion.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/DefaultHitExplosion.cs b/osu.Game.Rulesets.Catch/Skinning/Default/DefaultHitExplosion.cs index 2660e0730d..680fbc7b15 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/DefaultHitExplosion.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/DefaultHitExplosion.cs @@ -85,8 +85,6 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default private void applyTransforms(int randomSeed) { - ClearTransforms(true); - const double duration = 400; // we want our size to be very small so the glow dominates it. From 98ce69d1d32b65abd9983dbd1124cad8f6bc938b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 Aug 2021 23:32:58 +0200 Subject: [PATCH 1003/2442] Fix explosion reading out time values from wrong clock --- osu.Game.Rulesets.Catch/UI/HitExplosion.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game.Rulesets.Catch/UI/HitExplosion.cs b/osu.Game.Rulesets.Catch/UI/HitExplosion.cs index 0a5341cb67..35af39348d 100644 --- a/osu.Game.Rulesets.Catch/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Catch/UI/HitExplosion.cs @@ -34,7 +34,18 @@ namespace osu.Game.Rulesets.Catch.UI protected override void OnApply(HitExplosionEntry entry) { base.OnApply(entry); + if (IsLoaded) + apply(entry); + } + protected override void LoadComplete() + { + base.LoadComplete(); + apply(Entry); + } + + private void apply(HitExplosionEntry entry) + { ApplyTransformsAt(double.MinValue, true); ClearTransforms(true); From b06226e7385cbae56dce1729594003f5a5502cd4 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Thu, 12 Aug 2021 09:54:25 +1000 Subject: [PATCH 1004/2442] Change comments --- .../Difficulty/OsuPerformanceCalculator.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 2ca5145c6a..2c8ee93819 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty countMiss = Score.Statistics.GetValueOrDefault(HitResult.Miss); // Custom multipliers for NoFail and SpunOut. - double multiplier = 1.12; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things + double multiplier = 1.12; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things. if (mods.Any(m => m is OsuModNoFail)) multiplier *= Math.Max(0.90, 1.0 - 0.02 * countMiss); @@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty double aimValue = Math.Pow(5.0 * Math.Max(1.0, rawAim / 0.0675) - 4.0, 3.0) / 100000.0; - // Longer maps are worth more + // Longer maps are worth more. double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) + (totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0); @@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (countMiss > 0) aimValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), countMiss); - // Combo scaling + // Combo scaling. if (Attributes.MaxCombo > 0) aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); @@ -114,9 +114,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty aimValue *= approachRateBonus; - // Scale the aim value with accuracy _slightly_ + // Scale the aim value with accuracy _slightly_. aimValue *= 0.5 + accuracy / 2.0; - // It is important to also consider accuracy difficulty when doing that + // It is important to also consider accuracy difficulty when doing that. aimValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500; return aimValue; @@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty { double speedValue = Math.Pow(5.0 * Math.Max(1.0, Attributes.SpeedStrain / 0.0675) - 4.0, 3.0) / 100000.0; - // Longer maps are worth more + // Longer maps are worth more. double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) + (totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0); speedValue *= lengthBonus; @@ -135,7 +135,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (countMiss > 0) speedValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), Math.Pow(countMiss, .875)); - // Combo scaling + // Combo scaling. if (Attributes.MaxCombo > 0) speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); @@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(m => m is OsuModHidden)) speedValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); - // Scale the speed value with accuracy and OD + // Scale the speed value with accuracy and OD. speedValue *= (0.95 + Math.Pow(Attributes.OverallDifficulty, 2) / 750) * Math.Pow(accuracy, (14.5 - Math.Max(Attributes.OverallDifficulty, 8)) / 2); // Scale the speed value with # of 50s to punish doubletapping. speedValue *= Math.Pow(0.98, countMeh < totalHits / 500.0 ? 0 : countMeh - totalHits / 500.0); @@ -160,7 +160,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty private double computeAccuracyValue() { - // This percentage only considers HitCircles of any value - in this part of the calculation we focus on hitting the timing hit window + // This percentage only considers HitCircles of any value - in this part of the calculation we focus on hitting the timing hit window. double betterAccuracyPercentage; int amountHitObjectsWithAccuracy = Attributes.HitCircleCount; @@ -169,15 +169,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty else betterAccuracyPercentage = 0; - // It is possible to reach a negative accuracy with this formula. Cap it at zero - zero points + // It is possible to reach a negative accuracy with this formula. Cap it at zero - zero points. if (betterAccuracyPercentage < 0) betterAccuracyPercentage = 0; // Lots of arbitrary values from testing. - // Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution + // Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution. double accuracyValue = Math.Pow(1.52163, Attributes.OverallDifficulty) * Math.Pow(betterAccuracyPercentage, 24) * 2.83; - // Bonus for many hitcircles - it's harder to keep good accuracy up for longer + // Bonus for many hitcircles - it's harder to keep good accuracy up for longer. accuracyValue *= Math.Min(1.15, Math.Pow(amountHitObjectsWithAccuracy / 1000.0, 0.3)); if (mods.Any(m => m is OsuModHidden)) @@ -209,7 +209,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (countMiss > 0) flashlightValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), Math.Pow(countMiss, .875)); - // Combo scaling + // Combo scaling. if (Attributes.MaxCombo > 0) flashlightValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); @@ -217,9 +217,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty flashlightValue *= 0.5 + 0.15 * Math.Min(1.0, totalHits / 200.0) + (totalHits > 200 ? 0.35 * Math.Min(1.0, (totalHits - 200) / 600.0) : 0.0); - // Scale the aim value with accuracy _slightly_ + // Scale the flashlight value with accuracy _slightly_. flashlightValue *= 0.5 + accuracy / 2.0; - // It is important to also consider accuracy difficulty when doing that + // It is important to also consider accuracy difficulty when doing that. flashlightValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500; } From eaa0d383158facbf8b779b17010ba9c8b0fcd00b Mon Sep 17 00:00:00 2001 From: MBmasher Date: Thu, 12 Aug 2021 10:00:24 +1000 Subject: [PATCH 1005/2442] Add a buff to short maps for Flashlight skill --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 2c8ee93819..ad7376a044 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -214,8 +214,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty flashlightValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); // Account for shorter maps having a higher ratio of 0 combo/100 combo flashlight radius. - flashlightValue *= 0.5 + 0.15 * Math.Min(1.0, totalHits / 200.0) + - (totalHits > 200 ? 0.35 * Math.Min(1.0, (totalHits - 200) / 600.0) : 0.0); + flashlightValue *= 0.7 + 0.1 * Math.Min(1.0, totalHits / 200.0) + + (totalHits > 200 ? 0.2 * Math.Min(1.0, (totalHits - 200) / 200.0) : 0.0); // Scale the flashlight value with accuracy _slightly_. flashlightValue *= 0.5 + accuracy / 2.0; From 58d76e903618e640fe97d9b341959cca33804940 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Aug 2021 09:13:10 +0900 Subject: [PATCH 1006/2442] Use FinishTransforms() --- .../Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs index dfe7ff8cac..052ad35dd7 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs @@ -21,7 +21,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components [Resolved] private OsuColour colours { get; set; } - private bool firstDisplay = true; private PillContainer pill; private SpriteText statusText; @@ -51,6 +50,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components EndDate.BindValueChanged(_ => updateDisplay()); Status.BindValueChanged(_ => updateDisplay(), true); + + FinishTransforms(true); } private void updateDisplay() @@ -58,10 +59,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components RoomStatus status = getDisplayStatus(); pill.Background.Alpha = 1; - pill.Background.FadeColour(status.GetAppropriateColour(colours), firstDisplay ? 0 : 100); + pill.Background.FadeColour(status.GetAppropriateColour(colours), 100); statusText.Text = status.Message; - - firstDisplay = false; } private RoomStatus getDisplayStatus() From bbb28d1b2984c2478414e068830aa54c9b4beb03 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Aug 2021 09:14:46 +0900 Subject: [PATCH 1007/2442] Don't use null-propagation for status --- osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs index 052ad35dd7..1d43f2dc65 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs @@ -68,7 +68,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components if (EndDate.Value < DateTimeOffset.Now) return new RoomStatusEnded(); - return Status.Value ?? new RoomStatusOpen(); + return Status.Value; } } } From fbaa480b3e21808f15e6e3cbef7262e440f2ca66 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Aug 2021 10:08:54 +0900 Subject: [PATCH 1008/2442] Fix out-of-order hits in editor causing exceptions --- .../Edit/DrawableOsuEditorRuleset.cs | 5 +++++ osu.Game.Rulesets.Osu/UI/AnyOrderHitPolicy.cs | 22 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/UI/AnyOrderHitPolicy.cs diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs index 0e61c02e2d..d4f1602a46 100644 --- a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs +++ b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs @@ -41,6 +41,11 @@ namespace osu.Game.Rulesets.Osu.Edit protected override GameplayCursorContainer CreateCursor() => null; + public OsuEditorPlayfield() + { + HitPolicy = new AnyOrderHitPolicy(); + } + [BackgroundDependencyLoader] private void load(OsuConfigManager config) { diff --git a/osu.Game.Rulesets.Osu/UI/AnyOrderHitPolicy.cs b/osu.Game.Rulesets.Osu/UI/AnyOrderHitPolicy.cs new file mode 100644 index 0000000000..b4de91562b --- /dev/null +++ b/osu.Game.Rulesets.Osu/UI/AnyOrderHitPolicy.cs @@ -0,0 +1,22 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Osu.UI +{ + /// + /// An which allows hitobjects to be hit in any order. + /// + public class AnyOrderHitPolicy : IHitPolicy + { + public IHitObjectContainer HitObjectContainer { get; set; } + + public bool IsHittable(DrawableHitObject hitObject, double time) => true; + + public void HandleHit(DrawableHitObject hitObject) + { + } + } +} From 5d0b92e28cdff72e8185827d34f7cff5ddb3d191 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Aug 2021 10:38:20 +0900 Subject: [PATCH 1009/2442] Fix multi spectator test not showing teams --- .../Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index 3118a23fd5..65b1d6d53a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -97,11 +97,11 @@ namespace osu.Game.Tests.Visual.Multiplayer TeamID = 1, }; - SpectatorClient.StartPlay(PLAYER_1_ID, importedBeatmapId); - SpectatorClient.StartPlay(PLAYER_2_ID, importedBeatmapId); + SpectatorClient.StartPlay(player1.UserID, importedBeatmapId); + SpectatorClient.StartPlay(player2.UserID, importedBeatmapId); - playingUsers.Add(new MultiplayerRoomUser(PLAYER_1_ID)); - playingUsers.Add(new MultiplayerRoomUser(PLAYER_2_ID)); + playingUsers.Add(player1); + playingUsers.Add(player2); }); loadSpectateScreen(); From 543482111b470d63cf74c4ed6dab294282fa7af8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Aug 2021 10:40:14 +0900 Subject: [PATCH 1010/2442] Remove outdated todo --- .../OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs index 2846fbec29..d10917259d 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs @@ -55,7 +55,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate public MultiSpectatorScreen(MultiplayerRoomUser[] users) : base(users.Select(u => u.UserID).ToArray()) { - // todo: this is a bit ugly, but not sure on a better way to handle. this.users = users; instances = new PlayerArea[Users.Count]; From 531fba8f39949265ef61cd17a3c66d881695c9e4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Aug 2021 11:05:31 +0900 Subject: [PATCH 1011/2442] Fix potential test case failure --- .../TestSceneMultiplayerGameplayLeaderboard.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs index 0997de478b..3317ddc767 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs @@ -12,6 +12,7 @@ using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Configuration; using osu.Game.Online.API; +using osu.Game.Online.Multiplayer; using osu.Game.Online.Spectator; using osu.Game.Replays.Legacy; using osu.Game.Rulesets.Osu.Scoring; @@ -51,12 +52,13 @@ namespace osu.Game.Tests.Visual.Multiplayer OsuScoreProcessor scoreProcessor; Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value); - var playable = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value); + var playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value); + var multiplayerUsers = new List(); foreach (var user in users) { SpectatorClient.StartPlay(user, Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0); - OnlinePlayDependencies.Client.AddUser(new User { Id = user }, true); + multiplayerUsers.Add(OnlinePlayDependencies.Client.AddUser(new User { Id = user }, true)); } Children = new Drawable[] @@ -64,9 +66,9 @@ namespace osu.Game.Tests.Visual.Multiplayer scoreProcessor = new OsuScoreProcessor(), }; - scoreProcessor.ApplyBeatmap(playable); + scoreProcessor.ApplyBeatmap(playableBeatmap); - LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(scoreProcessor, Client.Room?.Users.ToArray()) + LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(scoreProcessor, multiplayerUsers.ToArray()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, From 9393c6351d41a7affb43988a1d6932476d31c99a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Aug 2021 11:08:52 +0900 Subject: [PATCH 1012/2442] Fix another potential test case failure --- .../TestSceneMultiplayerGameplayLeaderboardTeams.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs index 274c9ea4b2..dfaf2f1dc3 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Online.API; +using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; using osu.Game.Online.Rooms; using osu.Game.Rulesets.Osu.Scoring; @@ -55,7 +56,8 @@ namespace osu.Game.Tests.Visual.Multiplayer OsuScoreProcessor scoreProcessor; Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value); - var playable = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value); + var playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value); + var multiplayerUsers = new List(); foreach (var user in users) { @@ -66,6 +68,8 @@ namespace osu.Game.Tests.Visual.Multiplayer { TeamID = RNG.Next(0, 2) }; + + multiplayerUsers.Add(roomUser); } Children = new Drawable[] @@ -73,9 +77,9 @@ namespace osu.Game.Tests.Visual.Multiplayer scoreProcessor = new OsuScoreProcessor(), }; - scoreProcessor.ApplyBeatmap(playable); + scoreProcessor.ApplyBeatmap(playableBeatmap); - LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(scoreProcessor, Client.Room?.Users.ToArray()) + LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(scoreProcessor, multiplayerUsers.ToArray()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, From 18ecd8758ba0646e5c54621eb77d23d152eea397 Mon Sep 17 00:00:00 2001 From: PercyDan <50285552+PercyDan54@users.noreply.github.com> Date: Thu, 12 Aug 2021 10:12:35 +0800 Subject: [PATCH 1013/2442] Make Perfect auto restart toggleable --- osu.Game/Rulesets/Mods/ModFailCondition.cs | 5 +++++ osu.Game/Rulesets/Mods/ModPerfect.cs | 5 +++++ osu.Game/Rulesets/Mods/ModSuddenDeath.cs | 5 ----- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModFailCondition.cs b/osu.Game/Rulesets/Mods/ModFailCondition.cs index c0d7bae2b2..c6855a3d38 100644 --- a/osu.Game/Rulesets/Mods/ModFailCondition.cs +++ b/osu.Game/Rulesets/Mods/ModFailCondition.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Bindables; +using osu.Game.Configuration; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; @@ -11,6 +13,9 @@ namespace osu.Game.Rulesets.Mods { public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax), typeof(ModAutoplay) }; + [SettingSource("Restart on fail", "Automatically restarts when failed.")] + public BindableBool Restart { get; } = new BindableBool(); + public virtual bool PerformFail() => true; public virtual bool RestartOnFail => true; diff --git a/osu.Game/Rulesets/Mods/ModPerfect.cs b/osu.Game/Rulesets/Mods/ModPerfect.cs index 187a4d8e23..9016a24f8d 100644 --- a/osu.Game/Rulesets/Mods/ModPerfect.cs +++ b/osu.Game/Rulesets/Mods/ModPerfect.cs @@ -21,6 +21,11 @@ namespace osu.Game.Rulesets.Mods public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModSuddenDeath)).ToArray(); + protected ModPerfect() + { + Restart.Value = Restart.Default = true; + } + protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result) => result.Type.AffectsAccuracy() && result.Type != result.Judgement.MaxResult; diff --git a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs index 7df561b199..68ed112078 100644 --- a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs +++ b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs @@ -3,9 +3,7 @@ using System; using System.Linq; -using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; -using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; @@ -23,9 +21,6 @@ namespace osu.Game.Rulesets.Mods public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModPerfect)).ToArray(); - [SettingSource("Restart on fail", "Automatically restarts when failed.")] - public BindableBool Restart { get; } = new BindableBool(); - public override bool PerformFail() => true; public override bool RestartOnFail => Restart.Value; From d80a2dcca72da28a9fac4a6ec3c5abff2d5ddf30 Mon Sep 17 00:00:00 2001 From: PercyDan <50285552+PercyDan54@users.noreply.github.com> Date: Thu, 12 Aug 2021 10:14:01 +0800 Subject: [PATCH 1014/2442] Missed one --- osu.Game/Rulesets/Mods/ModFailCondition.cs | 2 +- osu.Game/Rulesets/Mods/ModSuddenDeath.cs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModFailCondition.cs b/osu.Game/Rulesets/Mods/ModFailCondition.cs index c6855a3d38..4425ece513 100644 --- a/osu.Game/Rulesets/Mods/ModFailCondition.cs +++ b/osu.Game/Rulesets/Mods/ModFailCondition.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mods public virtual bool PerformFail() => true; - public virtual bool RestartOnFail => true; + public virtual bool RestartOnFail => Restart.Value; public void ApplyToHealthProcessor(HealthProcessor healthProcessor) { diff --git a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs index 68ed112078..031eb716c3 100644 --- a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs +++ b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs @@ -23,8 +23,6 @@ namespace osu.Game.Rulesets.Mods public override bool PerformFail() => true; - public override bool RestartOnFail => Restart.Value; - protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result) => result.Type.AffectsCombo() && !result.IsHit; From 6ecc728c0121e4821cf83346e2cf8a5401a6210e Mon Sep 17 00:00:00 2001 From: PercyDan <50285552+PercyDan54@users.noreply.github.com> Date: Thu, 12 Aug 2021 10:27:36 +0800 Subject: [PATCH 1015/2442] Remove override --- osu.Game/Rulesets/Mods/ModSuddenDeath.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs index 031eb716c3..1abd353d20 100644 --- a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs +++ b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs @@ -21,8 +21,6 @@ namespace osu.Game.Rulesets.Mods public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModPerfect)).ToArray(); - public override bool PerformFail() => true; - protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result) => result.Type.AffectsCombo() && !result.IsHit; From 97041de09f5230e2fe1d5d9004e74b920be8b42b Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Wed, 11 Aug 2021 11:23:12 +0800 Subject: [PATCH 1016/2442] Preparation for localisation. --- osu.Game/Overlays/Settings/SettingsSection.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index 4143605c28..f993a46dc6 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -7,6 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK.Graphics; @@ -19,10 +20,10 @@ namespace osu.Game.Overlays.Settings protected override Container Content => FlowContent; public abstract Drawable CreateIcon(); - public abstract string Header { get; } + public abstract LocalisableString Header { get; } public IEnumerable FilterableChildren => Children.OfType(); - public virtual IEnumerable FilterTerms => new[] { Header }; + public virtual IEnumerable FilterTerms => new[] { Header.ToString() }; private const int header_size = 26; private const int margin = 20; From 9a5d4ffd43e1fc2fed39cf03992ff7e503cd5473 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Wed, 11 Aug 2021 15:25:08 +0800 Subject: [PATCH 1017/2442] Add GeneralSettingsStrings --- .../Localisation/GeneralSettingsStrings.cs | 59 +++++++++++++++++++ .../Sections/General/LanguageSettings.cs | 6 +- .../Sections/General/UpdateSettings.cs | 11 ++-- .../Settings/Sections/GeneralSection.cs | 4 +- 4 files changed, 71 insertions(+), 9 deletions(-) create mode 100644 osu.Game/Localisation/GeneralSettingsStrings.cs diff --git a/osu.Game/Localisation/GeneralSettingsStrings.cs b/osu.Game/Localisation/GeneralSettingsStrings.cs new file mode 100644 index 0000000000..19fb8de972 --- /dev/null +++ b/osu.Game/Localisation/GeneralSettingsStrings.cs @@ -0,0 +1,59 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class GeneralSettingsStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.GeneralSettings"; + + /// + /// "General" + /// + public static LocalisableString GeneralSectionHeader => new TranslatableString(getKey(@"general_section_header"), @"General"); + + /// + /// "Language" + /// + public static LocalisableString LanguageHeader => new TranslatableString(getKey(@"language_header"), @"Language"); + + /// + /// "Language" + /// + public static LocalisableString LanguageDropdown => new TranslatableString(getKey(@"language_dropdown"), @"Language"); + + /// + /// "Prefer metadata in original language" + /// + public static LocalisableString PreferOriginal => new TranslatableString(getKey(@"prefer_original"), @"Prefer metadata in original language"); + + /// + /// "Updates" + /// + public static LocalisableString UpdateHeader => new TranslatableString(getKey(@"update_header"), @"Updates"); + + /// + /// "Release stream" + /// + public static LocalisableString ReleaseStream => new TranslatableString(getKey(@"release_stream"), @"Release stream"); + + /// + /// "Check for updates" + /// + public static LocalisableString CheckUpdate => new TranslatableString(getKey(@"check_update"), @"Check for updates"); + + /// + /// "Open osu! folder" + /// + public static LocalisableString OpenOsuFolder => new TranslatableString(getKey(@"open_osu_folder"), @"Open osu! folder"); + + /// + /// "Change folder location..." + /// + public static LocalisableString ChangeFolderLocation => new TranslatableString(getKey(@"change_folder_location"), @"Change folder location..."); + + private static string getKey(string key) => $"{prefix}:{key}"; + } +} diff --git a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs index c6c752e2fd..ac95a713bf 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs @@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections.General private SettingsDropdown languageSelection; private Bindable frameworkLocale; - protected override LocalisableString Header => "Language"; + protected override LocalisableString Header => GeneralSettingsStrings.LanguageHeader; [BackgroundDependencyLoader] private void load(FrameworkConfigManager frameworkConfig) @@ -27,11 +27,11 @@ namespace osu.Game.Overlays.Settings.Sections.General { languageSelection = new SettingsEnumDropdown { - LabelText = "Language", + LabelText = GeneralSettingsStrings.LanguageDropdown, }, new SettingsCheckbox { - LabelText = "Prefer metadata in original language", + LabelText = GeneralSettingsStrings.PreferOriginal, Current = frameworkConfig.GetBindable(FrameworkSetting.ShowUnicode) }, }; diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index dd20e1d7ef..aa37748653 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -9,6 +9,7 @@ using osu.Framework.Localisation; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Game.Configuration; +using osu.Game.Localisation; using osu.Game.Overlays.Notifications; using osu.Game.Overlays.Settings.Sections.Maintenance; using osu.Game.Updater; @@ -20,7 +21,7 @@ namespace osu.Game.Overlays.Settings.Sections.General [Resolved(CanBeNull = true)] private UpdateManager updateManager { get; set; } - protected override LocalisableString Header => "Updates"; + protected override LocalisableString Header => GeneralSettingsStrings.UpdateHeader; private SettingsButton checkForUpdatesButton; @@ -32,7 +33,7 @@ namespace osu.Game.Overlays.Settings.Sections.General { Add(new SettingsEnumDropdown { - LabelText = "Release stream", + LabelText = GeneralSettingsStrings.ReleaseStream, Current = config.GetBindable(OsuSetting.ReleaseStream), }); @@ -40,7 +41,7 @@ namespace osu.Game.Overlays.Settings.Sections.General { Add(checkForUpdatesButton = new SettingsButton { - Text = "Check for updates", + Text = GeneralSettingsStrings.CheckUpdate, Action = () => { checkForUpdatesButton.Enabled.Value = false; @@ -65,13 +66,13 @@ namespace osu.Game.Overlays.Settings.Sections.General { Add(new SettingsButton { - Text = "Open osu! folder", + Text = GeneralSettingsStrings.OpenOsuFolder, Action = storage.OpenInNativeExplorer, }); Add(new SettingsButton { - Text = "Change folder location...", + Text = GeneralSettingsStrings.ChangeFolderLocation, Action = () => game?.PerformFromScreen(menu => menu.Push(new MigrationSelectScreen())) }); } diff --git a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs index fefc3fe6a7..87e9f34833 100644 --- a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs +++ b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs @@ -3,13 +3,15 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; +using osu.Game.Localisation; using osu.Game.Overlays.Settings.Sections.General; namespace osu.Game.Overlays.Settings.Sections { public class GeneralSection : SettingsSection { - public override string Header => "General"; + public override LocalisableString Header => GeneralSettingsStrings.GeneralSectionHeader; public override Drawable CreateIcon() => new SpriteIcon { From 078953980e65c4639899833556b6cd3969e6fd1c Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Wed, 11 Aug 2021 16:48:37 +0800 Subject: [PATCH 1018/2442] Add GraphicsSettingsStrings --- osu.Game/Localisation/CommonStrings.cs | 7 +- .../Localisation/GraphicsSettingsStrings.cs | 119 ++++++++++++++++++ .../Sections/Graphics/DetailSettings.cs | 11 +- .../Sections/Graphics/LayoutSettings.cs | 25 ++-- .../Sections/Graphics/RendererSettings.cs | 13 +- .../Settings/Sections/GraphicsSection.cs | 4 +- 6 files changed, 152 insertions(+), 27 deletions(-) create mode 100644 osu.Game/Localisation/GraphicsSettingsStrings.cs diff --git a/osu.Game/Localisation/CommonStrings.cs b/osu.Game/Localisation/CommonStrings.cs index bf488d2590..5e4ce235b4 100644 --- a/osu.Game/Localisation/CommonStrings.cs +++ b/osu.Game/Localisation/CommonStrings.cs @@ -19,6 +19,11 @@ namespace osu.Game.Localisation /// public static LocalisableString Enabled => new TranslatableString(getKey(@"enabled"), @"Enabled"); + /// + /// "Default" + /// + public static LocalisableString Default => new TranslatableString(getKey(@"default"), @"Default"); + /// /// "Width" /// @@ -31,4 +36,4 @@ namespace osu.Game.Localisation private static string getKey(string key) => $@"{prefix}:{key}"; } -} \ No newline at end of file +} diff --git a/osu.Game/Localisation/GraphicsSettingsStrings.cs b/osu.Game/Localisation/GraphicsSettingsStrings.cs new file mode 100644 index 0000000000..841635649e --- /dev/null +++ b/osu.Game/Localisation/GraphicsSettingsStrings.cs @@ -0,0 +1,119 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class GraphicsSettingsStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.GraphicsSettings"; + + /// + /// "Graphics" + /// + public static LocalisableString GraphicsSectionHeader => new TranslatableString(getKey(@"graphics_section_header"), @"Graphics"); + + /// + /// "Renderer" + /// + public static LocalisableString RendererHeader => new TranslatableString(getKey(@"renderer_header"), @"Renderer"); + + /// + /// "Frame limiter" + /// + public static LocalisableString FrameLimiter => new TranslatableString(getKey(@"frame_limiter"), @"Frame limiter"); + + /// + /// "Threading mode" + /// + public static LocalisableString ThreadingMode => new TranslatableString(getKey(@"threading_mode"), @"Threading mode"); + + /// + /// "Show FPS" + /// + public static LocalisableString ShowFPS => new TranslatableString(getKey(@"show_fps"), @"Show FPS"); + + /// + /// "Using unlimited frame limiter can lead to stutters, bad performance and overheating. It will not improve perceived latency. "2x refresh rate" is recommended." + /// + public static LocalisableString UnlimitedFramesNote => new TranslatableString(getKey(@"unlimited_frames_note"), @"Using unlimited frame limiter can lead to stutters, bad performance and overheating. It will not improve perceived latency. ""2x refresh rate"" is recommended."); + + /// + /// "Layout" + /// + public static LocalisableString LayoutHeader => new TranslatableString(getKey(@"layout_header"), @"Layout"); + + /// + /// "Screen mode" + /// + public static LocalisableString ScreenMode => new TranslatableString(getKey(@"screen_mode"), @"Screen mode"); + + /// + /// "Resolution" + /// + public static LocalisableString Resolution => new TranslatableString(getKey(@"resolution"), @"Resolution"); + + /// + /// "UI Scaling" + /// + public static LocalisableString UIScaling => new TranslatableString(getKey(@"ui_scaling"), @"UI Scaling"); + + /// + /// "Screen Scaling" + /// + public static LocalisableString ScreenScaling => new TranslatableString(getKey(@"screen_scaling"), @"Screen Scaling"); + + /// + /// "Horizontal position" + /// + public static LocalisableString HorizontalPosition => new TranslatableString(getKey(@"horizontal_position"), @"Horizontal position"); + + /// + /// "Vertical position" + /// + public static LocalisableString VerticalPosition => new TranslatableString(getKey(@"vertical_position"), @"Vertical position"); + + /// + /// "Horizontal scale" + /// + public static LocalisableString HorizontalScale => new TranslatableString(getKey(@"horizontal_scale"), @"Horizontal scale"); + + /// + /// "Vertical scale" + /// + public static LocalisableString VerticalScale => new TranslatableString(getKey(@"vertical_scale"), @"Vertical scale"); + + /// + /// "Running without fullscreen mode will increase your input latency!" + /// + public static LocalisableString NotFullscreenNote => new TranslatableString(getKey(@"not_fullscreen_note"), @"Running without fullscreen mode will increase your input latency!"); + + /// + /// "Detail Settings" + /// + public static LocalisableString DetailSettingsHeader => new TranslatableString(getKey(@"detail_settings_header"), @"Detail Settings"); + + /// + /// "Storyboard / Video" + /// + public static LocalisableString StoryboardVideo => new TranslatableString(getKey(@"storyboard_video"), @"Storyboard / Video"); + + /// + /// "Hit Lighting" + /// + public static LocalisableString HitLighting => new TranslatableString(getKey(@"hit_lighting"), @"Hit Lighting"); + + /// + /// "Screenshot format" + /// + public static LocalisableString ScreenshotFormat => new TranslatableString(getKey(@"screenshot_format"), @"Screenshot format"); + + /// + /// "Show menu cursor in screenshots" + /// + public static LocalisableString ShowCursorInScreenshots => new TranslatableString(getKey(@""), @"Show menu cursor in screenshots"); + + private static string getKey(string key) => $"{prefix}:{key}"; + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs index f889cfca0f..20b1d8d801 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs @@ -5,12 +5,13 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Configuration; +using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Graphics { public class DetailSettings : SettingsSubsection { - protected override LocalisableString Header => "Detail Settings"; + protected override LocalisableString Header => GraphicsSettingsStrings.DetailSettingsHeader; [BackgroundDependencyLoader] private void load(OsuConfigManager config) @@ -19,22 +20,22 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { new SettingsCheckbox { - LabelText = "Storyboard / Video", + LabelText = GraphicsSettingsStrings.StoryboardVideo, Current = config.GetBindable(OsuSetting.ShowStoryboard) }, new SettingsCheckbox { - LabelText = "Hit Lighting", + LabelText = GraphicsSettingsStrings.HitLighting, Current = config.GetBindable(OsuSetting.HitLighting) }, new SettingsEnumDropdown { - LabelText = "Screenshot format", + LabelText = GraphicsSettingsStrings.ScreenshotFormat, Current = config.GetBindable(OsuSetting.ScreenshotFormat) }, new SettingsCheckbox { - LabelText = "Show menu cursor in screenshots", + LabelText = GraphicsSettingsStrings.ShowCursorInScreenshots, Current = config.GetBindable(OsuSetting.ScreenshotCaptureMenuCursor) } }; diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 91208cb78a..50bd8184ee 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -16,13 +16,14 @@ using osu.Framework.Platform; using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; +using osu.Game.Localisation; using osuTK.Graphics; namespace osu.Game.Overlays.Settings.Sections.Graphics { public class LayoutSettings : SettingsSubsection { - protected override LocalisableString Header => "Layout"; + protected override LocalisableString Header => GraphicsSettingsStrings.LayoutHeader; private FillFlowContainer> scalingSettings; @@ -67,20 +68,20 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { windowModeDropdown = new SettingsDropdown { - LabelText = "Screen mode", + LabelText = GraphicsSettingsStrings.ScreenMode, ItemSource = windowModes, Current = config.GetBindable(FrameworkSetting.WindowMode), }, resolutionDropdown = new ResolutionSettingsDropdown { - LabelText = "Resolution", + LabelText = GraphicsSettingsStrings.Resolution, ShowsDefaultIndicator = false, ItemSource = resolutions, Current = sizeFullscreen }, new SettingsSlider { - LabelText = "UI Scaling", + LabelText = GraphicsSettingsStrings.UIScaling, TransferValueOnCommit = true, Current = osuConfig.GetBindable(OsuSetting.UIScale), KeyboardStep = 0.01f, @@ -88,7 +89,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics }, new SettingsEnumDropdown { - LabelText = "Screen Scaling", + LabelText = GraphicsSettingsStrings.ScreenScaling, Current = osuConfig.GetBindable(OsuSetting.Scaling), Keywords = new[] { "scale", "letterbox" }, }, @@ -104,28 +105,28 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { new SettingsSlider { - LabelText = "Horizontal position", + LabelText = GraphicsSettingsStrings.HorizontalPosition, Current = scalingPositionX, KeyboardStep = 0.01f, DisplayAsPercentage = true }, new SettingsSlider { - LabelText = "Vertical position", + LabelText = GraphicsSettingsStrings.VerticalPosition, Current = scalingPositionY, KeyboardStep = 0.01f, DisplayAsPercentage = true }, new SettingsSlider { - LabelText = "Horizontal scale", + LabelText = GraphicsSettingsStrings.HorizontalScale, Current = scalingSizeX, KeyboardStep = 0.01f, DisplayAsPercentage = true }, new SettingsSlider { - LabelText = "Vertical scale", + LabelText = GraphicsSettingsStrings.VerticalScale, Current = scalingSizeY, KeyboardStep = 0.01f, DisplayAsPercentage = true @@ -145,9 +146,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { updateResolutionDropdown(); - const string not_fullscreen_note = "Running without fullscreen mode will increase your input latency!"; - - windowModeDropdown.WarningText = mode.NewValue != WindowMode.Fullscreen ? not_fullscreen_note : string.Empty; + windowModeDropdown.WarningText = mode.NewValue != WindowMode.Fullscreen ? GraphicsSettingsStrings.NotFullscreenNote : string.Empty; }, true); windowModes.BindCollectionChanged((sender, args) => @@ -245,7 +244,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics protected override LocalisableString GenerateItemText(Size item) { if (item == new Size(9999, 9999)) - return "Default"; + return CommonStrings.Default; return $"{item.Width}x{item.Height}"; } diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index 2210c7911e..9747f6b373 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -7,12 +7,13 @@ using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Framework.Platform; using osu.Game.Configuration; +using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Graphics { public class RendererSettings : SettingsSubsection { - protected override LocalisableString Header => "Renderer"; + protected override LocalisableString Header => GraphicsSettingsStrings.RendererHeader; private SettingsEnumDropdown frameLimiterDropdown; @@ -25,17 +26,17 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics // TODO: this needs to be a custom dropdown at some point frameLimiterDropdown = new SettingsEnumDropdown { - LabelText = "Frame limiter", + LabelText = GraphicsSettingsStrings.FrameLimiter, Current = config.GetBindable(FrameworkSetting.FrameSync) }, new SettingsEnumDropdown { - LabelText = "Threading mode", + LabelText = GraphicsSettingsStrings.ThreadingMode, Current = config.GetBindable(FrameworkSetting.ExecutionMode) }, new SettingsCheckbox { - LabelText = "Show FPS", + LabelText = GraphicsSettingsStrings.ShowFPS, Current = osuConfig.GetBindable(OsuSetting.ShowFpsDisplay) }, }; @@ -47,9 +48,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics frameLimiterDropdown.Current.BindValueChanged(limit => { - const string unlimited_frames_note = "Using unlimited frame limiter can lead to stutters, bad performance and overheating. It will not improve perceived latency. \"2x refresh rate\" is recommended."; - - frameLimiterDropdown.WarningText = limit.NewValue == FrameSync.Unlimited ? unlimited_frames_note : string.Empty; + frameLimiterDropdown.WarningText = limit.NewValue == FrameSync.Unlimited ? GraphicsSettingsStrings.UnlimitedFramesNote : string.Empty; }, true); } } diff --git a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs index 4ade48031f..fd0718f9f2 100644 --- a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs +++ b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs @@ -3,13 +3,15 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; +using osu.Game.Localisation; using osu.Game.Overlays.Settings.Sections.Graphics; namespace osu.Game.Overlays.Settings.Sections { public class GraphicsSection : SettingsSection { - public override string Header => "Graphics"; + public override LocalisableString Header => GraphicsSettingsStrings.GraphicsSectionHeader; public override Drawable CreateIcon() => new SpriteIcon { From b2986e99d30a8e638e53047dd3b594343a18e073 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Wed, 11 Aug 2021 17:10:08 +0800 Subject: [PATCH 1019/2442] Add AudioSettingsStrings --- osu.Game/Localisation/AudioSettingsStrings.cs | 64 +++++++++++++++++++ .../Sections/Audio/AudioDevicesSettings.cs | 5 +- .../Settings/Sections/Audio/OffsetSettings.cs | 7 +- .../Settings/Sections/Audio/VolumeSettings.cs | 11 ++-- .../Settings/Sections/AudioSection.cs | 4 +- 5 files changed, 80 insertions(+), 11 deletions(-) create mode 100644 osu.Game/Localisation/AudioSettingsStrings.cs diff --git a/osu.Game/Localisation/AudioSettingsStrings.cs b/osu.Game/Localisation/AudioSettingsStrings.cs new file mode 100644 index 0000000000..aa6eabd7d1 --- /dev/null +++ b/osu.Game/Localisation/AudioSettingsStrings.cs @@ -0,0 +1,64 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class AudioSettingsStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.AudioSettings"; + + /// + /// "Audio" + /// + public static LocalisableString AudioSectionHeader => new TranslatableString(getKey(@"audio_section_header"), @"Audio"); + + /// + /// "Devices" + /// + public static LocalisableString AudioDevicesHeader => new TranslatableString(getKey(@"audio_devices_header"), @"Devices"); + + /// + /// "Volume" + /// + public static LocalisableString VolumeHeader => new TranslatableString(getKey(@"volume_header"), @"Volume"); + + /// + /// "Master" + /// + public static LocalisableString MasterVolume => new TranslatableString(getKey(@"master_volume"), @"Master"); + + /// + /// "Master (window inactive)" + /// + public static LocalisableString MasterVolumeInactive => new TranslatableString(getKey(@"master_volume_inactive"), @"Master (window inactive)"); + + /// + /// "Effect" + /// + public static LocalisableString EffectVolume => new TranslatableString(getKey(@"effect_volume"), @"Effect"); + + /// + /// "Music" + /// + public static LocalisableString MusicVolume => new TranslatableString(getKey(@"music_volume"), @"Music"); + + /// + /// "Offset Adjustment" + /// + public static LocalisableString OffsetHeader => new TranslatableString(getKey(@"offset_header"), @"Offset Adjustment"); + + /// + /// "Audio offset" + /// + public static LocalisableString AudioOffset => new TranslatableString(getKey(@"audio_offset"), @"Audio offset"); + + /// + /// "Offset wizard" + /// + public static LocalisableString OffsetWizard => new TranslatableString(getKey(@"offset_wizard"), @"Offset wizard"); + + private static string getKey(string key) => $"{prefix}:{key}"; + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs index d64f176468..501f1b86b8 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs @@ -8,12 +8,13 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Localisation; using osu.Game.Graphics.UserInterface; +using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Audio { public class AudioDevicesSettings : SettingsSubsection { - protected override LocalisableString Header => "Devices"; + protected override LocalisableString Header => AudioSettingsStrings.AudioDevicesHeader; [Resolved] private AudioManager audio { get; set; } @@ -78,7 +79,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio private class AudioDeviceDropdownControl : DropdownControl { protected override LocalisableString GenerateItemText(string item) - => string.IsNullOrEmpty(item) ? "Default" : base.GenerateItemText(item); + => string.IsNullOrEmpty(item) ? CommonStrings.Default : base.GenerateItemText(item); } } } diff --git a/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs index 7f2e377c83..85973c81c9 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs @@ -6,12 +6,13 @@ using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; +using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Audio { public class OffsetSettings : SettingsSubsection { - protected override LocalisableString Header => "Offset Adjustment"; + protected override LocalisableString Header => AudioSettingsStrings.OffsetHeader; [BackgroundDependencyLoader] private void load(OsuConfigManager config) @@ -20,13 +21,13 @@ namespace osu.Game.Overlays.Settings.Sections.Audio { new SettingsSlider { - LabelText = "Audio offset", + LabelText = AudioSettingsStrings.AudioOffset, Current = config.GetBindable(OsuSetting.AudioOffset), KeyboardStep = 1f }, new SettingsButton { - Text = "Offset wizard" + Text = AudioSettingsStrings.OffsetWizard } }; } diff --git a/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs index 8f88b03471..00c1cb8f43 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs @@ -6,12 +6,13 @@ using osu.Framework.Audio; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Configuration; +using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Audio { public class VolumeSettings : SettingsSubsection { - protected override LocalisableString Header => "Volume"; + protected override LocalisableString Header => AudioSettingsStrings.VolumeHeader; [BackgroundDependencyLoader] private void load(AudioManager audio, OsuConfigManager config) @@ -20,28 +21,28 @@ namespace osu.Game.Overlays.Settings.Sections.Audio { new SettingsSlider { - LabelText = "Master", + LabelText = AudioSettingsStrings.MasterVolume, Current = audio.Volume, KeyboardStep = 0.01f, DisplayAsPercentage = true }, new SettingsSlider { - LabelText = "Master (window inactive)", + LabelText = AudioSettingsStrings.MasterVolumeInactive, Current = config.GetBindable(OsuSetting.VolumeInactive), KeyboardStep = 0.01f, DisplayAsPercentage = true }, new SettingsSlider { - LabelText = "Effect", + LabelText = AudioSettingsStrings.EffectVolume, Current = audio.VolumeSample, KeyboardStep = 0.01f, DisplayAsPercentage = true }, new SettingsSlider { - LabelText = "Music", + LabelText = AudioSettingsStrings.MusicVolume, Current = audio.VolumeTrack, KeyboardStep = 0.01f, DisplayAsPercentage = true diff --git a/osu.Game/Overlays/Settings/Sections/AudioSection.cs b/osu.Game/Overlays/Settings/Sections/AudioSection.cs index 7072d8e63d..694da0529a 100644 --- a/osu.Game/Overlays/Settings/Sections/AudioSection.cs +++ b/osu.Game/Overlays/Settings/Sections/AudioSection.cs @@ -3,15 +3,17 @@ using System.Collections.Generic; using System.Linq; +using osu.Framework.Localisation; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Game.Localisation; using osu.Game.Overlays.Settings.Sections.Audio; namespace osu.Game.Overlays.Settings.Sections { public class AudioSection : SettingsSection { - public override string Header => "Audio"; + public override LocalisableString Header => AudioSettingsStrings.AudioSectionHeader; public override Drawable CreateIcon() => new SpriteIcon { From 2cc89f50cce503ed8ecd3a86206b8cd6a50660ea Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Thu, 12 Aug 2021 09:14:49 +0800 Subject: [PATCH 1020/2442] Add missing key --- osu.Game/Localisation/GraphicsSettingsStrings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Localisation/GraphicsSettingsStrings.cs b/osu.Game/Localisation/GraphicsSettingsStrings.cs index 841635649e..989eaf19b9 100644 --- a/osu.Game/Localisation/GraphicsSettingsStrings.cs +++ b/osu.Game/Localisation/GraphicsSettingsStrings.cs @@ -112,7 +112,7 @@ namespace osu.Game.Localisation /// /// "Show menu cursor in screenshots" /// - public static LocalisableString ShowCursorInScreenshots => new TranslatableString(getKey(@""), @"Show menu cursor in screenshots"); + public static LocalisableString ShowCursorInScreenshots => new TranslatableString(getKey(@"show_cursor_in_screenshots"), @"Show menu cursor in screenshots"); private static string getKey(string key) => $"{prefix}:{key}"; } From 03013d0d307480f3589714dd4b6043555fb4943d Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Thu, 12 Aug 2021 09:53:31 +0800 Subject: [PATCH 1021/2442] Add InputSettingsStrings Existed strings files keep no change --- osu.Game/Localisation/CommonStrings.cs | 5 ++ osu.Game/Localisation/InputSettingsStrings.cs | 59 +++++++++++++++++++ .../Input/GlobalKeyBindingsSection.cs | 11 ++-- .../Sections/Input/KeyBindingPanel.cs | 3 +- .../Settings/Sections/Input/KeyBindingRow.cs | 5 +- .../Sections/Input/KeyBindingsSubsection.cs | 3 +- .../Sections/Input/RulesetBindingsSection.cs | 3 +- .../Settings/Sections/InputSection.cs | 5 +- 8 files changed, 82 insertions(+), 12 deletions(-) create mode 100644 osu.Game/Localisation/InputSettingsStrings.cs diff --git a/osu.Game/Localisation/CommonStrings.cs b/osu.Game/Localisation/CommonStrings.cs index 5e4ce235b4..432c1c6255 100644 --- a/osu.Game/Localisation/CommonStrings.cs +++ b/osu.Game/Localisation/CommonStrings.cs @@ -14,6 +14,11 @@ namespace osu.Game.Localisation /// public static LocalisableString Cancel => new TranslatableString(getKey(@"cancel"), @"Cancel"); + /// + /// "Clear" + /// + public static LocalisableString Clear => new TranslatableString(getKey(@"clear"), @"Clear"); + /// /// "Enabled" /// diff --git a/osu.Game/Localisation/InputSettingsStrings.cs b/osu.Game/Localisation/InputSettingsStrings.cs new file mode 100644 index 0000000000..e46b4cecf3 --- /dev/null +++ b/osu.Game/Localisation/InputSettingsStrings.cs @@ -0,0 +1,59 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class InputSettingsStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.InputSettings"; + + /// + /// "Input" + /// + public static LocalisableString InputSectionHeader => new TranslatableString(getKey(@"input_section_header"), @"Input"); + + /// + /// "Global" + /// + public static LocalisableString GlobalKeyBindingHeader => new TranslatableString(getKey(@"global_key_binding_header"), @"Global"); + + /// + /// "Song Select" + /// + public static LocalisableString SongSelectSection => new TranslatableString(getKey(@"song_select_section"), @"Song Select"); + + /// + /// "In Game" + /// + public static LocalisableString InGameSection => new TranslatableString(getKey(@"in_game_section"), @"In Game"); + + /// + /// "Audio" + /// + public static LocalisableString AudioSection => new TranslatableString(getKey(@"audio_section"), @"Audio"); + + /// + /// "Editor" + /// + public static LocalisableString EditorSection => new TranslatableString(getKey(@"editor_section"), @"Editor"); + + /// + /// "Reset all bindings in section" + /// + public static LocalisableString ResetSectionButton => new TranslatableString(getKey(@"reset_section_button"), @"Reset all bindings in section"); + + /// + /// "key configuration" + /// + public static LocalisableString KeyBindingPanelHeader => new TranslatableString(getKey(@"key_binding_panel_header"), @"key configuration"); + + /// + /// "Customise your keys!" + /// + public static LocalisableString KeyBindingPanelDescription => new TranslatableString(getKey(@"key_binding_panel_description"), @"Customise your keys!"); + + private static string getKey(string key) => $"{prefix}:{key}"; + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Input/GlobalKeyBindingsSection.cs b/osu.Game/Overlays/Settings/Sections/Input/GlobalKeyBindingsSection.cs index 9898a50320..3350ff4eaa 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/GlobalKeyBindingsSection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/GlobalKeyBindingsSection.cs @@ -5,6 +5,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Input.Bindings; +using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Input { @@ -15,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input Icon = FontAwesome.Solid.Globe }; - public override string Header => "Global"; + public override LocalisableString Header => InputSettingsStrings.GlobalKeyBindingHeader; public GlobalKeyBindingsSection(GlobalActionContainer manager) { @@ -39,7 +40,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input private class SongSelectKeyBindingSubsection : KeyBindingsSubsection { - protected override LocalisableString Header => "Song Select"; + protected override LocalisableString Header => InputSettingsStrings.SongSelectSection; public SongSelectKeyBindingSubsection(GlobalActionContainer manager) : base(null) @@ -50,7 +51,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input private class InGameKeyBindingsSubsection : KeyBindingsSubsection { - protected override LocalisableString Header => "In Game"; + protected override LocalisableString Header => InputSettingsStrings.InGameSection; public InGameKeyBindingsSubsection(GlobalActionContainer manager) : base(null) @@ -61,7 +62,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input private class AudioControlKeyBindingsSubsection : KeyBindingsSubsection { - protected override LocalisableString Header => "Audio"; + protected override LocalisableString Header => InputSettingsStrings.AudioSection; public AudioControlKeyBindingsSubsection(GlobalActionContainer manager) : base(null) @@ -72,7 +73,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input private class EditorKeyBindingsSubsection : KeyBindingsSubsection { - protected override LocalisableString Header => "Editor"; + protected override LocalisableString Header => InputSettingsStrings.EditorSection; public EditorKeyBindingsSubsection(GlobalActionContainer manager) : base(null) diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingPanel.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingPanel.cs index 7cdc739b7c..67f1bb8d3e 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingPanel.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingPanel.cs @@ -4,13 +4,14 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Input.Bindings; +using osu.Game.Localisation; using osu.Game.Rulesets; namespace osu.Game.Overlays.Settings.Sections.Input { public class KeyBindingPanel : SettingsSubPanel { - protected override Drawable CreateHeader() => new SettingsHeader("key configuration", "Customise your keys!"); + protected override Drawable CreateHeader() => new SettingsHeader(InputSettingsStrings.KeyBindingPanelHeader, InputSettingsStrings.KeyBindingPanelDescription); [BackgroundDependencyLoader(permitNulls: true)] private void load(RulesetStore rulesets, GlobalActionContainer global) diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs index 6e018597be..c38c516f21 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs @@ -20,6 +20,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Input; using osu.Game.Input.Bindings; +using osu.Game.Localisation; using osuTK; using osuTK.Graphics; using osuTK.Input; @@ -385,7 +386,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input { public CancelButton() { - Text = "Cancel"; + Text = CommonStrings.Cancel; Size = new Vector2(80, 20); } } @@ -394,7 +395,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input { public ClearButton() { - Text = "Clear"; + Text = CommonStrings.Clear; Size = new Vector2(80, 20); } } diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs index d65684fd37..ef5ccae1a0 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs @@ -10,6 +10,7 @@ using osu.Game.Database; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Rulesets; +using osu.Game.Localisation; using osuTK; namespace osu.Game.Overlays.Settings.Sections.Input @@ -64,7 +65,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input [BackgroundDependencyLoader] private void load() { - Text = "Reset all bindings in section"; + Text = InputSettingsStrings.ResetSectionButton; RelativeSizeAxes = Axes.X; Width = 0.5f; Anchor = Anchor.TopCentre; diff --git a/osu.Game/Overlays/Settings/Sections/Input/RulesetBindingsSection.cs b/osu.Game/Overlays/Settings/Sections/Input/RulesetBindingsSection.cs index 81a4d7eccd..5246051a4a 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/RulesetBindingsSection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/RulesetBindingsSection.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Rulesets; @@ -15,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input Icon = OsuIcon.Hot }; - public override string Header => ruleset.Name; + public override LocalisableString Header => ruleset.Name; private readonly RulesetInfo ruleset; diff --git a/osu.Game/Overlays/Settings/Sections/InputSection.cs b/osu.Game/Overlays/Settings/Sections/InputSection.cs index 366f39388a..d282ba5318 100644 --- a/osu.Game/Overlays/Settings/Sections/InputSection.cs +++ b/osu.Game/Overlays/Settings/Sections/InputSection.cs @@ -11,6 +11,7 @@ using osu.Framework.Input.Handlers.Mouse; using osu.Framework.Input.Handlers.Tablet; using osu.Framework.Localisation; using osu.Framework.Platform; +using osu.Game.Localisation; using osu.Game.Overlays.Settings.Sections.Input; namespace osu.Game.Overlays.Settings.Sections @@ -19,7 +20,7 @@ namespace osu.Game.Overlays.Settings.Sections { private readonly KeyBindingPanel keyConfig; - public override string Header => "Input"; + public override LocalisableString Header => InputSettingsStrings.InputSectionHeader; [Resolved] private GameHost host { get; set; } @@ -95,7 +96,7 @@ namespace osu.Game.Overlays.Settings.Sections { new SettingsCheckbox { - LabelText = "Enabled", + LabelText = CommonStrings.Enabled, Current = handler.Enabled }, }; From 7adf2bb64c3be39f920cb074781fe032af3d0e9b Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Thu, 12 Aug 2021 10:48:29 +0800 Subject: [PATCH 1022/2442] Add UserInterfaceStrings --- osu.Game/Localisation/UserInterfaceStrings.cs | 114 ++++++++++++++++++ .../Sections/UserInterface/GeneralSettings.cs | 11 +- .../UserInterface/MainMenuSettings.cs | 17 ++- .../UserInterface/SongSelectSettings.cs | 15 +-- .../Settings/Sections/UserInterfaceSection.cs | 4 +- 5 files changed, 139 insertions(+), 22 deletions(-) create mode 100644 osu.Game/Localisation/UserInterfaceStrings.cs diff --git a/osu.Game/Localisation/UserInterfaceStrings.cs b/osu.Game/Localisation/UserInterfaceStrings.cs new file mode 100644 index 0000000000..18a9257732 --- /dev/null +++ b/osu.Game/Localisation/UserInterfaceStrings.cs @@ -0,0 +1,114 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class UserInterfaceStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.UserInterface"; + + /// + /// "User Interface" + /// + public static LocalisableString UserInterfaceSectionHeader => new TranslatableString(getKey(@"user_interface_section_header"), @"User Interface"); + + /// + /// "General" + /// + public static LocalisableString GeneralHeader => new TranslatableString(getKey(@"general_header"), @"General"); + + /// + /// "Rotate cursor when dragging" + /// + public static LocalisableString CursorRotation => new TranslatableString(getKey(@"cursor_rotation"), @"Rotate cursor when dragging"); + + /// + /// "Menu cursor size" + /// + public static LocalisableString MenuCursorSize => new TranslatableString(getKey(@"menu_cursor_size"), @"Menu cursor size"); + + /// + /// "Parallax" + /// + public static LocalisableString Parallax => new TranslatableString(getKey(@"parallax"), @"Parallax"); + + /// + /// "Hold-to-confirm activation time" + /// + public static LocalisableString HoldActivationDelay => new TranslatableString(getKey(@"hold_activation_delay"), @"Hold-to-confirm activation time"); + + /// + /// "Main Menu" + /// + public static LocalisableString MainMenuHeader => new TranslatableString(getKey(@"main_menu_header"), @"Main Menu"); + + /// + /// "Interface voices" + /// + public static LocalisableString InterfaceVoices => new TranslatableString(getKey(@"interface_voices"), @"Interface voices"); + + /// + /// "osu! music theme" + /// + public static LocalisableString OsuMusicTheme => new TranslatableString(getKey(@"osu_music_theme"), @"osu! music theme"); + + /// + /// "Intro sequence" + /// + public static LocalisableString IntroSequence => new TranslatableString(getKey(@"intro_sequence"), @"Intro sequence"); + + /// + /// "Background source" + /// + public static LocalisableString BackgroundSource => new TranslatableString(getKey(@"background_source"), @"Background source"); + + /// + /// "Seasonal backgrounds" + /// + public static LocalisableString SeasonalBackgrounds => new TranslatableString(getKey(@"seasonal_backgrounds"), @"Seasonal backgrounds"); + + /// + /// "Changes to this setting will only apply with an active osu!supporter tag." + /// + public static LocalisableString NotSupporterNote => new TranslatableString(getKey(@"not_supporter_note"), @"Changes to this setting will only apply with an active osu!supporter tag."); + + /// + /// "Song Select" + /// + public static LocalisableString SoneSelectHeader => new TranslatableString(getKey(@"song_select_header"), @"Song Select"); + + /// + /// "Right mouse drag to absolute scroll" + /// + public static LocalisableString RightMouseScroll => new TranslatableString(getKey(@"right_mouse_scroll"), @"Right mouse drag to absolute scroll"); + + /// + /// "Show converted beatmaps" + /// + public static LocalisableString ShowConvertedBeatmaps => new TranslatableString(getKey(@"show_converted_beatmaps"), @"Show converted beatmaps"); + + /// + /// "Display beatmaps from" + /// + public static LocalisableString StarsMinimum => new TranslatableString(getKey(@"stars_minimum"), @"Display beatmaps from"); + + /// + /// "up to" + /// + public static LocalisableString StarsMaximum => new TranslatableString(getKey(@"stars_maximum"), @"up to"); + + /// + /// "Random selection algorithm" + /// + public static LocalisableString RandomSelectionAlgorithm => new TranslatableString(getKey(@"random_selection_algorithm"), @"Random selection algorithm"); + + /// + /// "no limit" + /// + public static LocalisableString NoLimit => new TranslatableString(getKey(@"no_limit"), @"no limit"); + + private static string getKey(string key) => $"{prefix}:{key}"; + } +} diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs index 4b26645ef3..b26363a9da 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs @@ -6,12 +6,13 @@ using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; +using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.UserInterface { public class GeneralSettings : SettingsSubsection { - protected override LocalisableString Header => "General"; + protected override LocalisableString Header => UserInterfaceStrings.GeneralHeader; [BackgroundDependencyLoader] private void load(OsuConfigManager config) @@ -20,23 +21,23 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface { new SettingsCheckbox { - LabelText = "Rotate cursor when dragging", + LabelText = UserInterfaceStrings.CursorRotation, Current = config.GetBindable(OsuSetting.CursorRotation) }, new SettingsSlider { - LabelText = "Menu cursor size", + LabelText = UserInterfaceStrings.MenuCursorSize, Current = config.GetBindable(OsuSetting.MenuCursorSize), KeyboardStep = 0.01f }, new SettingsCheckbox { - LabelText = "Parallax", + LabelText = UserInterfaceStrings.Parallax, Current = config.GetBindable(OsuSetting.MenuParallax) }, new SettingsSlider { - LabelText = "Hold-to-confirm activation time", + LabelText = UserInterfaceStrings.HoldActivationDelay, Current = config.GetBindable(OsuSetting.UIHoldActivationDelay), KeyboardStep = 50 }, diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs index 81bbcbb54a..976893bf0a 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Configuration; +using osu.Game.Localisation; using osu.Game.Online.API; using osu.Game.Users; @@ -13,7 +14,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface { public class MainMenuSettings : SettingsSubsection { - protected override LocalisableString Header => "Main Menu"; + protected override LocalisableString Header => UserInterfaceStrings.MainMenuHeader; private IBindable user; @@ -28,27 +29,27 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface { new SettingsCheckbox { - LabelText = "Interface voices", + LabelText = UserInterfaceStrings.InterfaceVoices, Current = config.GetBindable(OsuSetting.MenuVoice) }, new SettingsCheckbox { - LabelText = "osu! music theme", + LabelText = UserInterfaceStrings.OsuMusicTheme, Current = config.GetBindable(OsuSetting.MenuMusic) }, new SettingsEnumDropdown { - LabelText = "Intro sequence", + LabelText = UserInterfaceStrings.IntroSequence, Current = config.GetBindable(OsuSetting.IntroSequence), }, backgroundSourceDropdown = new SettingsEnumDropdown { - LabelText = "Background source", + LabelText = UserInterfaceStrings.BackgroundSource, Current = config.GetBindable(OsuSetting.MenuBackgroundSource), }, new SettingsEnumDropdown { - LabelText = "Seasonal backgrounds", + LabelText = UserInterfaceStrings.SeasonalBackgrounds, Current = config.GetBindable(OsuSetting.SeasonalBackgroundMode), } }; @@ -60,9 +61,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface user.BindValueChanged(u => { - const string not_supporter_note = "Changes to this setting will only apply with an active osu!supporter tag."; - - backgroundSourceDropdown.WarningText = u.NewValue?.IsSupporter != true ? not_supporter_note : string.Empty; + backgroundSourceDropdown.WarningText = u.NewValue?.IsSupporter != true ? UserInterfaceStrings.NotSupporterNote : string.Empty; }, true); } } diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs index 587155eb0d..8596cecbb8 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; +using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.UserInterface { @@ -16,7 +17,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface private Bindable minStars; private Bindable maxStars; - protected override LocalisableString Header => "Song Select"; + protected override LocalisableString Header => UserInterfaceStrings.SoneSelectHeader; [BackgroundDependencyLoader] private void load(OsuConfigManager config) @@ -31,31 +32,31 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface { new SettingsCheckbox { - LabelText = "Right mouse drag to absolute scroll", + LabelText = UserInterfaceStrings.RightMouseScroll, Current = config.GetBindable(OsuSetting.SongSelectRightMouseScroll), }, new SettingsCheckbox { - LabelText = "Show converted beatmaps", + LabelText = UserInterfaceStrings.ShowConvertedBeatmaps, Current = config.GetBindable(OsuSetting.ShowConvertedBeatmaps), }, new SettingsSlider { - LabelText = "Display beatmaps from", + LabelText = UserInterfaceStrings.StarsMinimum, Current = config.GetBindable(OsuSetting.DisplayStarsMinimum), KeyboardStep = 0.1f, Keywords = new[] { "minimum", "maximum", "star", "difficulty" } }, new SettingsSlider { - LabelText = "up to", + LabelText = UserInterfaceStrings.StarsMaximum, Current = config.GetBindable(OsuSetting.DisplayStarsMaximum), KeyboardStep = 0.1f, Keywords = new[] { "minimum", "maximum", "star", "difficulty" } }, new SettingsEnumDropdown { - LabelText = "Random selection algorithm", + LabelText = UserInterfaceStrings.RandomSelectionAlgorithm, Current = config.GetBindable(OsuSetting.RandomSelectAlgorithm), } }; @@ -63,7 +64,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface private class MaximumStarsSlider : StarsSlider { - public override LocalisableString TooltipText => Current.IsDefault ? "no limit" : base.TooltipText; + public override LocalisableString TooltipText => Current.IsDefault ? UserInterfaceStrings.NoLimit : base.TooltipText; } private class StarsSlider : OsuSliderBar diff --git a/osu.Game/Overlays/Settings/Sections/UserInterfaceSection.cs b/osu.Game/Overlays/Settings/Sections/UserInterfaceSection.cs index 718fea5f2b..6228c4c99a 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterfaceSection.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterfaceSection.cs @@ -3,13 +3,15 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; +using osu.Game.Localisation; using osu.Game.Overlays.Settings.Sections.UserInterface; namespace osu.Game.Overlays.Settings.Sections { public class UserInterfaceSection : SettingsSection { - public override string Header => "User Interface"; + public override LocalisableString Header => UserInterfaceStrings.UserInterfaceSectionHeader; public override Drawable CreateIcon() => new SpriteIcon { From 31ffaf15d4866275f1c86c0dbecd67af5eba59ff Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Thu, 12 Aug 2021 11:11:22 +0800 Subject: [PATCH 1023/2442] Add GameplaySettingsStrings --- .../Localisation/GameplaySettingsStrings.cs | 95 +++++++++++++++++++ .../Sections/Gameplay/GeneralSettings.cs | 27 +++--- .../Sections/Gameplay/ModsSettings.cs | 5 +- .../Settings/Sections/GameplaySection.cs | 4 +- 4 files changed, 115 insertions(+), 16 deletions(-) create mode 100644 osu.Game/Localisation/GameplaySettingsStrings.cs diff --git a/osu.Game/Localisation/GameplaySettingsStrings.cs b/osu.Game/Localisation/GameplaySettingsStrings.cs new file mode 100644 index 0000000000..172387bbb8 --- /dev/null +++ b/osu.Game/Localisation/GameplaySettingsStrings.cs @@ -0,0 +1,95 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class GameplaySettingsStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.GameplaySettings"; + + /// + /// "Gameplay" + /// + public static LocalisableString GameplaySectionHeader => new TranslatableString(getKey(@"gameplay_section_header"), @"Gameplay"); + + /// + /// "General" + /// + public static LocalisableString GeneralHeader => new TranslatableString(getKey(@"general_header"), @"General"); + + /// + /// "Background dim" + /// + public static LocalisableString Dim => new TranslatableString(getKey(@"dim"), @"Background dim"); + + /// + /// "Background blur" + /// + public static LocalisableString Blur => new TranslatableString(getKey(@"blur"), @"Background blur"); + + /// + /// "Lighten playfield during breaks" + /// + public static LocalisableString LightenDuringBreaks => new TranslatableString(getKey(@"lighten_during_breaks"), @"Lighten playfield during breaks"); + + /// + /// "HUD overlay visibility mode" + /// + public static LocalisableString HUDVisibilityMode => new TranslatableString(getKey(@"hud_visibility_mode"), @"HUD overlay visibility mode"); + + /// + /// "Show difficulty graph on progress bar" + /// + public static LocalisableString ShowProgressGraph => new TranslatableString(getKey(@"show_progress_graph"), @"Show difficulty graph on progress bar"); + + /// + /// "Show health display even when you can't fail" + /// + public static LocalisableString ShowHealthDisplayWhenCantFail => new TranslatableString(getKey(@"show_health_display_when_cant_fail"), @"Show health display even when you can't fail"); + + /// + /// "Fade playfield to red when health is low" + /// + public static LocalisableString FadePlayfieldWhenHealthLow => new TranslatableString(getKey(@"fade_playfield_when_health_low"), @"Fade playfield to red when health is low"); + + /// + /// "Always show key overlay" + /// + public static LocalisableString KeyOverlay => new TranslatableString(getKey(@"key_overlay"), @"Always show key overlay"); + + /// + /// "Positional hitsounds" + /// + public static LocalisableString PositionalHitsounds => new TranslatableString(getKey(@"positional_hitsounds"), @"Positional hitsounds"); + + /// + /// "Always play first combo break sound" + /// + public static LocalisableString AlwaysPlayFirstComboBreak => new TranslatableString(getKey(@"always_play_first_combo_break"), @"Always play first combo break sound"); + + /// + /// "Score display mode" + /// + public static LocalisableString ScoreDisplayMode => new TranslatableString(getKey(@"score_display_mode"), @"Score display mode"); + + /// + /// "Disable Windows key during gameplay" + /// + public static LocalisableString DisableWinKey => new TranslatableString(getKey(@"disable_win_key"), @"Disable Windows key during gameplay"); + + /// + /// "Mods" + /// + public static LocalisableString ModsHeader => new TranslatableString(getKey(@"mods_header"), @"Mods"); + + /// + /// "Increase visibility of first object when visual impairment mods are enabled" + /// + public static LocalisableString IncreaseFirstObjectVisibility => new TranslatableString(getKey(@"increase_first_object_visibility"), @"Increase visibility of first object when visual impairment mods are enabled"); + + + private static string getKey(string key) => $"{prefix}:{key}"; + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index 353292606f..efb586fdca 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -6,13 +6,14 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Configuration; +using osu.Game.Localisation; using osu.Game.Rulesets.Scoring; namespace osu.Game.Overlays.Settings.Sections.Gameplay { public class GeneralSettings : SettingsSubsection { - protected override LocalisableString Header => "General"; + protected override LocalisableString Header => GameplaySettingsStrings.GeneralHeader; [BackgroundDependencyLoader] private void load(OsuConfigManager config) @@ -21,62 +22,62 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay { new SettingsSlider { - LabelText = "Background dim", + LabelText = GameplaySettingsStrings.Dim, Current = config.GetBindable(OsuSetting.DimLevel), KeyboardStep = 0.01f, DisplayAsPercentage = true }, new SettingsSlider { - LabelText = "Background blur", + LabelText = GameplaySettingsStrings.Blur, Current = config.GetBindable(OsuSetting.BlurLevel), KeyboardStep = 0.01f, DisplayAsPercentage = true }, new SettingsCheckbox { - LabelText = "Lighten playfield during breaks", + LabelText = GameplaySettingsStrings.LightenDuringBreaks, Current = config.GetBindable(OsuSetting.LightenDuringBreaks) }, new SettingsEnumDropdown { - LabelText = "HUD overlay visibility mode", + LabelText = GameplaySettingsStrings.HUDVisibilityMode, Current = config.GetBindable(OsuSetting.HUDVisibilityMode) }, new SettingsCheckbox { - LabelText = "Show difficulty graph on progress bar", + LabelText = GameplaySettingsStrings.ShowProgressGraph, Current = config.GetBindable(OsuSetting.ShowProgressGraph) }, new SettingsCheckbox { - LabelText = "Show health display even when you can't fail", + LabelText = GameplaySettingsStrings.ShowHealthDisplayWhenCantFail, Current = config.GetBindable(OsuSetting.ShowHealthDisplayWhenCantFail), Keywords = new[] { "hp", "bar" } }, new SettingsCheckbox { - LabelText = "Fade playfield to red when health is low", + LabelText = GameplaySettingsStrings.FadePlayfieldWhenHealthLow, Current = config.GetBindable(OsuSetting.FadePlayfieldWhenHealthLow), }, new SettingsCheckbox { - LabelText = "Always show key overlay", + LabelText = GameplaySettingsStrings.KeyOverlay, Current = config.GetBindable(OsuSetting.KeyOverlay) }, new SettingsCheckbox { - LabelText = "Positional hitsounds", + LabelText = GameplaySettingsStrings.PositionalHitsounds, Current = config.GetBindable(OsuSetting.PositionalHitSounds) }, new SettingsCheckbox { - LabelText = "Always play first combo break sound", + LabelText = GameplaySettingsStrings.AlwaysPlayFirstComboBreak, Current = config.GetBindable(OsuSetting.AlwaysPlayFirstComboBreak) }, new SettingsEnumDropdown { - LabelText = "Score display mode", + LabelText = GameplaySettingsStrings.ScoreDisplayMode, Current = config.GetBindable(OsuSetting.ScoreDisplayMode), Keywords = new[] { "scoring" } }, @@ -86,7 +87,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay { Add(new SettingsCheckbox { - LabelText = "Disable Windows key during gameplay", + LabelText = GameplaySettingsStrings.DisableWinKey, Current = config.GetBindable(OsuSetting.GameplayDisableWinKey) }); } diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs index ec9ddde2da..dfa060e8d5 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs @@ -6,12 +6,13 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Localisation; using osu.Game.Configuration; +using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Gameplay { public class ModsSettings : SettingsSubsection { - protected override LocalisableString Header => "Mods"; + protected override LocalisableString Header => GameplaySettingsStrings.ModsHeader; public override IEnumerable FilterTerms => base.FilterTerms.Concat(new[] { "mod" }); @@ -22,7 +23,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay { new SettingsCheckbox { - LabelText = "Increase visibility of first object when visual impairment mods are enabled", + LabelText = GameplaySettingsStrings.IncreaseFirstObjectVisibility, Current = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility), }, }; diff --git a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs index acb94a6a01..42d9d48d73 100644 --- a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs +++ b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs @@ -9,12 +9,14 @@ using osu.Game.Rulesets; using System.Linq; using osu.Framework.Graphics.Sprites; using osu.Framework.Logging; +using osu.Framework.Localisation; +using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections { public class GameplaySection : SettingsSection { - public override string Header => "Gameplay"; + public override LocalisableString Header => GameplaySettingsStrings.GameplaySectionHeader; public override Drawable CreateIcon() => new SpriteIcon { From 61502e977afcaa4731fb8d6f2b3d2f6c46eb11b8 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Thu, 12 Aug 2021 11:40:22 +0800 Subject: [PATCH 1024/2442] Add SkinSettingsStrings --- osu.Game/Localisation/SkinSettingsStrings.cs | 54 +++++++++++++++++++ .../Overlays/Settings/Sections/SkinSection.cs | 17 +++--- 2 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 osu.Game/Localisation/SkinSettingsStrings.cs diff --git a/osu.Game/Localisation/SkinSettingsStrings.cs b/osu.Game/Localisation/SkinSettingsStrings.cs new file mode 100644 index 0000000000..848c01b93a --- /dev/null +++ b/osu.Game/Localisation/SkinSettingsStrings.cs @@ -0,0 +1,54 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class SkinSettingsStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.SkinSettings"; + + /// + /// "Skin" + /// + public static LocalisableString SkinSectionHeader => new TranslatableString(getKey(@"skin_section_header"), @"Skin"); + + /// + /// "Skin layout editor" + /// + public static LocalisableString SkinLayoutEditor => new TranslatableString(getKey(@"skin_layout_editor"), @"Skin layout editor"); + + /// + /// "Gameplay cursor size" + /// + public static LocalisableString CursorSize => new TranslatableString(getKey(@"cursor_size"), @"Gameplay cursor size"); + + /// + /// "Adjust gameplay cursor size based on current beatmap" + /// + public static LocalisableString AutoCursorSize => new TranslatableString(getKey(@"auto_cursor_size"), @"Adjust gameplay cursor size based on current beatmap"); + + /// + /// "Beatmap skins" + /// + public static LocalisableString BeatmapSkins => new TranslatableString(getKey(@"beatmap_skins"), @"Beatmap skins"); + + /// + /// "Beatmap colours" + /// + public static LocalisableString BeatmapColours => new TranslatableString(getKey(@"beatmap_colours"), @"Beatmap colours"); + + /// + /// "Beatmap hitsounds" + /// + public static LocalisableString BeatmapHitsounds => new TranslatableString(getKey(@"beatmap_hitsounds"), @"Beatmap hitsounds"); + + /// + /// "Export selected skin" + /// + public static LocalisableString ExportSkinButton => new TranslatableString(getKey(@"export_skin_button"), @"Export selected skin"); + + private static string getKey(string key) => $"{prefix}:{key}"; + } +} diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 9f3543d059..007302c584 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -13,6 +13,7 @@ using osu.Framework.Localisation; using osu.Framework.Logging; using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; +using osu.Game.Localisation; using osu.Game.Skinning; using osu.Game.Skinning.Editor; using osuTK; @@ -23,7 +24,7 @@ namespace osu.Game.Overlays.Settings.Sections { private SkinSettingsDropdown skinDropdown; - public override string Header => "Skin"; + public override LocalisableString Header => SkinSettingsStrings.SkinSectionHeader; public override Drawable CreateIcon() => new SpriteIcon { @@ -69,34 +70,34 @@ namespace osu.Game.Overlays.Settings.Sections skinDropdown = new SkinSettingsDropdown(), new SettingsButton { - Text = "Skin layout editor", + Text = SkinSettingsStrings.SkinLayoutEditor, Action = () => skinEditor?.Toggle(), }, new ExportSkinButton(), new SettingsSlider { - LabelText = "Gameplay cursor size", + LabelText = SkinSettingsStrings.CursorSize, Current = config.GetBindable(OsuSetting.GameplayCursorSize), KeyboardStep = 0.01f }, new SettingsCheckbox { - LabelText = "Adjust gameplay cursor size based on current beatmap", + LabelText = SkinSettingsStrings.AutoCursorSize, Current = config.GetBindable(OsuSetting.AutoCursorSize) }, new SettingsCheckbox { - LabelText = "Beatmap skins", + LabelText = SkinSettingsStrings.BeatmapSkins, Current = config.GetBindable(OsuSetting.BeatmapSkins) }, new SettingsCheckbox { - LabelText = "Beatmap colours", + LabelText = SkinSettingsStrings.BeatmapColours, Current = config.GetBindable(OsuSetting.BeatmapColours) }, new SettingsCheckbox { - LabelText = "Beatmap hitsounds", + LabelText = SkinSettingsStrings.BeatmapHitsounds, Current = config.GetBindable(OsuSetting.BeatmapHitsounds) }, }; @@ -200,7 +201,7 @@ namespace osu.Game.Overlays.Settings.Sections [BackgroundDependencyLoader] private void load() { - Text = "Export selected skin"; + Text = SkinSettingsStrings.ExportSkinButton; Action = export; currentSkin = skins.CurrentSkin.GetBoundCopy(); From 9d391ad13817df8a81c5733fa7ee4f759852e095 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Thu, 12 Aug 2021 11:57:51 +0800 Subject: [PATCH 1025/2442] Add OnlineSettingsStrings --- .../Localisation/OnlineSettingsStrings.cs | 69 +++++++++++++++++++ .../Online/AlertsAndPrivacySettings.cs | 7 +- .../Sections/Online/IntegrationSettings.cs | 5 +- .../Settings/Sections/Online/WebSettings.cs | 11 +-- .../Settings/Sections/OnlineSection.cs | 4 +- 5 files changed, 85 insertions(+), 11 deletions(-) create mode 100644 osu.Game/Localisation/OnlineSettingsStrings.cs diff --git a/osu.Game/Localisation/OnlineSettingsStrings.cs b/osu.Game/Localisation/OnlineSettingsStrings.cs new file mode 100644 index 0000000000..0ef98a720c --- /dev/null +++ b/osu.Game/Localisation/OnlineSettingsStrings.cs @@ -0,0 +1,69 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class OnlineSettingsStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.OnlineSettings"; + + /// + /// "Online" + /// + public static LocalisableString OnlineSectionHeader => new TranslatableString(getKey(@"online_section_header"), @"Online"); + + /// + /// "Alerts and Privacy" + /// + public static LocalisableString AlertsAndPrivacyHeader => new TranslatableString(getKey(@"alerts_and_privacy_header"), @"Alerts and Privacy"); + + /// + /// "Show a notification when someone mentions your name" + /// + public static LocalisableString NotifyOnMentioned => new TranslatableString(getKey(@"notify_on_mentioned"), @"Show a notification when someone mentions your name"); + + /// + /// "Show a notification when you receive a private message" + /// + public static LocalisableString NotifyOnPM => new TranslatableString(getKey(@"notify_on_pm"), @"Show a notification when you receive a private message"); + + /// + /// "Integrations" + /// + public static LocalisableString IntegrationHeader => new TranslatableString(getKey(@"integration_header"), @"Integrations"); + + /// + /// "Discord Rich Presence" + /// + public static LocalisableString DiscordRichPresence => new TranslatableString(getKey(@"discord_rich_presence"), @"Discord Rich Presence"); + + /// + /// "Web" + /// + public static LocalisableString WebHeader => new TranslatableString(getKey(@"web_header"), @"Web"); + + /// + /// "Warn about opening external links" + /// + public static LocalisableString ExternalLinkWarning => new TranslatableString(getKey(@"external_link_warning"), @"Warn about opening external links"); + + /// + /// "Prefer downloads without video" + /// + public static LocalisableString PreferNoVideo => new TranslatableString(getKey(@"prefer_no_video"), @"Prefer downloads without video"); + + /// + /// "Automatically download beatmaps when spectating" + /// + public static LocalisableString AutomaticallyDownload => new TranslatableString(getKey(@"automatically_download"), @"Automatically download beatmaps when spectating"); + + /// + /// "Show explicit content in search results" + /// + public static LocalisableString ShowExplicitContent => new TranslatableString(getKey(@"show_explicit_content"), @"Show explicit content in search results"); + + private static string getKey(string key) => $"{prefix}:{key}"; + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Online/AlertsAndPrivacySettings.cs b/osu.Game/Overlays/Settings/Sections/Online/AlertsAndPrivacySettings.cs index 3a2de2ee36..1728b565c2 100644 --- a/osu.Game/Overlays/Settings/Sections/Online/AlertsAndPrivacySettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Online/AlertsAndPrivacySettings.cs @@ -5,12 +5,13 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Configuration; +using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Online { public class AlertsAndPrivacySettings : SettingsSubsection { - protected override LocalisableString Header => "Alerts and Privacy"; + protected override LocalisableString Header => OnlineSettingsStrings.AlertsAndPrivacyHeader; [BackgroundDependencyLoader] private void load(OsuConfigManager config) @@ -19,12 +20,12 @@ namespace osu.Game.Overlays.Settings.Sections.Online { new SettingsCheckbox { - LabelText = "Show a notification when someone mentions your name", + LabelText = OnlineSettingsStrings.NotifyOnMentioned, Current = config.GetBindable(OsuSetting.NotifyOnUsernameMentioned) }, new SettingsCheckbox { - LabelText = "Show a notification when you receive a private message", + LabelText = OnlineSettingsStrings.NotifyOnPM, Current = config.GetBindable(OsuSetting.NotifyOnPrivateMessage) }, }; diff --git a/osu.Game/Overlays/Settings/Sections/Online/IntegrationSettings.cs b/osu.Game/Overlays/Settings/Sections/Online/IntegrationSettings.cs index f2012f0d9c..f2a516feef 100644 --- a/osu.Game/Overlays/Settings/Sections/Online/IntegrationSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Online/IntegrationSettings.cs @@ -5,12 +5,13 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Configuration; +using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Online { public class IntegrationSettings : SettingsSubsection { - protected override LocalisableString Header => "Integrations"; + protected override LocalisableString Header => OnlineSettingsStrings.IntegrationHeader; [BackgroundDependencyLoader] private void load(OsuConfigManager config) @@ -19,7 +20,7 @@ namespace osu.Game.Overlays.Settings.Sections.Online { new SettingsEnumDropdown { - LabelText = "Discord Rich Presence", + LabelText = OnlineSettingsStrings.DiscordRichPresence, Current = config.GetBindable(OsuSetting.DiscordRichPresence) } }; diff --git a/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs b/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs index 89e7b096f3..4200ddda3d 100644 --- a/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs @@ -5,12 +5,13 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Configuration; +using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Online { public class WebSettings : SettingsSubsection { - protected override LocalisableString Header => "Web"; + protected override LocalisableString Header => OnlineSettingsStrings.WebHeader; [BackgroundDependencyLoader] private void load(OsuConfigManager config) @@ -19,24 +20,24 @@ namespace osu.Game.Overlays.Settings.Sections.Online { new SettingsCheckbox { - LabelText = "Warn about opening external links", + LabelText = OnlineSettingsStrings.ExternalLinkWarning, Current = config.GetBindable(OsuSetting.ExternalLinkWarning) }, new SettingsCheckbox { - LabelText = "Prefer downloads without video", + LabelText = OnlineSettingsStrings.PreferNoVideo, Keywords = new[] { "no-video" }, Current = config.GetBindable(OsuSetting.PreferNoVideo) }, new SettingsCheckbox { - LabelText = "Automatically download beatmaps when spectating", + LabelText = OnlineSettingsStrings.AutomaticallyDownload, Keywords = new[] { "spectator" }, Current = config.GetBindable(OsuSetting.AutomaticallyDownloadWhenSpectating), }, new SettingsCheckbox { - LabelText = "Show explicit content in search results", + LabelText = OnlineSettingsStrings.ShowExplicitContent, Keywords = new[] { "nsfw", "18+", "offensive" }, Current = config.GetBindable(OsuSetting.ShowOnlineExplicitContent), } diff --git a/osu.Game/Overlays/Settings/Sections/OnlineSection.cs b/osu.Game/Overlays/Settings/Sections/OnlineSection.cs index 680d11f7da..8b523b90b9 100644 --- a/osu.Game/Overlays/Settings/Sections/OnlineSection.cs +++ b/osu.Game/Overlays/Settings/Sections/OnlineSection.cs @@ -3,13 +3,15 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; +using osu.Game.Localisation; using osu.Game.Overlays.Settings.Sections.Online; namespace osu.Game.Overlays.Settings.Sections { public class OnlineSection : SettingsSection { - public override string Header => "Online"; + public override LocalisableString Header => OnlineSettingsStrings.OnlineSectionHeader; public override Drawable CreateIcon() => new SpriteIcon { From ac52b891489afc591ee4d5b08c54547c5c43de52 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Thu, 12 Aug 2021 12:26:42 +0800 Subject: [PATCH 1026/2442] Add Maintenance and Debug SettingsStrings --- osu.Game/Localisation/DebugSettingsStrings.cs | 49 ++++++++++++ .../MaintenanceSettingsStrings.cs | 74 +++++++++++++++++++ .../Sections/Debug/GeneralSettings.cs | 9 ++- .../Settings/Sections/Debug/MemorySettings.cs | 5 +- .../Settings/Sections/DebugSection.cs | 4 +- .../Maintenance/DirectorySelectScreen.cs | 3 +- .../Sections/Maintenance/GeneralSettings.cs | 21 +++--- .../Settings/Sections/MaintenanceSection.cs | 4 +- 8 files changed, 150 insertions(+), 19 deletions(-) create mode 100644 osu.Game/Localisation/DebugSettingsStrings.cs create mode 100644 osu.Game/Localisation/MaintenanceSettingsStrings.cs diff --git a/osu.Game/Localisation/DebugSettingsStrings.cs b/osu.Game/Localisation/DebugSettingsStrings.cs new file mode 100644 index 0000000000..dd21739096 --- /dev/null +++ b/osu.Game/Localisation/DebugSettingsStrings.cs @@ -0,0 +1,49 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class DebugSettingsStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.DebugSettings"; + + /// + /// "Debug" + /// + public static LocalisableString DebugSectionHeader => new TranslatableString(getKey(@"debug_section_header"), @"Debug"); + + /// + /// "General" + /// + public static LocalisableString GeneralHeader => new TranslatableString(getKey(@"general_header"), @"General"); + + /// + /// "Show log overlay" + /// + public static LocalisableString ShowLogOverlay => new TranslatableString(getKey(@"show_log_overlay"), @"Show log overlay"); + + /// + /// "Bypass front-to-back render pass" + /// + public static LocalisableString BypassFrontToBackPass => new TranslatableString(getKey(@"bypass_front_to_back_pass"), @"Bypass front-to-back render pass"); + + /// + /// "Import files" + /// + public static LocalisableString ImportFiles => new TranslatableString(getKey(@"import_files"), @"Import files"); + + /// + /// "Memory" + /// + public static LocalisableString MemoryHeader => new TranslatableString(getKey(@"memory_header"), @"Memory"); + + /// + /// "Clear all caches" + /// + public static LocalisableString ClearAllCaches => new TranslatableString(getKey(@"clear_all_caches"), @"Clear all caches"); + + private static string getKey(string key) => $"{prefix}:{key}"; + } +} diff --git a/osu.Game/Localisation/MaintenanceSettingsStrings.cs b/osu.Game/Localisation/MaintenanceSettingsStrings.cs new file mode 100644 index 0000000000..a0e1a9ddab --- /dev/null +++ b/osu.Game/Localisation/MaintenanceSettingsStrings.cs @@ -0,0 +1,74 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class MaintenanceSettingsStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.MaintenanceSettings"; + + /// + /// "Maintenance" + /// + public static LocalisableString MaintenanceSectionHeader => new TranslatableString(getKey(@"maintenance_section_header"), @"Maintenance"); + + /// + /// "Select directory" + /// + public static LocalisableString SelectDirectory => new TranslatableString(getKey(@"select_directory"), @"Select directory"); + + /// + /// "Import beatmaps from stable" + /// + public static LocalisableString ImportBeatmapsFromStable => new TranslatableString(getKey(@"import_beatmaps_from_stable"), @"Import beatmaps from stable"); + + /// + /// "Delete ALL beatmaps" + /// + public static LocalisableString DeleteAllBeatmaps => new TranslatableString(getKey(@"delete_all_beatmaps"), @"Delete ALL beatmaps"); + + /// + /// "Import scores from stable" + /// + public static LocalisableString ImportScoresFromStable => new TranslatableString(getKey(@"import_scores_from_stable"), @"Import scores from stable"); + + /// + /// "Delete ALL scores" + /// + public static LocalisableString DeleteAllScores => new TranslatableString(getKey(@"delete_all_scores"), @"Delete ALL scores"); + + /// + /// "Import skins from stable" + /// + public static LocalisableString ImportSkinsFromStable => new TranslatableString(getKey(@"import_skins_from_stable"), @"Import skins from stable"); + + /// + /// "Delete ALL skins" + /// + public static LocalisableString DeleteAllSkins => new TranslatableString(getKey(@"delete_all_skins"), @"Delete ALL skins"); + + /// + /// "Import collections from stable" + /// + public static LocalisableString ImportCollectionsFromStable => new TranslatableString(getKey(@"import_collections_from_stable"), @"Import collections from stable"); + + /// + /// "Delete ALL collections" + /// + public static LocalisableString DeleteAllCollections => new TranslatableString(getKey(@"delete_all_collections"), @"Delete ALL collections"); + + /// + /// "Restore all hidden difficulties" + /// + public static LocalisableString RestoreAllHiddenDifficulties => new TranslatableString(getKey(@"restore_all_hidden_difficulties"), @"Restore all hidden difficulties"); + + /// + /// "Restore all recently deleted beatmaps" + /// + public static LocalisableString RestoreAllRecentlyDeletedBeatmaps => new TranslatableString(getKey(@"restore_all_recently_deleted_beatmaps"), @"Restore all recently deleted beatmaps"); + + private static string getKey(string key) => $"{prefix}:{key}"; + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs index 2b868cab85..25e20911b8 100644 --- a/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Debug/GeneralSettings.cs @@ -6,13 +6,14 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Framework.Screens; +using osu.Game.Localisation; using osu.Game.Screens.Import; namespace osu.Game.Overlays.Settings.Sections.Debug { public class GeneralSettings : SettingsSubsection { - protected override LocalisableString Header => "General"; + protected override LocalisableString Header => DebugSettingsStrings.GeneralHeader; [BackgroundDependencyLoader(true)] private void load(FrameworkDebugConfigManager config, FrameworkConfigManager frameworkConfig, OsuGame game) @@ -21,18 +22,18 @@ namespace osu.Game.Overlays.Settings.Sections.Debug { new SettingsCheckbox { - LabelText = "Show log overlay", + LabelText = DebugSettingsStrings.ShowLogOverlay, Current = frameworkConfig.GetBindable(FrameworkSetting.ShowLogOverlay) }, new SettingsCheckbox { - LabelText = "Bypass front-to-back render pass", + LabelText = DebugSettingsStrings.BypassFrontToBackPass, Current = config.GetBindable(DebugSetting.BypassFrontToBackPass) } }; Add(new SettingsButton { - Text = "Import files", + Text = DebugSettingsStrings.ImportFiles, Action = () => game?.PerformFromScreen(menu => menu.Push(new FileImportScreen())) }); } diff --git a/osu.Game/Overlays/Settings/Sections/Debug/MemorySettings.cs b/osu.Game/Overlays/Settings/Sections/Debug/MemorySettings.cs index bf7fb351c0..07fb0aca5a 100644 --- a/osu.Game/Overlays/Settings/Sections/Debug/MemorySettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Debug/MemorySettings.cs @@ -6,12 +6,13 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Framework.Platform; +using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Debug { public class MemorySettings : SettingsSubsection { - protected override LocalisableString Header => "Memory"; + protected override LocalisableString Header => DebugSettingsStrings.MemoryHeader; [BackgroundDependencyLoader] private void load(FrameworkDebugConfigManager config, GameHost host) @@ -20,7 +21,7 @@ namespace osu.Game.Overlays.Settings.Sections.Debug { new SettingsButton { - Text = "Clear all caches", + Text = DebugSettingsStrings.ClearAllCaches, Action = host.Collect }, }; diff --git a/osu.Game/Overlays/Settings/Sections/DebugSection.cs b/osu.Game/Overlays/Settings/Sections/DebugSection.cs index 44d4088972..aa85ec920c 100644 --- a/osu.Game/Overlays/Settings/Sections/DebugSection.cs +++ b/osu.Game/Overlays/Settings/Sections/DebugSection.cs @@ -3,13 +3,15 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; +using osu.Game.Localisation; using osu.Game.Overlays.Settings.Sections.Debug; namespace osu.Game.Overlays.Settings.Sections { public class DebugSection : SettingsSection { - public override string Header => "Debug"; + public override LocalisableString Header => DebugSettingsStrings.DebugSectionHeader; public override Drawable CreateIcon() => new SpriteIcon { diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs index 5392ba5d93..e509cac2f1 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs @@ -14,6 +14,7 @@ using osu.Framework.Localisation; using osu.Game.Graphics.UserInterface; using osu.Framework.Screens; using osu.Game.Graphics.Containers; +using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Maintenance { @@ -104,7 +105,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance Origin = Anchor.Centre, Width = 300, Margin = new MarginPadding(10), - Text = "Select directory", + Text = MaintenanceSettingsStrings.SelectDirectory, Action = () => OnSelection(directorySelector.CurrentPath.Value) }, } diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index b9a408b1f8..803c8332c1 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -11,6 +11,7 @@ using osu.Game.Beatmaps; using osu.Game.Collections; using osu.Game.Database; using osu.Game.Graphics.UserInterface; +using osu.Game.Localisation; using osu.Game.Scoring; using osu.Game.Skinning; @@ -37,7 +38,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { Add(importBeatmapsButton = new SettingsButton { - Text = "Import beatmaps from stable", + Text = MaintenanceSettingsStrings.ImportBeatmapsFromStable, Action = () => { importBeatmapsButton.Enabled.Value = false; @@ -48,7 +49,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance Add(deleteBeatmapsButton = new DangerousSettingsButton { - Text = "Delete ALL beatmaps", + Text = MaintenanceSettingsStrings.DeleteAllBeatmaps, Action = () => { dialogOverlay?.Push(new DeleteAllBeatmapsDialog(() => @@ -63,7 +64,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { Add(importScoresButton = new SettingsButton { - Text = "Import scores from stable", + Text = MaintenanceSettingsStrings.ImportScoresFromStable, Action = () => { importScoresButton.Enabled.Value = false; @@ -74,7 +75,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance Add(deleteScoresButton = new DangerousSettingsButton { - Text = "Delete ALL scores", + Text = MaintenanceSettingsStrings.DeleteAllScores, Action = () => { dialogOverlay?.Push(new DeleteAllBeatmapsDialog(() => @@ -89,7 +90,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { Add(importSkinsButton = new SettingsButton { - Text = "Import skins from stable", + Text = MaintenanceSettingsStrings.ImportSkinsFromStable, Action = () => { importSkinsButton.Enabled.Value = false; @@ -100,7 +101,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance Add(deleteSkinsButton = new DangerousSettingsButton { - Text = "Delete ALL skins", + Text = MaintenanceSettingsStrings.DeleteAllSkins, Action = () => { dialogOverlay?.Push(new DeleteAllBeatmapsDialog(() => @@ -117,7 +118,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { Add(importCollectionsButton = new SettingsButton { - Text = "Import collections from stable", + Text = MaintenanceSettingsStrings.ImportCollectionsFromStable, Action = () => { importCollectionsButton.Enabled.Value = false; @@ -128,7 +129,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance Add(new DangerousSettingsButton { - Text = "Delete ALL collections", + Text = MaintenanceSettingsStrings.DeleteAllCollections, Action = () => { dialogOverlay?.Push(new DeleteAllBeatmapsDialog(collectionManager.DeleteAll)); @@ -140,7 +141,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { restoreButton = new SettingsButton { - Text = "Restore all hidden difficulties", + Text = MaintenanceSettingsStrings.RestoreAllHiddenDifficulties, Action = () => { restoreButton.Enabled.Value = false; @@ -153,7 +154,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance }, undeleteButton = new SettingsButton { - Text = "Restore all recently deleted beatmaps", + Text = MaintenanceSettingsStrings.RestoreAllRecentlyDeletedBeatmaps, Action = () => { undeleteButton.Enabled.Value = false; diff --git a/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs b/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs index 73c88b8e71..fa0c06167b 100644 --- a/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs +++ b/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs @@ -3,6 +3,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; +using osu.Game.Localisation; using osu.Game.Overlays.Settings.Sections.Maintenance; using osuTK; @@ -10,7 +12,7 @@ namespace osu.Game.Overlays.Settings.Sections { public class MaintenanceSection : SettingsSection { - public override string Header => "Maintenance"; + public override LocalisableString Header => MaintenanceSettingsStrings.MaintenanceSectionHeader; public override Drawable CreateIcon() => new SpriteIcon { From 3f434c8474f19be27afc46254461bd5bfceeadfe Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Thu, 12 Aug 2021 13:42:16 +0800 Subject: [PATCH 1027/2442] Resolve code quality issue --- osu.Game/Localisation/GameplaySettingsStrings.cs | 1 - osu.sln.DotSettings | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Localisation/GameplaySettingsStrings.cs b/osu.Game/Localisation/GameplaySettingsStrings.cs index 172387bbb8..4354272ab5 100644 --- a/osu.Game/Localisation/GameplaySettingsStrings.cs +++ b/osu.Game/Localisation/GameplaySettingsStrings.cs @@ -89,7 +89,6 @@ namespace osu.Game.Localisation /// public static LocalisableString IncreaseFirstObjectVisibility => new TranslatableString(getKey(@"increase_first_object_visibility"), @"Increase visibility of first object when visual impairment mods are enabled"); - private static string getKey(string key) => $"{prefix}:{key}"; } } diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 139ee02b96..d884ea31c5 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -304,6 +304,7 @@ AABB API BPM + FPS GC GL GLSL From 4d6101f4e5443438d95cd9c3df835b070e3aad78 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Aug 2021 14:56:58 +0900 Subject: [PATCH 1028/2442] Lease selected room while in match screen to avoid lounge potentially changing it --- .../OnlinePlay/Lounge/Components/RoomsContainer.cs | 4 +++- .../Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 13 ++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index d2253b2d2c..e951caa8a8 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -137,7 +137,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components roomFlow.Remove(toRemove); - selectedRoom.Value = null; + // selection may have a lease due to being in a sub screen. + if (!selectedRoom.Disabled) + selectedRoom.Value = null; } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 68bd3cd613..64c068e07e 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; @@ -49,6 +50,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private RoomsContainer roomsContainer; + [CanBeNull] + private LeasedBindable selectionLease; + [BackgroundDependencyLoader] private void load() { @@ -144,6 +148,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { base.OnResuming(last); + Debug.Assert(selectionLease != null); + + selectionLease.Return(); + selectionLease = null; + if (selectedRoom.Value?.RoomID.Value == null) selectedRoom.Value = new Room(); @@ -210,7 +219,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge protected virtual void OpenNewRoom(Room room) { - selectedRoom.Value = room; + selectionLease = selectedRoom.BeginLease(false); + Debug.Assert(selectionLease != null); + selectionLease.Value = room; this.Push(CreateRoomSubScreen(room)); } From 524128441ede2f5ab66a5582ad2a3cd3a1650d08 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Aug 2021 15:03:05 +0900 Subject: [PATCH 1029/2442] Mark `PollingComponent` test as headless --- osu.Game.Tests/Visual/Components/TestScenePollingComponent.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Components/TestScenePollingComponent.cs b/osu.Game.Tests/Visual/Components/TestScenePollingComponent.cs index 2236f85b92..cc8503589d 100644 --- a/osu.Game.Tests/Visual/Components/TestScenePollingComponent.cs +++ b/osu.Game.Tests/Visual/Components/TestScenePollingComponent.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Logging; +using osu.Framework.Testing; using osu.Game.Graphics.Sprites; using osu.Game.Online; using osuTK; @@ -15,6 +16,7 @@ using osuTK.Graphics; namespace osu.Game.Tests.Visual.Components { + [HeadlessTest] public class TestScenePollingComponent : OsuTestScene { private Container pollBox; From b713ba1bd441ae1cf5c7cfcbb9f51c3d0666abe1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Aug 2021 15:11:32 +0900 Subject: [PATCH 1030/2442] Add test coverage --- .../TestScenePlaylistsLoungeSubScreen.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs index ecdb046203..aff0e7ba4b 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs @@ -62,6 +62,24 @@ namespace osu.Game.Tests.Visual.Playlists AddUntilStep("last room is not masked", () => checkRoomVisible(roomsContainer.Rooms[^1])); } + [Test] + public void TestEnteringRoomTakesLeaseOnSelection() + { + AddStep("add rooms", () => RoomManager.AddRooms(1)); + + AddAssert("selected room is not disabled", () => !OnlinePlayDependencies.SelectedRoom.Disabled); + + AddStep("select room", () => roomsContainer.Rooms[0].TriggerClick()); + AddAssert("selected room is non-null", () => OnlinePlayDependencies.SelectedRoom.Value != null); + + AddStep("enter room", () => roomsContainer.Rooms[0].TriggerClick()); + + AddUntilStep("wait for match load", () => Stack.CurrentScreen is PlaylistsRoomSubScreen); + + AddAssert("selected room is non-null", () => OnlinePlayDependencies.SelectedRoom.Value != null); + AddAssert("selected room is disabled", () => OnlinePlayDependencies.SelectedRoom.Disabled); + } + private bool checkRoomVisible(DrawableRoom room) => loungeScreen.ChildrenOfType().First().ScreenSpaceDrawQuad .Contains(room.ScreenSpaceDrawQuad.Centre); From f4591b01d7a4bb4f8e6ef8a9eb02bd47fe527a39 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Aug 2021 15:21:00 +0900 Subject: [PATCH 1031/2442] Add test step to show leaderboard in expanded state by default --- osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs index 17fe09f2c6..950f255a02 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs @@ -40,6 +40,7 @@ namespace osu.Game.Tests.Visual.Gameplay }); AddStep("add local player", () => createLeaderboardScore(playerScore, new User { Username = "You", Id = 3 }, true)); + AddStep("toggle expanded", () => leaderboard.Expanded.Value = !leaderboard.Expanded.Value); AddSliderStep("set player score", 50, 5000000, 1222333, v => playerScore.Value = v); } From 14fe2eea2a907f5cf2130aed4204e350500909e2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 6 Aug 2021 19:40:29 +0900 Subject: [PATCH 1032/2442] Add empty step to multiplayer TestEmpty() test --- osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 0ffa5209e3..7ff0368fc2 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -87,6 +87,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public void TestEmpty() { // used to test the flow of multiplayer from visual tests. + AddStep("empty step", () => { }); } [Test] From 68dbbc17e88f89ca19cdd58436811e308f4411fa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Aug 2021 16:17:14 +0900 Subject: [PATCH 1033/2442] Add support for automatic scrolling in gameplay leaderboard --- .../Gameplay/TestSceneGameplayLeaderboard.cs | 21 ++- .../Screens/Play/HUD/GameplayLeaderboard.cs | 131 ++++++++++++++++-- .../Play/HUD/GameplayLeaderboardScore.cs | 13 +- 3 files changed, 144 insertions(+), 21 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs index 950f255a02..10257f9027 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs @@ -84,6 +84,23 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("add frenzibyte", () => createRandomScore(new User { Username = "frenzibyte", Id = 14210502 })); } + [Test] + public void TestMaxHeight() + { + int playerNumber = 1; + AddRepeatStep("add 3 other players", () => createRandomScore(new User { Username = $"Player {playerNumber++}" }), 3); + checkHeight(4); + + AddRepeatStep("add 4 other players", () => createRandomScore(new User { Username = $"Player {playerNumber++}" }), 4); + checkHeight(8); + + AddRepeatStep("add 4 other players", () => createRandomScore(new User { Username = $"Player {playerNumber++}" }), 4); + checkHeight(8); + + void checkHeight(int panelCount) + => AddAssert($"leaderboard height is {panelCount} panels high", () => leaderboard.DrawHeight == (GameplayLeaderboardScore.PANEL_HEIGHT + leaderboard.Spacing) * panelCount); + } + private void createRandomScore(User user) => createLeaderboardScore(new BindableDouble(RNG.Next(0, 5_000_000)), user); private void createLeaderboardScore(BindableDouble score, User user, bool isTracked = false) @@ -94,9 +111,11 @@ namespace osu.Game.Tests.Visual.Gameplay private class TestGameplayLeaderboard : GameplayLeaderboard { + public float Spacing => Flow.Spacing.Y; + public bool CheckPositionByUsername(string username, int? expectedPosition) { - var scoreItem = this.FirstOrDefault(i => i.User?.Username == username); + var scoreItem = Flow.FirstOrDefault(i => i.User?.Username == username); return scoreItem != null && scoreItem.ScorePosition == expectedPosition; } diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index 63cb4f89f5..807289d664 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -6,29 +6,58 @@ using System.Linq; using JetBrains.Annotations; using osu.Framework.Bindables; using osu.Framework.Caching; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Containers; using osu.Game.Users; using osuTK; +using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD { - public class GameplayLeaderboard : FillFlowContainer + public class GameplayLeaderboard : CompositeDrawable { + private readonly int maxPanels; private readonly Cached sorting = new Cached(); public Bindable Expanded = new Bindable(); - public GameplayLeaderboard() + protected readonly FillFlowContainer Flow; + + private bool requiresScroll; + private readonly OsuScrollContainer scroll; + + private GameplayLeaderboardScore trackedScore; + + /// + /// Create a new leaderboard. + /// + /// The maximum panels to show at once. Defines the maximum height of this component. + public GameplayLeaderboard(int maxPanels = 8) { + this.maxPanels = maxPanels; + Width = GameplayLeaderboardScore.EXTENDED_WIDTH + GameplayLeaderboardScore.SHEAR_WIDTH; - Direction = FillDirection.Vertical; - - Spacing = new Vector2(2.5f); - - LayoutDuration = 250; - LayoutEasing = Easing.OutQuint; + InternalChildren = new Drawable[] + { + scroll = new ManualScrollScrollContainer + { + RelativeSizeAxes = Axes.Both, + Child = Flow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + X = GameplayLeaderboardScore.SHEAR_WIDTH, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(2.5f), + LayoutDuration = 450, + LayoutEasing = Easing.OutQuint, + } + } + }; } protected override void LoadComplete() @@ -50,22 +79,83 @@ namespace osu.Game.Screens.Play.HUD { var drawable = CreateLeaderboardScoreDrawable(user, isTracked); + if (isTracked) + { + if (trackedScore != null) + throw new InvalidOperationException("Cannot track more than one scores."); + + trackedScore = drawable; + } + drawable.Expanded.BindTo(Expanded); - base.Add(drawable); + Flow.Add(drawable); drawable.TotalScore.BindValueChanged(_ => sorting.Invalidate(), true); - Height = Count * (GameplayLeaderboardScore.PANEL_HEIGHT + Spacing.Y); + int displayCount = Math.Min(Flow.Count, maxPanels); + Height = displayCount * (GameplayLeaderboardScore.PANEL_HEIGHT + Flow.Spacing.Y); + requiresScroll = displayCount != Flow.Count; return drawable; } + public void Clear() + { + Flow.Clear(); + trackedScore = null; + scroll.ScrollToStart(false); + } + protected virtual GameplayLeaderboardScore CreateLeaderboardScoreDrawable(User user, bool isTracked) => new GameplayLeaderboardScore(user, isTracked); - public sealed override void Add(GameplayLeaderboardScore drawable) + protected override void Update() { - throw new NotSupportedException($"Use {nameof(AddPlayer)} instead."); + base.Update(); + + if (requiresScroll && trackedScore != null) + { + float scrollTarget = scroll.GetChildPosInContent(trackedScore) + trackedScore.DrawHeight / 2 - scroll.DrawHeight / 2; + scroll.ScrollTo(scrollTarget, false); + } + + const float panel_height = GameplayLeaderboardScore.PANEL_HEIGHT; + + float fadeBottom = scroll.Current + scroll.DrawHeight; + float fadeTop = scroll.Current + panel_height; + + if (scroll.Current <= 0) fadeTop -= panel_height; + if (!scroll.IsScrolledToEnd()) fadeBottom -= panel_height; + + // logic is mostly shared with Leaderboard, copied here for simplicity. + foreach (var c in Flow.Children) + { + float topY = c.ToSpaceOfOtherDrawable(Vector2.Zero, Flow).Y; + float bottomY = topY + panel_height; + + bool requireTopFade = requiresScroll && topY <= fadeTop; + bool requireBottomFade = requiresScroll && bottomY >= fadeBottom; + + if (!requireTopFade && !requireBottomFade) + c.Colour = Color4.White; + else if (topY > fadeBottom + panel_height || bottomY < fadeTop - panel_height) + c.Colour = Color4.Transparent; + else + { + if (bottomY - fadeBottom > 0 && requiresScroll) + { + c.Colour = ColourInfo.GradientVertical( + Color4.White.Opacity(Math.Min(1 - (topY - fadeBottom) / panel_height, 1)), + Color4.White.Opacity(Math.Min(1 - (bottomY - fadeBottom) / panel_height, 1))); + } + else if (requiresScroll) + { + c.Colour = ColourInfo.GradientVertical( + Color4.White.Opacity(Math.Min(1 - (fadeTop - topY) / panel_height, 1)), + Color4.White.Opacity(Math.Min(1 - (fadeTop - bottomY) / panel_height, 1))); + } + } + } } private void sort() @@ -73,15 +163,26 @@ namespace osu.Game.Screens.Play.HUD if (sorting.IsValid) return; - var orderedByScore = this.OrderByDescending(i => i.TotalScore.Value).ToList(); + var orderedByScore = Flow.OrderByDescending(i => i.TotalScore.Value).ToList(); - for (int i = 0; i < Count; i++) + for (int i = 0; i < Flow.Count; i++) { - SetLayoutPosition(orderedByScore[i], i); + Flow.SetLayoutPosition(orderedByScore[i], i); orderedByScore[i].ScorePosition = i + 1; } sorting.Validate(); } + + private class ManualScrollScrollContainer : OsuScrollContainer + { + public ManualScrollScrollContainer() + { + ScrollbarVisible = false; + } + + public override bool HandlePositionalInput => false; + public override bool HandleNonPositionalInput => false; + } } } diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs index 433bf78e9b..85cf9d1966 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboardScore.cs @@ -81,7 +81,10 @@ namespace osu.Game.Screens.Play.HUD [CanBeNull] public User User { get; } - private readonly bool trackedPlayer; + /// + /// Whether this score is the local user or a replay player (and should be focused / always visible). + /// + public readonly bool Tracked; private Container mainFillContainer; @@ -97,11 +100,11 @@ namespace osu.Game.Screens.Play.HUD /// Creates a new . /// /// The score's player. - /// Whether the player is the local user or a replay player. - public GameplayLeaderboardScore([CanBeNull] User user, bool trackedPlayer) + /// Whether the player is the local user or a replay player. + public GameplayLeaderboardScore([CanBeNull] User user, bool tracked) { User = user; - this.trackedPlayer = trackedPlayer; + Tracked = tracked; AutoSizeAxes = Axes.X; Height = PANEL_HEIGHT; @@ -338,7 +341,7 @@ namespace osu.Game.Screens.Play.HUD panelColour = BackgroundColour ?? Color4Extensions.FromHex("7fcc33"); textColour = TextColour ?? Color4.White; } - else if (trackedPlayer) + else if (Tracked) { widthExtension = true; panelColour = BackgroundColour ?? Color4Extensions.FromHex("ffd966"); From e84224f64c057f0d2caac52ad54ac06dc92a77e1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Aug 2021 16:17:27 +0900 Subject: [PATCH 1034/2442] Rename `AddPlayer` method now that there's no conflict --- osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs | 2 +- osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs | 2 +- osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs index 10257f9027..0441c5641e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs @@ -105,7 +105,7 @@ namespace osu.Game.Tests.Visual.Gameplay private void createLeaderboardScore(BindableDouble score, User user, bool isTracked = false) { - var leaderboardScore = leaderboard.AddPlayer(user, isTracked); + var leaderboardScore = leaderboard.Add(user, isTracked); leaderboardScore.TotalScore.BindTo(score); } diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index 807289d664..14fcaa91fa 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -75,7 +75,7 @@ namespace osu.Game.Screens.Play.HUD /// Whether the player should be tracked on the leaderboard. /// Set to true for the local player or a player whose replay is currently being played. /// - public ILeaderboardScore AddPlayer([CanBeNull] User user, bool isTracked) + public ILeaderboardScore Add([CanBeNull] User user, bool isTracked) { var drawable = CreateLeaderboardScoreDrawable(user, isTracked); diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 3f9258930e..1c5743a656 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -85,7 +85,7 @@ namespace osu.Game.Screens.Play.HUD var trackedUser = UserScores[user.Id]; - var leaderboardScore = AddPlayer(user, user.Id == api.LocalUser.Value.Id); + var leaderboardScore = Add(user, user.Id == api.LocalUser.Value.Id); leaderboardScore.Accuracy.BindTo(trackedUser.Accuracy); leaderboardScore.TotalScore.BindTo(trackedUser.Score); leaderboardScore.Combo.BindTo(trackedUser.CurrentCombo); From ab1cc6ad4856503da10296681e28b896d9be8a03 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Aug 2021 16:50:09 +0900 Subject: [PATCH 1035/2442] Fix padding around recent participants icon being uneven --- .../OnlinePlay/Lounge/Components/RecentParticipantsList.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs index 7f3fdf01d0..6766a51840 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs @@ -54,7 +54,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, Spacing = new Vector2(4), - Padding = new MarginPadding { Left = 8, Right = 16 }, + Padding = new MarginPadding { Right = 16 }, Children = new Drawable[] { new SpriteIcon @@ -62,6 +62,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Size = new Vector2(16), + Margin = new MarginPadding(8), Icon = FontAwesome.Solid.User, }, avatarFlow = new FillFlowContainer From d07bb10d0294fd4986442cb415b603248d022ed4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Aug 2021 16:52:30 +0900 Subject: [PATCH 1036/2442] Remove breadcrumbs from header --- osu.Game/Screens/OnlinePlay/Header.cs | 88 +++++---------------------- 1 file changed, 16 insertions(+), 72 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Header.cs b/osu.Game/Screens/OnlinePlay/Header.cs index bf0a53cbb6..c025099ebe 100644 --- a/osu.Game/Screens/OnlinePlay/Header.cs +++ b/osu.Game/Screens/OnlinePlay/Header.cs @@ -2,19 +2,15 @@ // See the LICENCE file in the repository root for full licence text. using Humanizer; +using JetBrains.Annotations; using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.UserInterface; using osu.Framework.Screens; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osuTK; -using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay { @@ -22,52 +18,29 @@ namespace osu.Game.Screens.OnlinePlay { public const float HEIGHT = 80; + private readonly ScreenStack stack; + private readonly MultiHeaderTitle title; + public Header(string mainTitle, ScreenStack stack) { + this.stack = stack; + RelativeSizeAxes = Axes.X; Height = HEIGHT; + Padding = new MarginPadding { Left = WaveOverlayContainer.WIDTH_PADDING }; - HeaderBreadcrumbControl breadcrumbs; - MultiHeaderTitle title; - - Children = new Drawable[] + Child = title = new MultiHeaderTitle(mainTitle) { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"#1f1921"), - }, - new Container - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = WaveOverlayContainer.WIDTH_PADDING + OsuScreen.HORIZONTAL_OVERFLOW_PADDING }, - Children = new Drawable[] - { - title = new MultiHeaderTitle(mainTitle) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.BottomLeft, - }, - breadcrumbs = new HeaderBreadcrumbControl(stack) - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft - } - }, - }, + Anchor = Anchor.CentreLeft, + Origin = Anchor.BottomLeft, }; - breadcrumbs.Current.ValueChanged += screen => - { - if (screen.NewValue is IOnlinePlaySubScreen onlineSubScreen) - title.Screen = onlineSubScreen; - }; - - breadcrumbs.Current.TriggerChange(); + stack.ScreenPushed += (_, __) => updateSubScreenTitle(); + stack.ScreenExited += (_, __) => updateSubScreenTitle(); } + private void updateSubScreenTitle() => title.Screen = stack.CurrentScreen as IOnlinePlaySubScreen; + private class MultiHeaderTitle : CompositeDrawable { private const float spacing = 6; @@ -75,9 +48,10 @@ namespace osu.Game.Screens.OnlinePlay private readonly OsuSpriteText dot; private readonly OsuSpriteText pageTitle; + [CanBeNull] public IOnlinePlaySubScreen Screen { - set => pageTitle.Text = value.ShortTitle.Titleize(); + set => pageTitle.Text = value?.ShortTitle.Titleize() ?? string.Empty; } public MultiHeaderTitle(string mainTitle) @@ -125,35 +99,5 @@ namespace osu.Game.Screens.OnlinePlay pageTitle.Colour = dot.Colour = colours.Yellow; } } - - private class HeaderBreadcrumbControl : ScreenBreadcrumbControl - { - public HeaderBreadcrumbControl(ScreenStack stack) - : base(stack) - { - RelativeSizeAxes = Axes.X; - StripColour = Color4.Transparent; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - AccentColour = Color4Extensions.FromHex("#e35c99"); - } - - protected override TabItem CreateTabItem(IScreen value) => new HeaderBreadcrumbTabItem(value) - { - AccentColour = AccentColour - }; - - private class HeaderBreadcrumbTabItem : BreadcrumbTabItem - { - public HeaderBreadcrumbTabItem(IScreen value) - : base(value) - { - Bar.Colour = Color4.Transparent; - } - } - } } } From 3b7aa262d5b559f72511af9c8dfb6230f3c9ba1e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Aug 2021 16:52:35 +0900 Subject: [PATCH 1037/2442] Make header overlap content --- osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index fee612c0a5..6238000e85 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -101,7 +101,6 @@ namespace osu.Game.Screens.OnlinePlay new Container { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = Header.HEIGHT }, Children = new[] { header = new Container From b75c20fee421138d8b10b538fe55baf6d59c5d78 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Aug 2021 18:02:00 +0900 Subject: [PATCH 1038/2442] Adjust positioning and paddings --- osu.Game/Screens/OnlinePlay/Header.cs | 2 +- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 2 ++ osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs | 2 ++ osu.Game/Screens/Select/SongSelect.cs | 9 +++++---- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Header.cs b/osu.Game/Screens/OnlinePlay/Header.cs index c025099ebe..58c67a51e8 100644 --- a/osu.Game/Screens/OnlinePlay/Header.cs +++ b/osu.Game/Screens/OnlinePlay/Header.cs @@ -32,7 +32,7 @@ namespace osu.Game.Screens.OnlinePlay Child = title = new MultiHeaderTitle(mainTitle) { Anchor = Anchor.CentreLeft, - Origin = Anchor.BottomLeft, + Origin = Anchor.CentreLeft, }; stack.ScreenPushed += (_, __) => updateSubScreenTitle(); diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index a53e253581..b29a1ab1b6 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -61,6 +61,8 @@ namespace osu.Game.Screens.OnlinePlay.Match protected RoomSubScreen() { + Padding = new MarginPadding { Top = Header.HEIGHT }; + AddRangeInternal(new Drawable[] { BeatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index be28de5c43..1502463022 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -59,6 +59,8 @@ namespace osu.Game.Screens.OnlinePlay [BackgroundDependencyLoader] private void load() { + LeftArea.Padding = new MarginPadding { Top = Header.HEIGHT }; + initialBeatmap = Beatmap.Value; initialRuleset = Ruleset.Value; initialMods = Mods.Value.ToList(); diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 270addc8e6..0e113a71bc 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -79,6 +79,8 @@ namespace osu.Game.Screens.Select protected BeatmapCarousel Carousel { get; private set; } + protected Container LeftArea { get; private set; } + private BeatmapInfoWedge beatmapInfoWedge; private DialogOverlay dialogOverlay; @@ -186,12 +188,12 @@ namespace osu.Game.Screens.Select { new Drawable[] { - new Container + LeftArea = new Container { Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, RelativeSizeAxes = Axes.Both, - + Padding = new MarginPadding { Top = left_area_padding }, Children = new Drawable[] { beatmapInfoWedge = new BeatmapInfoWedge @@ -200,7 +202,6 @@ namespace osu.Game.Screens.Select RelativeSizeAxes = Axes.X, Margin = new MarginPadding { - Top = left_area_padding, Right = left_area_padding, }, }, @@ -210,7 +211,7 @@ namespace osu.Game.Screens.Select Padding = new MarginPadding { Bottom = Footer.HEIGHT, - Top = WEDGE_HEIGHT + left_area_padding, + Top = WEDGE_HEIGHT, Left = left_area_padding, Right = left_area_padding * 2, }, From b58b5ec2b47af2376b9ff355c5591629af397d55 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 12 Aug 2021 12:14:39 +0300 Subject: [PATCH 1039/2442] Apply horizontal offset changing once per frame The previous way was causing every-frame invalidation when an overlay is visible. --- osu.Game/OsuGame.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 1539d984ae..b6809c0290 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1013,10 +1013,14 @@ namespace osu.Game ScreenOffsetContainer.Padding = new MarginPadding { Top = ToolbarOffset }; overlayContent.Padding = new MarginPadding { Top = ToolbarOffset }; - ScreenOffsetContainer.X = 0f; + var horizontalOffset = 0f; - if (Settings.IsLoaded) ScreenOffsetContainer.X += (ToLocalSpace(Settings.ScreenSpaceDrawQuad.TopRight).X) * SCREEN_OFFSET_RATIO; - if (Notifications.IsLoaded) ScreenOffsetContainer.X += (ToLocalSpace(Notifications.ScreenSpaceDrawQuad.TopLeft).X - DrawWidth) * SCREEN_OFFSET_RATIO; + if (Settings.IsLoaded) + horizontalOffset += (ToLocalSpace(Settings.ScreenSpaceDrawQuad.TopRight).X) * SCREEN_OFFSET_RATIO; + if (Notifications.IsLoaded) + horizontalOffset += (ToLocalSpace(Notifications.ScreenSpaceDrawQuad.TopLeft).X - DrawWidth) * SCREEN_OFFSET_RATIO; + + ScreenOffsetContainer.X = horizontalOffset; MenuCursorContainer.CanShowCursor = (ScreenStack.CurrentScreen as IOsuScreen)?.CursorVisible ?? false; } From 8b29f52d9f119a2bfc72b1e6bbb85412f5e54dfb Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 12 Aug 2021 16:54:58 +0700 Subject: [PATCH 1040/2442] update supporter note text Co-authored-by: Dean Herbert --- osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index c0d78cc887..3d6a469247 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -114,7 +114,7 @@ namespace osu.Game.Overlays.Changelog t.Colour = colour.PinkLighter; }) { - Text = "Not only will you help speed development, but you will also get some extra features and customisations!", + Text = ChangelogStrings.SupportText2.ToString(), Margin = new MarginPadding { Top = 10 }, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, From 98859b3759a78649b85b5611c6ff6087e6a64610 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 12 Aug 2021 17:06:11 +0700 Subject: [PATCH 1041/2442] cache pink colour provider Co-authored-by: Dean Herbert --- osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index 3d6a469247..9bfb141eea 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -25,6 +25,9 @@ namespace osu.Game.Overlays.Changelog private readonly FillFlowContainer textContainer; private readonly Container imageContainer; + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink); + public ChangelogSupporterPromo() { RelativeSizeAxes = Axes.X; From 18684ad21f52e2821810181a0170f4a98eda66ed Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 12 Aug 2021 17:08:54 +0700 Subject: [PATCH 1042/2442] remove colour creation in add link --- osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index 9bfb141eea..477745e0ba 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -125,11 +125,7 @@ namespace osu.Game.Overlays.Changelog }; supportLinkText.AddText("Support further development of osu! and "); - supportLinkText.AddLink("become an osu!supporter", "https://osu.ppy.sh/home/support", t => - { - t.Colour = colour.PinkDark; - t.Font = t.Font.With(weight: FontWeight.Bold); - }); + supportLinkText.AddLink("become an osu!supporter", "https://osu.ppy.sh/home/support", t => t.Font = t.Font.With(weight: FontWeight.Bold)); supportLinkText.AddText(" today!"); imageContainer.Children = new Drawable[] From ab7bd1df9dade8869369175d53055efcbf40a42d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Aug 2021 19:27:06 +0900 Subject: [PATCH 1043/2442] Use full-screen background --- .../Screens/OnlinePlay/OnlinePlayScreen.cs | 62 ++++--------------- 1 file changed, 13 insertions(+), 49 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 6238000e85..21e4f047f0 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -21,8 +21,8 @@ using osu.Game.Screens.Menu; using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.OnlinePlay.Lounge.Components; -using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Users; +using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay { @@ -68,9 +68,6 @@ namespace osu.Game.Screens.OnlinePlay [Resolved(CanBeNull = true)] private OsuLogo logo { get; set; } - private Drawable header; - private Drawable headerBackground; - protected OnlinePlayScreen() { Anchor = Anchor.Centre; @@ -101,41 +98,21 @@ namespace osu.Game.Screens.OnlinePlay new Container { RelativeSizeAxes = Axes.Both, - Children = new[] + Children = new Drawable[] { - header = new Container + new HeaderBackgroundSprite { - RelativeSizeAxes = Axes.X, - Height = 400, - Children = new[] - { - headerBackground = new Container - { - RelativeSizeAxes = Axes.Both, - Width = 1.25f, - Masking = true, - Children = new Drawable[] - { - new HeaderBackgroundSprite - { - RelativeSizeAxes = Axes.X, - Height = 400 // Keep a static height so the header doesn't change as it's resized between subscreens - }, - } - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Bottom = -1 }, // 1px padding to avoid a 1px gap due to masking - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(backgroundColour.Opacity(0.5f), backgroundColour) - }, - } - } + RelativeSizeAxes = Axes.Both }, - screenStack = new OnlinePlaySubScreenStack { RelativeSizeAxes = Axes.Both } + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.9f), Color4.Black.Opacity(0.6f)) + }, + screenStack = new OnlinePlaySubScreenStack + { + RelativeSizeAxes = Axes.Both + } } }, new Header(ScreenTitle, screenStack), @@ -288,19 +265,6 @@ namespace osu.Game.Screens.OnlinePlay private void subScreenChanged(IScreen lastScreen, IScreen newScreen) { - switch (newScreen) - { - case LoungeSubScreen _: - header.Delay(OnlinePlaySubScreen.RESUME_TRANSITION_DELAY).ResizeHeightTo(400, OnlinePlaySubScreen.APPEAR_DURATION, Easing.OutQuint); - headerBackground.MoveToX(0, OnlinePlaySubScreen.X_MOVE_DURATION, Easing.OutQuint); - break; - - case RoomSubScreen _: - header.ResizeHeightTo(135, OnlinePlaySubScreen.APPEAR_DURATION, Easing.OutQuint); - headerBackground.MoveToX(-OnlinePlaySubScreen.X_SHIFT, OnlinePlaySubScreen.X_MOVE_DURATION, Easing.OutQuint); - break; - } - if (lastScreen is IOsuScreen lastOsuScreen) Activity.UnbindFrom(lastOsuScreen.Activity); From 512382987e0e85d4fc6825f065fe94677627851a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Aug 2021 17:02:55 +0900 Subject: [PATCH 1044/2442] Add colour provider for multiplayer usage --- .../Visual/Multiplayer/TestSceneDrawableRoom.cs | 4 ++++ .../Multiplayer/TestSceneRecentParticipantsList.cs | 5 +++++ osu.Game/Overlays/OverlayColourProvider.cs | 7 ++++++- .../OnlinePlay/Lounge/Components/DrawableRoom.cs | 13 ++++++------- .../Lounge/Components/RecentParticipantsList.cs | 5 +++-- osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs | 3 +++ 6 files changed, 27 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index e6a1bbeb92..299bbacf08 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -13,6 +13,7 @@ using osu.Framework.Utils; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Rooms; using osu.Game.Online.Rooms.RoomStatuses; +using osu.Game.Overlays; using osu.Game.Rulesets.Osu; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Tests.Beatmaps; @@ -26,6 +27,9 @@ namespace osu.Game.Tests.Visual.Multiplayer [Cached] private readonly Bindable selectedRoom = new Bindable(); + [Cached] + protected readonly OverlayColourProvider ColourProvider = new OverlayColourProvider(OverlayColourScheme.Plum); + [Test] public void TestMultipleStatuses() { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs index f05c092dfb..27ccd33440 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs @@ -3,9 +3,11 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Online.Rooms; +using osu.Game.Overlays; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Tests.Visual.OnlinePlay; using osu.Game.Users; @@ -17,6 +19,9 @@ namespace osu.Game.Tests.Visual.Multiplayer { private RecentParticipantsList list; + [Cached] + protected readonly OverlayColourProvider ColourProvider = new OverlayColourProvider(OverlayColourScheme.Plum); + [SetUp] public new void Setup() => Schedule(() => { diff --git a/osu.Game/Overlays/OverlayColourProvider.cs b/osu.Game/Overlays/OverlayColourProvider.cs index 008e7696e1..e7b3e6d873 100644 --- a/osu.Game/Overlays/OverlayColourProvider.cs +++ b/osu.Game/Overlays/OverlayColourProvider.cs @@ -18,6 +18,7 @@ namespace osu.Game.Overlays public static OverlayColourProvider Green { get; } = new OverlayColourProvider(OverlayColourScheme.Green); public static OverlayColourProvider Purple { get; } = new OverlayColourProvider(OverlayColourScheme.Purple); public static OverlayColourProvider Blue { get; } = new OverlayColourProvider(OverlayColourScheme.Blue); + public static OverlayColourProvider Plum { get; } = new OverlayColourProvider(OverlayColourScheme.Plum); public OverlayColourProvider(OverlayColourScheme colourScheme) { @@ -80,6 +81,9 @@ namespace osu.Game.Overlays case OverlayColourScheme.Blue: return 200 / 360f; + + case OverlayColourScheme.Plum: + return 320 / 360f; } } } @@ -92,6 +96,7 @@ namespace osu.Game.Overlays Lime, Green, Purple, - Blue + Blue, + Plum, } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 3a56be64e0..c2a872efe5 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -28,6 +28,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Input.Bindings; using osu.Game.Online.Rooms; +using osu.Game.Overlays; using osu.Game.Screens.OnlinePlay.Components; using osuTK; using osuTK.Graphics; @@ -41,8 +42,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private const float transition_duration = 60; private const float height = 100; - private static readonly Color4 background_colour = Color4Extensions.FromHex(@"#27302E"); - public event Action StateChanged; protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds(); @@ -154,7 +153,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } [BackgroundDependencyLoader] - private void load(OsuColour colours, AudioManager audio) + private void load(OverlayColourProvider colours, AudioManager audio) { Children = new Drawable[] { @@ -167,7 +166,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components new Box { RelativeSizeAxes = Axes.Both, - Colour = background_colour, + Colour = colours.Background5, }, new OnlinePlayBackgroundSprite { @@ -208,12 +207,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components new Box { RelativeSizeAxes = Axes.Both, - Colour = background_colour, + Colour = colours.Background5, }, new Box { RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal(background_colour, background_colour.Opacity(0.3f)) + Colour = ColourInfo.GradientHorizontal(colours.Background5, colours.Background5.Opacity(0.3f)) }, } } @@ -531,7 +530,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private OsuPasswordTextBox passwordTextbox; [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load() { Child = new FillFlowContainer { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs index 6766a51840..e22f55a716 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; using osu.Game.Users; using osu.Game.Users.Drawables; using osuTK; @@ -31,7 +32,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } [BackgroundDependencyLoader] - private void load() + private void load(OverlayColourProvider colours) { InternalChildren = new Drawable[] { @@ -44,7 +45,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Child = new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"#2E3835") + Colour = colours.Background4, } }, new FillFlowContainer diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index fee612c0a5..86ce61f845 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -29,6 +29,9 @@ namespace osu.Game.Screens.OnlinePlay [Cached] public abstract class OnlinePlayScreen : OsuScreen, IHasSubScreenStack { + [Cached] + protected readonly OverlayColourProvider ColourProvider = new OverlayColourProvider(OverlayColourScheme.Plum); + public override bool CursorVisible => (screenStack?.CurrentScreen as IOnlinePlaySubScreen)?.CursorVisible ?? true; // this is required due to PlayerLoader eventually being pushed to the main stack From 2c07b68f6fca41f997dc32d642fe899e14ca4af1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Aug 2021 17:24:21 +0900 Subject: [PATCH 1045/2442] Fix incorrect colour for hidden user display --- .../Components/RecentParticipantsList.cs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs index e22f55a716..e6e879d652 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs @@ -4,7 +4,6 @@ using System.Collections.Specialized; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -14,7 +13,6 @@ using osu.Game.Overlays; using osu.Game.Users; using osu.Game.Users.Drawables; using osuTK; -using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { @@ -208,9 +206,14 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private int count; - private readonly SpriteText countText; + private readonly SpriteText countText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; - public HiddenUserCount() + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colours) { Size = new Vector2(avatar_size); Alpha = 0; @@ -224,13 +227,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4.Black + Colour = colours.Background5, }, - countText = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - } + countText } }; } From 127fd4d292d9ac8c95e3f2ec3c6dfb7572e45377 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Aug 2021 17:28:10 +0900 Subject: [PATCH 1046/2442] Match font weight of design for hidden user count --- .../OnlinePlay/Lounge/Components/RecentParticipantsList.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs index e6e879d652..32568f5058 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osu.Game.Users; @@ -210,6 +211,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { Anchor = Anchor.Centre, Origin = Anchor.Centre, + Font = OsuFont.Default.With(weight: FontWeight.Bold), }; [BackgroundDependencyLoader] From 8a67304b9fe6a93ac60105ae34ee570fa99b1ea0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Aug 2021 18:10:06 +0900 Subject: [PATCH 1047/2442] Fix recent participants hidden user logic not handling edge case correctly Hiding just one user never makes sense, so this will now always show up to the required circle count until two users are required to be hidden. This will make the listing more consistent with the width requirement spec. --- .../TestSceneRecentParticipantsList.cs | 46 +++++++++++++----- .../Lounge/Components/DrawableRoom.cs | 4 +- .../Components/RecentParticipantsList.cs | 48 +++++++++++++------ 3 files changed, 71 insertions(+), 27 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs index 27ccd33440..2436da4655 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs @@ -31,12 +31,36 @@ namespace osu.Game.Tests.Visual.Multiplayer { Anchor = Anchor.Centre, Origin = Anchor.Centre, - NumberOfAvatars = 3 + NumberOfCircles = 3 }; }); [Test] - public void TestAvatarCount() + public void TestCircleCountNearLimit() + { + AddStep("add 8 users", () => + { + for (int i = 0; i < 8; i++) + addUser(i); + }); + AddStep("set 8 circles", () => list.NumberOfCircles = 8); + AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0); + + AddStep("add one more user", () => addUser(9)); + AddAssert("2 hidden users", () => list.ChildrenOfType().Single().Count == 2); + + AddStep("remove first user", () => removeUserAt(0)); + AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0); + + AddStep("add one more user", () => addUser(9)); + AddAssert("2 hidden users", () => list.ChildrenOfType().Single().Count == 2); + + AddStep("remove last user", () => removeUserAt(8)); + AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0); + } + + [Test] + public void TestCircleCount() { AddStep("add 50 users", () => { @@ -44,12 +68,12 @@ namespace osu.Game.Tests.Visual.Multiplayer addUser(i); }); - AddStep("set 3 avatars", () => list.NumberOfAvatars = 3); - AddAssert("3 avatars displayed", () => list.ChildrenOfType().Count() == 3); + AddStep("set 3 circles", () => list.NumberOfCircles = 3); + AddAssert("3 circles displayed", () => list.ChildrenOfType().Count() == 3); AddAssert("47 hidden users", () => list.ChildrenOfType().Single().Count == 47); - AddStep("set 10 avatars", () => list.NumberOfAvatars = 10); - AddAssert("10 avatars displayed", () => list.ChildrenOfType().Count() == 10); + AddStep("set 10 circles", () => list.NumberOfCircles = 10); + AddAssert("10 circles displayed", () => list.ChildrenOfType().Count() == 10); AddAssert("40 hidden users", () => list.ChildrenOfType().Single().Count == 40); } @@ -63,24 +87,24 @@ namespace osu.Game.Tests.Visual.Multiplayer }); AddStep("remove from start", () => removeUserAt(0)); - AddAssert("3 avatars displayed", () => list.ChildrenOfType().Count() == 3); + AddAssert("3 circles displayed", () => list.ChildrenOfType().Count() == 3); AddAssert("46 hidden users", () => list.ChildrenOfType().Single().Count == 46); AddStep("remove from end", () => removeUserAt(SelectedRoom.Value.RecentParticipants.Count - 1)); - AddAssert("3 avatars displayed", () => list.ChildrenOfType().Count() == 3); + AddAssert("3 circles displayed", () => list.ChildrenOfType().Count() == 3); AddAssert("45 hidden users", () => list.ChildrenOfType().Single().Count == 45); AddRepeatStep("remove 45 users", () => removeUserAt(0), 45); - AddAssert("3 avatars displayed", () => list.ChildrenOfType().Count() == 3); + AddAssert("3 circles displayed", () => list.ChildrenOfType().Count() == 3); AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0); AddAssert("hidden users bubble hidden", () => list.ChildrenOfType().Single().Alpha < 0.5f); AddStep("remove another user", () => removeUserAt(0)); - AddAssert("2 avatars displayed", () => list.ChildrenOfType().Count() == 2); + AddAssert("2 circles displayed", () => list.ChildrenOfType().Count() == 2); AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0); AddRepeatStep("remove the remaining two users", () => removeUserAt(0), 2); - AddAssert("0 avatars displayed", () => !list.ChildrenOfType().Any()); + AddAssert("0 circles displayed", () => !list.ChildrenOfType().Any()); } private void addUser(int id) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index c2a872efe5..193fb0cf57 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -120,7 +120,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components numberOfAvatars = value; if (recentParticipantsList != null) - recentParticipantsList.NumberOfAvatars = value; + recentParticipantsList.NumberOfCircles = value; } } @@ -315,7 +315,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, - NumberOfAvatars = NumberOfAvatars + NumberOfCircles = NumberOfAvatars } } }, diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs index 32568f5058..ab13f27469 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Specialized; +using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -21,6 +22,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { private const float avatar_size = 36; + private bool canDisplayMoreUsers = true; + private FillFlowContainer avatarFlow; private HiddenUserCount hiddenUsers; @@ -91,14 +94,17 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components ParticipantCount.BindValueChanged(_ => updateHiddenUserCount(), true); } - private int numberOfAvatars = 3; + private int numberOfCircles = 3; - public int NumberOfAvatars + /// + /// The maximum number of circles visible (including the "hidden count" circle in the overflow case). + /// + public int NumberOfCircles { - get => numberOfAvatars; + get => numberOfCircles; set { - numberOfAvatars = value; + numberOfCircles = value; if (LoadState < LoadState.Loaded) return; @@ -107,6 +113,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components clearUsers(); foreach (var u in RecentParticipants) addUser(u); + + updateHiddenUserCount(); } } @@ -136,34 +144,46 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components addUser(u); break; } + + updateHiddenUserCount(); } private void addUser(User user) { - if (avatarFlow.Count < NumberOfAvatars) - avatarFlow.Add(new CircularAvatar { User = user }); + if (!canDisplayMoreUsers) + return; - updateHiddenUserCount(); + if (avatarFlow.Count < NumberOfCircles) + avatarFlow.Add(new CircularAvatar { User = user }); + else if (avatarFlow.Count == NumberOfCircles) + avatarFlow.Remove(avatarFlow.Last()); } private void removeUser(User user) { - if (avatarFlow.RemoveAll(a => a.User == user) > 0) - { - if (RecentParticipants.Count >= NumberOfAvatars) - avatarFlow.Add(new CircularAvatar { User = RecentParticipants.First(u => avatarFlow.All(a => a.User != u)) }); - } + var nextUser = RecentParticipants.FirstOrDefault(u => avatarFlow.All(a => a.User != u)); + if (nextUser == null) + return; - updateHiddenUserCount(); + if (canDisplayMoreUsers || NumberOfCircles == RecentParticipants.Count) + avatarFlow.Add(new CircularAvatar { User = nextUser }); } private void clearUsers() { + canDisplayMoreUsers = true; avatarFlow.Clear(); updateHiddenUserCount(); } - private void updateHiddenUserCount() => hiddenUsers.Count = ParticipantCount.Value - avatarFlow.Count; + private void updateHiddenUserCount() + { + int count = ParticipantCount.Value - avatarFlow.Count; + + Debug.Assert(count != 1); + + hiddenUsers.Count = count; + } private class CircularAvatar : CompositeDrawable { From 7b66616dc40df3f9f87ac0afe67d46915d4c2711 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Aug 2021 18:47:22 +0900 Subject: [PATCH 1048/2442] Simplify logic and test/fix edge case --- .../TestSceneRecentParticipantsList.cs | 36 ++++++++--- .../Components/RecentParticipantsList.cs | 60 ++++++++++--------- 2 files changed, 62 insertions(+), 34 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs index 2436da4655..7d3880dd16 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs @@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Anchor = Anchor.Centre, Origin = Anchor.Centre, - NumberOfCircles = 3 + NumberOfCircles = 4 }; }); @@ -43,6 +43,7 @@ namespace osu.Game.Tests.Visual.Multiplayer for (int i = 0; i < 8; i++) addUser(i); }); + AddStep("set 8 circles", () => list.NumberOfCircles = 8); AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0); @@ -59,6 +60,27 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0); } + [Test] + public void TestHiddenUsersBecomeDisplayed() + { + AddStep("add 8 users", () => + { + for (int i = 0; i < 8; i++) + addUser(i); + }); + + AddStep("set 3 circles", () => list.NumberOfCircles = 3); + + for (int i = 0; i < 8; i++) + { + AddStep("remove user", () => removeUserAt(0)); + int remainingUsers = 7 - i; + + int displayedUsers = remainingUsers > 3 ? 2 : remainingUsers; + AddAssert($"{displayedUsers} avatars displayed", () => list.ChildrenOfType().Count() == displayedUsers); + } + } + [Test] public void TestCircleCount() { @@ -69,12 +91,12 @@ namespace osu.Game.Tests.Visual.Multiplayer }); AddStep("set 3 circles", () => list.NumberOfCircles = 3); - AddAssert("3 circles displayed", () => list.ChildrenOfType().Count() == 3); - AddAssert("47 hidden users", () => list.ChildrenOfType().Single().Count == 47); + AddAssert("2 users displayed", () => list.ChildrenOfType().Count() == 2); + AddAssert("48 hidden users", () => list.ChildrenOfType().Single().Count == 48); AddStep("set 10 circles", () => list.NumberOfCircles = 10); - AddAssert("10 circles displayed", () => list.ChildrenOfType().Count() == 10); - AddAssert("40 hidden users", () => list.ChildrenOfType().Single().Count == 40); + AddAssert("9 users displayed", () => list.ChildrenOfType().Count() == 9); + AddAssert("41 hidden users", () => list.ChildrenOfType().Single().Count == 41); } [Test] @@ -109,18 +131,18 @@ namespace osu.Game.Tests.Visual.Multiplayer private void addUser(int id) { - SelectedRoom.Value.ParticipantCount.Value++; SelectedRoom.Value.RecentParticipants.Add(new User { Id = id, Username = $"User {id}" }); + SelectedRoom.Value.ParticipantCount.Value++; } private void removeUserAt(int index) { - SelectedRoom.Value.ParticipantCount.Value--; SelectedRoom.Value.RecentParticipants.RemoveAt(index); + SelectedRoom.Value.ParticipantCount.Value--; } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs index ab13f27469..3085286ccb 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs @@ -22,8 +22,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { private const float avatar_size = 36; - private bool canDisplayMoreUsers = true; - private FillFlowContainer avatarFlow; private HiddenUserCount hiddenUsers; @@ -91,10 +89,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components base.LoadComplete(); RecentParticipants.BindCollectionChanged(onParticipantsChanged, true); - ParticipantCount.BindValueChanged(_ => updateHiddenUserCount(), true); + ParticipantCount.BindValueChanged(_ => updateHiddenUsers(), true); } - private int numberOfCircles = 3; + private int numberOfCircles = 4; /// /// The maximum number of circles visible (including the "hidden count" circle in the overflow case). @@ -114,7 +112,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components foreach (var u in RecentParticipants) addUser(u); - updateHiddenUserCount(); + updateHiddenUsers(); } } @@ -145,44 +143,43 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components break; } - updateHiddenUserCount(); + updateHiddenUsers(); } + private int displayedCircles => avatarFlow.Count + (hiddenUsers.Count > 0 ? 1 : 0); + private void addUser(User user) { - if (!canDisplayMoreUsers) - return; - - if (avatarFlow.Count < NumberOfCircles) + if (displayedCircles < NumberOfCircles) avatarFlow.Add(new CircularAvatar { User = user }); - else if (avatarFlow.Count == NumberOfCircles) - avatarFlow.Remove(avatarFlow.Last()); } private void removeUser(User user) { - var nextUser = RecentParticipants.FirstOrDefault(u => avatarFlow.All(a => a.User != u)); - if (nextUser == null) - return; - - if (canDisplayMoreUsers || NumberOfCircles == RecentParticipants.Count) - avatarFlow.Add(new CircularAvatar { User = nextUser }); + avatarFlow.RemoveAll(a => a.User == user); } private void clearUsers() { - canDisplayMoreUsers = true; avatarFlow.Clear(); - updateHiddenUserCount(); + updateHiddenUsers(); } - private void updateHiddenUserCount() + private void updateHiddenUsers() { - int count = ParticipantCount.Value - avatarFlow.Count; + int hiddenCount = 0; + if (RecentParticipants.Count > NumberOfCircles) + hiddenCount = ParticipantCount.Value - NumberOfCircles + 1; - Debug.Assert(count != 1); + hiddenUsers.Count = hiddenCount; - hiddenUsers.Count = count; + if (displayedCircles > NumberOfCircles) + avatarFlow.Remove(avatarFlow.Last()); + else if (displayedCircles < NumberOfCircles) + { + var nextUser = RecentParticipants.FirstOrDefault(u => avatarFlow.All(a => a.User != u)); + if (nextUser != null) addUser(nextUser); + } } private class CircularAvatar : CompositeDrawable @@ -193,9 +190,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components set => avatar.User = value; } - private readonly UpdateableAvatar avatar; + private readonly UpdateableAvatar avatar = new UpdateableAvatar(showUsernameTooltip: true) { RelativeSizeAxes = Axes.Both }; - public CircularAvatar() + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colours) { Size = new Vector2(avatar_size); @@ -203,7 +201,15 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { RelativeSizeAxes = Axes.Both, Masking = true, - Child = avatar = new UpdateableAvatar(showUsernameTooltip: true) { RelativeSizeAxes = Axes.Both } + Children = new Drawable[] + { + new Box + { + Colour = colours.Background5, + RelativeSizeAxes = Axes.Both, + }, + avatar + } }; } } From 10195e0c53574a8036b691541357a2595fa878fe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Aug 2021 18:51:50 +0900 Subject: [PATCH 1049/2442] Add total user count --- .../Components/RecentParticipantsList.cs | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs index 3085286ccb..bc658f45e4 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Specialized; -using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -23,7 +22,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private const float avatar_size = 36; private FillFlowContainer avatarFlow; + private HiddenUserCount hiddenUsers; + private OsuSpriteText totalCount; public RecentParticipantsList() { @@ -63,16 +64,23 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Size = new Vector2(16), - Margin = new MarginPadding(8), + Margin = new MarginPadding { Left = 8 }, Icon = FontAwesome.Solid.User, }, + totalCount = new OsuSpriteText + { + Font = OsuFont.Default.With(weight: FontWeight.Bold), + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, avatarFlow = new FillFlowContainer { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - Spacing = new Vector2(4) + Spacing = new Vector2(4), + Margin = new MarginPadding { Left = 4 }, }, hiddenUsers = new HiddenUserCount { @@ -89,7 +97,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components base.LoadComplete(); RecentParticipants.BindCollectionChanged(onParticipantsChanged, true); - ParticipantCount.BindValueChanged(_ => updateHiddenUsers(), true); + ParticipantCount.BindValueChanged(_ => + { + updateHiddenUsers(); + totalCount.Text = ParticipantCount.Value.ToString(); + }, true); } private int numberOfCircles = 4; From e89aea1fc2b91265ee518b450b9fb3ca02b781c0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Aug 2021 19:38:53 +0900 Subject: [PATCH 1050/2442] Add some padding between scroll bar and content --- .../Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index b9b034272d..5e5863c7c4 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -50,6 +50,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; + // account for the fact we are in a scroll container and want a bit of spacing from the scroll bar. + Padding = new MarginPadding { Right = 5 }; + InternalChild = new OsuContextMenuContainer { RelativeSizeAxes = Axes.X, From 047b37788bb8087c248986de83a4b4732d362020 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Aug 2021 19:48:15 +0900 Subject: [PATCH 1051/2442] Merge online play filter control with the lounge subscreen --- .../TestScenePlaylistsFilterControl.cs | 23 ---- .../Lounge/Components/FilterControl.cs | 125 ----------------- .../Components/PlaylistsFilterControl.cs | 57 -------- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 129 ++++++++++++++---- .../Multiplayer/MultiplayerFilterControl.cs | 17 --- .../Multiplayer/MultiplayerLoungeSubScreen.cs | 7 +- .../Playlists/PlaylistsLoungeSubScreen.cs | 44 +++++- 7 files changed, 154 insertions(+), 248 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Playlists/TestScenePlaylistsFilterControl.cs delete mode 100644 osu.Game/Screens/OnlinePlay/Lounge/Components/FilterControl.cs delete mode 100644 osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistsFilterControl.cs delete mode 100644 osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerFilterControl.cs diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsFilterControl.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsFilterControl.cs deleted file mode 100644 index 40e191dd7e..0000000000 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsFilterControl.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Game.Screens.OnlinePlay.Lounge.Components; - -namespace osu.Game.Tests.Visual.Playlists -{ - public class TestScenePlaylistsFilterControl : OsuTestScene - { - public TestScenePlaylistsFilterControl() - { - Child = new PlaylistsFilterControl - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.X, - Width = 0.7f, - Height = 80, - }; - } - } -} diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterControl.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterControl.cs deleted file mode 100644 index e2f02fca68..0000000000 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterControl.cs +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.UserInterface; -using osu.Framework.Threading; -using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; -using osu.Game.Rulesets; -using osuTK; - -namespace osu.Game.Screens.OnlinePlay.Lounge.Components -{ - public abstract class FilterControl : CompositeDrawable - { - protected readonly FillFlowContainer Filters; - - [Resolved(CanBeNull = true)] - private Bindable filter { get; set; } - - [Resolved] - private IBindable ruleset { get; set; } - - private readonly SearchTextBox search; - private readonly Dropdown statusDropdown; - - protected FilterControl() - { - RelativeSizeAxes = Axes.X; - Height = 70; - - InternalChild = new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(10), - Children = new Drawable[] - { - search = new FilterSearchTextBox - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - RelativeSizeAxes = Axes.X, - Width = 0.6f, - }, - Filters = new FillFlowContainer - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(10), - Child = statusDropdown = new SlimEnumDropdown - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - RelativeSizeAxes = Axes.None, - Width = 160, - } - }, - } - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - filter ??= new Bindable(); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - search.Current.BindValueChanged(_ => updateFilterDebounced()); - ruleset.BindValueChanged(_ => UpdateFilter()); - statusDropdown.Current.BindValueChanged(_ => UpdateFilter(), true); - } - - private ScheduledDelegate scheduledFilterUpdate; - - private void updateFilterDebounced() - { - scheduledFilterUpdate?.Cancel(); - scheduledFilterUpdate = Scheduler.AddDelayed(UpdateFilter, 200); - } - - protected void UpdateFilter() => Scheduler.AddOnce(updateFilter); - - private void updateFilter() - { - scheduledFilterUpdate?.Cancel(); - - var criteria = CreateCriteria(); - criteria.SearchString = search.Current.Value; - criteria.Status = statusDropdown.Current.Value; - criteria.Ruleset = ruleset.Value; - - filter.Value = criteria; - } - - protected virtual FilterCriteria CreateCriteria() => new FilterCriteria(); - - public bool HoldFocus - { - get => search.HoldFocus; - set => search.HoldFocus = value; - } - - public void TakeFocus() => search.TakeFocus(); - - private class FilterSearchTextBox : SearchTextBox - { - [BackgroundDependencyLoader] - private void load() - { - BackgroundUnfocused = OsuColour.Gray(0.06f); - BackgroundFocused = OsuColour.Gray(0.12f); - } - } - } -} diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistsFilterControl.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistsFilterControl.cs deleted file mode 100644 index bbf34d3893..0000000000 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistsFilterControl.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Framework.Graphics.UserInterface; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Screens.OnlinePlay.Lounge.Components -{ - public class PlaylistsFilterControl : FilterControl - { - private readonly Dropdown categoryDropdown; - - public PlaylistsFilterControl() - { - Filters.Add(categoryDropdown = new SlimEnumDropdown - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - RelativeSizeAxes = Axes.None, - Width = 160, - }); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - categoryDropdown.Current.BindValueChanged(_ => UpdateFilter()); - } - - protected override FilterCriteria CreateCriteria() - { - var criteria = base.CreateCriteria(); - - switch (categoryDropdown.Current.Value) - { - case PlaylistsCategory.Normal: - criteria.Category = "normal"; - break; - - case PlaylistsCategory.Spotlight: - criteria.Category = "spotlight"; - break; - } - - return criteria; - } - - private enum PlaylistsCategory - { - Any, - Normal, - Spotlight - } - } -} diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 122b30b1d2..1c28405e40 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; @@ -9,24 +10,28 @@ using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Framework.Screens; +using osu.Framework.Threading; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Rooms; using osu.Game.Overlays; +using osu.Game.Rulesets; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Users; using osuTK; -using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay.Lounge { [Cached] public abstract class LoungeSubScreen : OnlinePlaySubScreen { + private const float button_height = 25; + public override string Title => "Lounge"; protected override UserActivity InitialActivity => new UserActivity.SearchingForLobby(); @@ -41,7 +46,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private readonly IBindable initialRoomsReceived = new Bindable(); private readonly IBindable operationInProgress = new Bindable(); - private FilterControl filter; private LoadingLayer loadingLayer; [Resolved] @@ -53,31 +57,33 @@ namespace osu.Game.Screens.OnlinePlay.Lounge [Resolved(CanBeNull = true)] private OngoingOperationTracker ongoingOperationTracker { get; set; } + [Resolved(CanBeNull = true)] + private Bindable filter { get; set; } + + [Resolved] + private IBindable ruleset { get; set; } + [CanBeNull] private IDisposable joiningRoomOperation { get; set; } private RoomsContainer roomsContainer; + private SearchTextBox searchTextBox; + private Dropdown statusDropdown; [BackgroundDependencyLoader] private void load() { + filter ??= new Bindable(new FilterCriteria()); + OsuScrollContainer scrollContainer; InternalChildren = new Drawable[] { - new Box - { - RelativeSizeAxes = Axes.X, - Height = 100, - Colour = Color4.Black, - Alpha = 0.5f, - }, new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { - Top = 20, Left = WaveOverlayContainer.WIDTH_PADDING, Right = WaveOverlayContainer.WIDTH_PADDING, }, @@ -86,26 +92,48 @@ namespace osu.Game.Screens.OnlinePlay.Lounge RelativeSizeAxes = Axes.Both, RowDimensions = new[] { - new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.Absolute, Header.HEIGHT), + new Dimension(GridSizeMode.Absolute, button_height), new Dimension(GridSizeMode.Absolute, 20) }, Content = new[] { + new Drawable[] + { + searchTextBox = new LoungeSearchTextBox + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.X, + Width = 0.6f, + }, + }, new Drawable[] { new Container { - RelativeSizeAxes = Axes.X, - Height = 70, - Depth = -1, + RelativeSizeAxes = Axes.Both, + Depth = float.MinValue, // Contained filters should appear over the top of rooms. Children = new Drawable[] { - filter = CreateFilterControl(), Buttons.WithChild(CreateNewRoomButton().With(d => { - d.Size = new Vector2(150, 25); + d.Size = new Vector2(150, button_height); d.Action = () => Open(); - })) + })), + new FillFlowContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10), + ChildrenEnumerable = CreateFilterControls().Select(f => f.With(d => + { + d.Anchor = Anchor.TopRight; + d.Origin = Anchor.TopRight; + })) + } } } }, @@ -145,6 +173,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { base.LoadComplete(); + searchTextBox.Current.BindValueChanged(_ => updateFilterDebounced()); + ruleset.BindValueChanged(_ => UpdateFilter()); + initialRoomsReceived.BindTo(RoomManager.InitialRoomsReceived); initialRoomsReceived.BindValueChanged(_ => updateLoadingLayer()); @@ -153,13 +184,50 @@ namespace osu.Game.Screens.OnlinePlay.Lounge operationInProgress.BindTo(ongoingOperationTracker.InProgress); operationInProgress.BindValueChanged(_ => updateLoadingLayer(), true); } + + updateFilter(); } - protected override void OnFocus(FocusEvent e) + #region Filtering + + protected void UpdateFilter() => Scheduler.AddOnce(updateFilter); + + private ScheduledDelegate scheduledFilterUpdate; + + private void updateFilterDebounced() { - filter.TakeFocus(); + scheduledFilterUpdate?.Cancel(); + scheduledFilterUpdate = Scheduler.AddDelayed(UpdateFilter, 200); } + private void updateFilter() + { + scheduledFilterUpdate?.Cancel(); + filter.Value = CreateFilterCriteria(); + } + + protected virtual FilterCriteria CreateFilterCriteria() => new FilterCriteria + { + SearchString = searchTextBox.Current.Value, + Ruleset = ruleset.Value, + Status = statusDropdown.Current.Value + }; + + protected virtual IEnumerable CreateFilterControls() + { + statusDropdown = new SlimEnumDropdown + { + RelativeSizeAxes = Axes.None, + Width = 160, + }; + + statusDropdown.Current.BindValueChanged(_ => UpdateFilter()); + + yield return statusDropdown; + } + + #endregion + public override void OnEntering(IScreen last) { base.OnEntering(last); @@ -191,14 +259,19 @@ namespace osu.Game.Screens.OnlinePlay.Lounge base.OnSuspending(next); } + protected override void OnFocus(FocusEvent e) + { + searchTextBox.TakeFocus(); + } + private void onReturning() { - filter.HoldFocus = true; + searchTextBox.HoldFocus = true; } private void onLeaving() { - filter.HoldFocus = false; + searchTextBox.HoldFocus = false; // ensure any password prompt is dismissed. this.HidePopover(); @@ -243,8 +316,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge this.Push(CreateRoomSubScreen(room)); } - protected abstract FilterControl CreateFilterControl(); - protected abstract OsuButton CreateNewRoomButton(); /// @@ -262,5 +333,15 @@ namespace osu.Game.Screens.OnlinePlay.Lounge else loadingLayer.Hide(); } + + private class LoungeSearchTextBox : SearchTextBox + { + [BackgroundDependencyLoader] + private void load() + { + BackgroundUnfocused = OsuColour.Gray(0.06f); + BackgroundFocused = OsuColour.Gray(0.12f); + } + } } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerFilterControl.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerFilterControl.cs deleted file mode 100644 index 37e0fd109a..0000000000 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerFilterControl.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Screens.OnlinePlay.Lounge.Components; - -namespace osu.Game.Screens.OnlinePlay.Multiplayer -{ - public class MultiplayerFilterControl : FilterControl - { - protected override FilterCriteria CreateCriteria() - { - var criteria = base.CreateCriteria(); - criteria.Category = "realtime"; - return criteria; - } - } -} diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs index 621ff8881f..a7eaa37faa 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs @@ -21,7 +21,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private MultiplayerClient client { get; set; } - protected override FilterControl CreateFilterControl() => new MultiplayerFilterControl(); + protected override FilterCriteria CreateFilterCriteria() + { + var criteria = base.CreateFilterCriteria(); + criteria.Category = "realtime"; + return criteria; + } protected override OsuButton CreateNewRoomButton() => new CreateMultiplayerMatchButton(); diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs index 4db1d6380d..254769d06b 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs @@ -1,7 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.Rooms; @@ -16,7 +20,38 @@ namespace osu.Game.Screens.OnlinePlay.Playlists [Resolved] private IAPIProvider api { get; set; } - protected override FilterControl CreateFilterControl() => new PlaylistsFilterControl(); + private Dropdown categoryDropdown; + + protected override IEnumerable CreateFilterControls() + { + categoryDropdown = new SlimEnumDropdown + { + RelativeSizeAxes = Axes.None, + Width = 160, + }; + + categoryDropdown.Current.BindValueChanged(_ => UpdateFilter()); + + return base.CreateFilterControls().Append(categoryDropdown); + } + + protected override FilterCriteria CreateFilterCriteria() + { + var criteria = base.CreateFilterCriteria(); + + switch (categoryDropdown.Current.Value) + { + case PlaylistsCategory.Normal: + criteria.Category = "normal"; + break; + + case PlaylistsCategory.Spotlight: + criteria.Category = "spotlight"; + break; + } + + return criteria; + } protected override OsuButton CreateNewRoomButton() => new CreatePlaylistsRoomButton(); @@ -30,5 +65,12 @@ namespace osu.Game.Screens.OnlinePlay.Playlists } protected override RoomSubScreen CreateRoomSubScreen(Room room) => new PlaylistsRoomSubScreen(room); + + private enum PlaylistsCategory + { + Any, + Normal, + Spotlight + } } } From 050f2d6b0d379a675c2d5dbee3d7ba80e85873f5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Aug 2021 19:51:03 +0900 Subject: [PATCH 1052/2442] Add background to room subscreen --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index b29a1ab1b6..0074479a4d 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -8,8 +8,10 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Screens; using osu.Game.Audio; using osu.Game.Beatmaps; @@ -65,6 +67,11 @@ namespace osu.Game.Screens.OnlinePlay.Match AddRangeInternal(new Drawable[] { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex(@"3e3a44") // This is super temporary. + }, BeatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker { SelectedItem = { BindTarget = SelectedItem } From 03351cf434d100356e1d16fdc86e07d05541a374 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Aug 2021 20:01:53 +0900 Subject: [PATCH 1053/2442] Add blur to background --- osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 21e4f047f0..d1c701974e 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -22,6 +22,7 @@ using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Users; +using osuTK; using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay @@ -100,9 +101,14 @@ namespace osu.Game.Screens.OnlinePlay RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new HeaderBackgroundSprite + new BufferedContainer { - RelativeSizeAxes = Axes.Both + RelativeSizeAxes = Axes.Both, + BlurSigma = new Vector2(10), + Child = new HeaderBackgroundSprite + { + RelativeSizeAxes = Axes.Both + } }, new Box { From 83703e42835630d5d600f529efd900df38785c15 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Aug 2021 20:08:14 +0900 Subject: [PATCH 1054/2442] Add colour provider to online play dependencies --- .../Visual/Multiplayer/TestSceneRecentParticipantsList.cs | 5 ----- .../Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs | 2 ++ 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs index 7d3880dd16..50ec2bf3ac 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs @@ -3,11 +3,9 @@ using System.Linq; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Online.Rooms; -using osu.Game.Overlays; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Tests.Visual.OnlinePlay; using osu.Game.Users; @@ -19,9 +17,6 @@ namespace osu.Game.Tests.Visual.Multiplayer { private RecentParticipantsList list; - [Cached] - protected readonly OverlayColourProvider ColourProvider = new OverlayColourProvider(OverlayColourScheme.Plum); - [SetUp] public new void Setup() => Schedule(() => { diff --git a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs index ddbbfe501b..05ba509a73 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs @@ -7,6 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Online.Rooms; +using osu.Game.Overlays; using osu.Game.Screens.OnlinePlay; using osu.Game.Screens.OnlinePlay.Lounge.Components; @@ -46,6 +47,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay CacheAs(Filter); CacheAs(OngoingOperationTracker); CacheAs(AvailabilityTracker); + CacheAs(new OverlayColourProvider(OverlayColourScheme.Plum)); } public object Get(Type type) From 3d7866e82da57bc422af3bb0cc22557653d10964 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 12 Aug 2021 14:14:54 +0300 Subject: [PATCH 1055/2442] Calculate horizontal offset on present overlays only --- osu.Game/OsuGame.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b6809c0290..b8a4388282 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1015,9 +1015,9 @@ namespace osu.Game var horizontalOffset = 0f; - if (Settings.IsLoaded) + if (Settings.IsLoaded && Settings.IsPresent) horizontalOffset += (ToLocalSpace(Settings.ScreenSpaceDrawQuad.TopRight).X) * SCREEN_OFFSET_RATIO; - if (Notifications.IsLoaded) + if (Notifications.IsLoaded && Notifications.IsPresent) horizontalOffset += (ToLocalSpace(Notifications.ScreenSpaceDrawQuad.TopLeft).X - DrawWidth) * SCREEN_OFFSET_RATIO; ScreenOffsetContainer.X = horizontalOffset; From bb1d74255e70081a806f9f3a9db773b6a3b06262 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 12 Aug 2021 14:15:51 +0300 Subject: [PATCH 1056/2442] Remove unrequired parenthesis --- osu.Game/OsuGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b8a4388282..f92f7e9e8c 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1016,7 +1016,7 @@ namespace osu.Game var horizontalOffset = 0f; if (Settings.IsLoaded && Settings.IsPresent) - horizontalOffset += (ToLocalSpace(Settings.ScreenSpaceDrawQuad.TopRight).X) * SCREEN_OFFSET_RATIO; + horizontalOffset += ToLocalSpace(Settings.ScreenSpaceDrawQuad.TopRight).X * SCREEN_OFFSET_RATIO; if (Notifications.IsLoaded && Notifications.IsPresent) horizontalOffset += (ToLocalSpace(Notifications.ScreenSpaceDrawQuad.TopLeft).X - DrawWidth) * SCREEN_OFFSET_RATIO; From 40db228e910c90b87e719ff08da9f12e7219a5a5 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 12 Aug 2021 19:34:44 +0700 Subject: [PATCH 1057/2442] change to osu text flow container --- osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index 477745e0ba..bdbba20f5c 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -111,7 +111,7 @@ namespace osu.Game.Overlays.Changelog RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, }, - new TextFlowContainer(t => + new OsuTextFlowContainer(t => { t.Font = t.Font.With(size: 12); t.Colour = colour.PinkLighter; From 66ba24e86540146fcab7ecba094e3d6d67c34da1 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 12 Aug 2021 20:36:47 +0700 Subject: [PATCH 1058/2442] create local link flow container --- .../Changelog/ChangelogSupporterPromo.cs | 46 ++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index bdbba20f5c..f617b4fc82 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.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; +using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -12,6 +14,7 @@ using osu.Framework.Graphics.Textures; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Online.Chat; using osu.Game.Resources.Localisation.Web; using osuTK; using osuTK.Graphics; @@ -25,9 +28,6 @@ namespace osu.Game.Overlays.Changelog private readonly FillFlowContainer textContainer; private readonly Container imageContainer; - [Cached] - private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink); - public ChangelogSupporterPromo() { RelativeSizeAxes = Axes.X; @@ -93,7 +93,7 @@ namespace osu.Game.Overlays.Changelog [BackgroundDependencyLoader] private void load(OsuColour colour, TextureStore textures) { - LinkFlowContainer supportLinkText; + SupporterPromoLinkFlowContainer supportLinkText; textContainer.Children = new Drawable[] { new OsuSpriteText @@ -102,7 +102,7 @@ namespace osu.Game.Overlays.Changelog Font = OsuFont.GetFont(size: 20, weight: FontWeight.Light), Margin = new MarginPadding { Bottom = 20 }, }, - supportLinkText = new LinkFlowContainer(t => + supportLinkText = new SupporterPromoLinkFlowContainer(t => { t.Font = t.Font.With(size: 14); t.Colour = colour.PinkLighter; @@ -125,7 +125,7 @@ namespace osu.Game.Overlays.Changelog }; supportLinkText.AddText("Support further development of osu! and "); - supportLinkText.AddLink("become an osu!supporter", "https://osu.ppy.sh/home/support", t => t.Font = t.Font.With(weight: FontWeight.Bold)); + supportLinkText.AddLink("become and osu!supporter", "https://osu.ppy.sh/home/support", t => t.Font = t.Font.With(weight: FontWeight.Bold)); supportLinkText.AddText(" today!"); imageContainer.Children = new Drawable[] @@ -149,5 +149,39 @@ namespace osu.Game.Overlays.Changelog }, }; } + + private class SupporterPromoLinkFlowContainer : LinkFlowContainer + { + public SupporterPromoLinkFlowContainer(Action defaultCreationParameters) + : base(defaultCreationParameters) + { + } + + public new void AddLink(string text, string url, Action creationParameters) => + AddInternal(new SupporterPromoLinkCompiler(AddText(text, creationParameters)) { Url = url }); + + private class SupporterPromoLinkCompiler : DrawableLinkCompiler + { + [Resolved(CanBeNull = true)] + private OsuGame game { get; set; } + + public string Url; + + public SupporterPromoLinkCompiler(IEnumerable parts) + : base(parts) + { + RelativeSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colour) + { + TooltipText = Url; + Action = () => game?.HandleLink(Url); + IdleColour = colour.PinkDark; + HoverColour = Color4.White; + } + } + } } } From 1069f9d50119bfc08e97cb28bc33a7608e568587 Mon Sep 17 00:00:00 2001 From: TheOmyNomy Date: Fri, 13 Aug 2021 00:13:03 +1000 Subject: [PATCH 1059/2442] Always add cursor trail for legacy cursor with disjoint trail --- .../Skinning/Legacy/LegacyCursorTrail.cs | 24 ++++++++--- .../UI/Cursor/CursorTrail.cs | 43 ++++++++++++------- 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs index f6fd3e36ab..f98d936b48 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs @@ -5,10 +5,12 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Shaders; using osu.Framework.Input.Events; using osu.Game.Configuration; using osu.Game.Rulesets.Osu.UI.Cursor; using osu.Game.Skinning; +using osuTK; namespace osu.Game.Rulesets.Osu.Skinning.Legacy { @@ -21,14 +23,18 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private double lastTrailTime; private IBindable cursorSize; + private Vector2? currentPosition; + public LegacyCursorTrail(ISkin skin) { this.skin = skin; } [BackgroundDependencyLoader] - private void load(OsuConfigManager config) + private void load(ShaderManager shaders, OsuConfigManager config) { + Shader = shaders.Load(@"LegacyCursorTrail", FragmentShaderDescriptor.TEXTURE); + Texture = skin.GetTexture("cursortrail"); disjointTrail = skin.GetTexture("cursormiddle") == null; @@ -59,18 +65,24 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy protected override float IntervalMultiplier => 1 / Math.Max(cursorSize.Value, 1); - protected override bool OnMouseMove(MouseMoveEvent e) + protected override void Update() { - if (!disjointTrail) - return base.OnMouseMove(e); + base.Update(); + + if (!disjointTrail || !currentPosition.HasValue) + return; if (Time.Current - lastTrailTime >= disjoint_trail_time_separation) { lastTrailTime = Time.Current; - return base.OnMouseMove(e); + AddTrail(currentPosition.Value); } + } - return false; + protected override bool OnMouseMove(MouseMoveEvent e) + { + currentPosition = e.ScreenSpaceMousePosition; + return base.OnMouseMove(e); } } } diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 7f86e9daf7..66a9302f5f 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -28,7 +28,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private readonly TrailPart[] parts = new TrailPart[max_sprites]; private int currentIndex; - private IShader shader; + + protected IShader Shader; + private double timeOffset; private float time; @@ -63,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor [BackgroundDependencyLoader] private void load(ShaderManager shaders) { - shader = shaders.Load(@"CursorTrail", FragmentShaderDescriptor.TEXTURE); + Shader = shaders.Load(@"CursorTrail", FragmentShaderDescriptor.TEXTURE); } protected override void LoadComplete() @@ -141,21 +143,32 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor protected override bool OnMouseMove(MouseMoveEvent e) { - Vector2 pos = e.ScreenSpaceMousePosition; + Vector2 position = e.ScreenSpaceMousePosition; if (lastPosition == null) { - lastPosition = pos; + lastPosition = position; resampler.AddPosition(lastPosition.Value); return base.OnMouseMove(e); } - foreach (Vector2 pos2 in resampler.AddPosition(pos)) - { - Trace.Assert(lastPosition.HasValue); + if (InterpolateMovements) + AddTrail(position); - if (InterpolateMovements) + return base.OnMouseMove(e); + } + + protected void AddTrail(Vector2 position) + { + if (!lastPosition.HasValue) + return; + + if (InterpolateMovements) + { + foreach (Vector2 pos2 in resampler.AddPosition(position)) { + Trace.Assert(lastPosition.HasValue); + // ReSharper disable once PossibleInvalidOperationException Vector2 pos1 = lastPosition.Value; Vector2 diff = pos2 - pos1; @@ -170,14 +183,12 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor addPart(lastPosition.Value); } } - else - { - lastPosition = pos2; - addPart(lastPosition.Value); - } } - - return base.OnMouseMove(e); + else + { + lastPosition = position; + addPart(lastPosition.Value); + } } private void addPart(Vector2 screenSpacePosition) @@ -223,7 +234,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { base.ApplyState(); - shader = Source.shader; + shader = Source.Shader; texture = Source.texture; size = Source.partSize; time = Source.time; From 5f67d991b4a02ad983cda08c4fc673618c6fc93e Mon Sep 17 00:00:00 2001 From: Lucas A Date: Thu, 12 Aug 2021 19:17:32 +0200 Subject: [PATCH 1060/2442] Localise beatmap info. --- osu.Game/Overlays/BeatmapSet/MetadataSection.cs | 3 ++- osu.Game/Overlays/BeatmapSet/MetadataType.cs | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/MetadataSection.cs b/osu.Game/Overlays/BeatmapSet/MetadataSection.cs index 3648c55714..f1b9f98528 100644 --- a/osu.Game/Overlays/BeatmapSet/MetadataSection.cs +++ b/osu.Game/Overlays/BeatmapSet/MetadataSection.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -46,7 +47,7 @@ namespace osu.Game.Overlays.BeatmapSet AutoSizeAxes = Axes.Y, Child = new OsuSpriteText { - Text = this.type.ToString(), + Text = this.type.GetLocalisableDescription(), Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 14), }, }, diff --git a/osu.Game/Overlays/BeatmapSet/MetadataType.cs b/osu.Game/Overlays/BeatmapSet/MetadataType.cs index 1ab4c6887e..f992dca0ef 100644 --- a/osu.Game/Overlays/BeatmapSet/MetadataType.cs +++ b/osu.Game/Overlays/BeatmapSet/MetadataType.cs @@ -1,14 +1,26 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; + namespace osu.Game.Overlays.BeatmapSet { public enum MetadataType { + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowInfoTags))] Tags, + + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowInfoSource))] Source, + + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowInfoDescription))] Description, + + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowInfoGenre))] Genre, + + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowInfoLanguage))] Language } } From 4b4c1448eadd3837117ac2814ffd89f145e804cc Mon Sep 17 00:00:00 2001 From: Lucas A Date: Thu, 12 Aug 2021 19:19:03 +0200 Subject: [PATCH 1061/2442] Localise success rate metrics. --- osu.Game/Overlays/BeatmapSet/SuccessRate.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs index 3bb36545cd..d370e57f14 100644 --- a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs +++ b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs @@ -4,10 +4,12 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Resources.Localisation.Web; using osu.Game.Screens.Select.Details; namespace osu.Game.Overlays.BeatmapSet @@ -42,7 +44,7 @@ namespace osu.Game.Overlays.BeatmapSet int playCount = beatmap?.OnlineInfo?.PlayCount ?? 0; var rate = playCount != 0 ? (float)passCount / playCount : 0; - successPercent.Text = rate.ToString("0.#%"); + successPercent.Text = rate.ToLocalisableString("0.#%"); successRate.Length = rate; percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic); @@ -64,7 +66,7 @@ namespace osu.Game.Overlays.BeatmapSet { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Text = "Success Rate", + Text = BeatmapsetsStrings.ShowInfoSuccessRate, Font = OsuFont.GetFont(size: 12) }, successRate = new Bar @@ -89,7 +91,7 @@ namespace osu.Game.Overlays.BeatmapSet { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Text = "Points of Failure", + Text = BeatmapsetsStrings.ShowInfoPointsOfFailure, Font = OsuFont.GetFont(size: 12), Margin = new MarginPadding { Vertical = 20 }, }, From 4d26bb67142d47283ea9ed7a223eaad2ccfd0957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 12 Aug 2021 19:27:32 +0200 Subject: [PATCH 1062/2442] Scale score panel to remove overlap with team score display --- .../OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs index 0e4655e336..8b7aaa02e7 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs @@ -6,7 +6,6 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -15,6 +14,7 @@ using osu.Game.Online.Rooms; using osu.Game.Scoring; using osu.Game.Screens.OnlinePlay.Playlists; using osu.Game.Screens.Play.HUD; +using osuTK; namespace osu.Game.Screens.OnlinePlay.Multiplayer { @@ -36,6 +36,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [BackgroundDependencyLoader] private void load() { + ScorePanelList.Anchor = ScorePanelList.Origin = Anchor.TopCentre; + ScorePanelList.Scale = new Vector2(0.9f); + ScorePanelList.Y = 75; + if (teamScores.Count == 2) { var redScore = teamScores.First().Value; From 828268ad4dfd50a83d7a919de8c29b9df7887047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 12 Aug 2021 19:32:23 +0200 Subject: [PATCH 1063/2442] Add winner text background to increase contrast --- .../Multiplayer/MultiplayerResultsScreen.cs | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs index 8b7aaa02e7..92b20f4915 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs @@ -6,7 +6,10 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Localisation; @@ -22,6 +25,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { private readonly SortedDictionary teamScores; + private Container winnerBackground; private Drawable winnerText; public MultiplayerResultsScreen(ScoreInfo score, long roomId, PlaylistItem playlistItem, SortedDictionary teamScores) @@ -36,6 +40,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [BackgroundDependencyLoader] private void load() { + const float winner_background_half_height = 250; + ScorePanelList.Anchor = ScorePanelList.Origin = Anchor.TopCentre; ScorePanelList.Scale = new Vector2(0.9f); ScorePanelList.Y = 75; @@ -59,6 +65,32 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer Team1Score = { BindTarget = redScore }, Team2Score = { BindTarget = blueScore }, }, + winnerBackground = new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Alpha = 0, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.X, + Height = winner_background_half_height, + Anchor = Anchor.Centre, + Origin = Anchor.BottomCentre, + Colour = ColourInfo.GradientVertical(Colour4.Black.Opacity(0), Colour4.Black.Opacity(0.4f)) + }, + new Box + { + RelativeSizeAxes = Axes.X, + Height = winner_background_half_height, + Anchor = Anchor.Centre, + Origin = Anchor.TopCentre, + Colour = ColourInfo.GradientVertical(Colour4.Black.Opacity(0.4f), Colour4.Black.Opacity(0)) + } + } + }, (winnerText = new OsuSpriteText { Alpha = 0, @@ -83,7 +115,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer using (BeginDelayedSequence(300)) { - winnerText.FadeInFromZero(600, Easing.InQuint); + const double fade_in_duration = 600; + + winnerText.FadeInFromZero(fade_in_duration, Easing.InQuint); + winnerBackground.FadeInFromZero(fade_in_duration, Easing.InQuint); winnerText .ScaleTo(10) @@ -91,6 +126,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer .Then() .ScaleTo(1.02f, 1600, Easing.OutQuint) .FadeOut(5000, Easing.InQuad); + winnerBackground.Delay(2200).FadeOut(2000); } } } From d08d22e3e943c5e411dfe5198cc09f1f06691306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 12 Aug 2021 19:48:57 +0200 Subject: [PATCH 1064/2442] Ensure tests wait for screen load --- .../Multiplayer/TestSceneMultiplayerTeamResults.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs index 2b04955d5e..94043c311a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs @@ -18,6 +18,8 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestDisplayWithTeams() { + MultiplayerResultsScreen screen = null; + AddStep("show results screen", () => { var rulesetInfo = new OsuRuleset().RulesetInfo; @@ -48,13 +50,17 @@ namespace osu.Game.Tests.Visual.Multiplayer { 1, new BindableInt(1048576) } }; - Stack.Push(new MultiplayerResultsScreen(score, 1, playlistItem, teamScores)); + Stack.Push(screen = new MultiplayerResultsScreen(score, 1, playlistItem, teamScores)); }); + + AddUntilStep("wait for loaded", () => screen.IsLoaded); } [Test] public void TestDisplayWithoutTeams() { + MultiplayerResultsScreen screen = null; + AddStep("show results screen", () => { var rulesetInfo = new OsuRuleset().RulesetInfo; @@ -81,8 +87,10 @@ namespace osu.Game.Tests.Visual.Multiplayer SortedDictionary teamScores = new SortedDictionary(); - Stack.Push(new MultiplayerResultsScreen(score, 1, playlistItem, teamScores)); + Stack.Push(screen = new MultiplayerResultsScreen(score, 1, playlistItem, teamScores)); }); + + AddUntilStep("wait for loaded", () => screen.IsLoaded); } } } From f06f13215b731a39d9128a30d52d3449fd3277ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 12 Aug 2021 20:01:31 +0200 Subject: [PATCH 1065/2442] Split off multiplayer team results screen to separate class The previous version tried to keep both normal multiplayer and team multiplayer results as one screen, but didn't check that team-specific components aren't null in `LoadComplete()`. To decrease number of conditional, split off the team results screen to a separate implementation, and choose one or the other at push time in `MultiplayerPlayer`, depending on team count. --- .../TestSceneMultiplayerTeamResults.cs | 6 +- .../Multiplayer/MultiplayerPlayer.cs | 4 +- .../Multiplayer/MultiplayerResultsScreen.cs | 118 +--------------- .../MultiplayerTeamResultsScreen.cs | 133 ++++++++++++++++++ 4 files changed, 139 insertions(+), 122 deletions(-) create mode 100644 osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs index 94043c311a..8878cbdfcb 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs @@ -50,7 +50,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { 1, new BindableInt(1048576) } }; - Stack.Push(screen = new MultiplayerResultsScreen(score, 1, playlistItem, teamScores)); + Stack.Push(screen = new MultiplayerTeamResultsScreen(score, 1, playlistItem, teamScores)); }); AddUntilStep("wait for loaded", () => screen.IsLoaded); @@ -85,9 +85,7 @@ namespace osu.Game.Tests.Visual.Multiplayer BeatmapID = beatmapInfo.ID, }; - SortedDictionary teamScores = new SortedDictionary(); - - Stack.Push(screen = new MultiplayerResultsScreen(score, 1, playlistItem, teamScores)); + Stack.Push(screen = new MultiplayerResultsScreen(score, 1, playlistItem)); }); AddUntilStep("wait for loaded", () => screen.IsLoaded); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index 3c892d03df..ca1a3710ab 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -181,7 +181,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override ResultsScreen CreateResults(ScoreInfo score) { Debug.Assert(RoomId.Value != null); - return new MultiplayerResultsScreen(score, RoomId.Value.Value, PlaylistItem, leaderboard.TeamScores); + return leaderboard.TeamScores.Count == 2 + ? new MultiplayerTeamResultsScreen(score, RoomId.Value.Value, PlaylistItem, leaderboard.TeamScores) + : new MultiplayerResultsScreen(score, RoomId.Value.Value, PlaylistItem); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs index 92b20f4915..140b3c45d8 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerResultsScreen.cs @@ -1,133 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Effects; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Localisation; using osu.Game.Online.Rooms; using osu.Game.Scoring; using osu.Game.Screens.OnlinePlay.Playlists; -using osu.Game.Screens.Play.HUD; -using osuTK; namespace osu.Game.Screens.OnlinePlay.Multiplayer { public class MultiplayerResultsScreen : PlaylistsResultsScreen { - private readonly SortedDictionary teamScores; - - private Container winnerBackground; - private Drawable winnerText; - - public MultiplayerResultsScreen(ScoreInfo score, long roomId, PlaylistItem playlistItem, SortedDictionary teamScores) + public MultiplayerResultsScreen(ScoreInfo score, long roomId, PlaylistItem playlistItem) : base(score, roomId, playlistItem, false, false) { - this.teamScores = teamScores; - } - - [Resolved] - private OsuColour colours { get; set; } - - [BackgroundDependencyLoader] - private void load() - { - const float winner_background_half_height = 250; - - ScorePanelList.Anchor = ScorePanelList.Origin = Anchor.TopCentre; - ScorePanelList.Scale = new Vector2(0.9f); - ScorePanelList.Y = 75; - - if (teamScores.Count == 2) - { - var redScore = teamScores.First().Value; - var blueScore = teamScores.Last().Value; - - // eventually this will be replaced by team names coming from the multiplayer match state. - string winner = redScore.Value > blueScore.Value ? @"Red" : @"Blue"; - - var winnerColour = redScore.Value > blueScore.Value ? colours.TeamColourRed : colours.TeamColourBlue; - - AddRangeInternal(new Drawable[] - { - new MatchScoreDisplay - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Team1Score = { BindTarget = redScore }, - Team2Score = { BindTarget = blueScore }, - }, - winnerBackground = new Container - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Alpha = 0, - Children = new[] - { - new Box - { - RelativeSizeAxes = Axes.X, - Height = winner_background_half_height, - Anchor = Anchor.Centre, - Origin = Anchor.BottomCentre, - Colour = ColourInfo.GradientVertical(Colour4.Black.Opacity(0), Colour4.Black.Opacity(0.4f)) - }, - new Box - { - RelativeSizeAxes = Axes.X, - Height = winner_background_half_height, - Anchor = Anchor.Centre, - Origin = Anchor.TopCentre, - Colour = ColourInfo.GradientVertical(Colour4.Black.Opacity(0.4f), Colour4.Black.Opacity(0)) - } - } - }, - (winnerText = new OsuSpriteText - { - Alpha = 0, - Font = OsuFont.Torus.With(size: 80, weight: FontWeight.Bold), - Text = MultiplayerResultsScreenStrings.TeamWins(winner), - Blending = BlendingParameters.Additive - }).WithEffect(new GlowEffect - { - Colour = winnerColour, - }).With(e => - { - e.Anchor = Anchor.Centre; - e.Origin = Anchor.Centre; - }) - }); - } - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - using (BeginDelayedSequence(300)) - { - const double fade_in_duration = 600; - - winnerText.FadeInFromZero(fade_in_duration, Easing.InQuint); - winnerBackground.FadeInFromZero(fade_in_duration, Easing.InQuint); - - winnerText - .ScaleTo(10) - .ScaleTo(1, 600, Easing.InQuad) - .Then() - .ScaleTo(1.02f, 1600, Easing.OutQuint) - .FadeOut(5000, Easing.InQuad); - winnerBackground.Delay(2200).FadeOut(2000); - } } } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs new file mode 100644 index 0000000000..fc5b72e210 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs @@ -0,0 +1,133 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Localisation; +using osu.Game.Online.Rooms; +using osu.Game.Scoring; +using osu.Game.Screens.Play.HUD; +using osuTK; + +namespace osu.Game.Screens.OnlinePlay.Multiplayer +{ + public class MultiplayerTeamResultsScreen : MultiplayerResultsScreen + { + private readonly SortedDictionary teamScores; + + private Container winnerBackground; + private Drawable winnerText; + + public MultiplayerTeamResultsScreen(ScoreInfo score, long roomId, PlaylistItem playlistItem, SortedDictionary teamScores) + : base(score, roomId, playlistItem) + { + if (teamScores.Count != 2) + throw new NotSupportedException(@"This screen currently only supports 2 teams"); + + this.teamScores = teamScores; + } + + [Resolved] + private OsuColour colours { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + const float winner_background_half_height = 250; + + ScorePanelList.Anchor = ScorePanelList.Origin = Anchor.TopCentre; + ScorePanelList.Scale = new Vector2(0.9f); + ScorePanelList.Y = 75; + + var redScore = teamScores.First().Value; + var blueScore = teamScores.Last().Value; + + // eventually this will be replaced by team names coming from the multiplayer match state. + string winner = redScore.Value > blueScore.Value ? @"Red" : @"Blue"; + + var winnerColour = redScore.Value > blueScore.Value ? colours.TeamColourRed : colours.TeamColourBlue; + + AddRangeInternal(new Drawable[] + { + new MatchScoreDisplay + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Team1Score = { BindTarget = redScore }, + Team2Score = { BindTarget = blueScore }, + }, + winnerBackground = new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Alpha = 0, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.X, + Height = winner_background_half_height, + Anchor = Anchor.Centre, + Origin = Anchor.BottomCentre, + Colour = ColourInfo.GradientVertical(Colour4.Black.Opacity(0), Colour4.Black.Opacity(0.4f)) + }, + new Box + { + RelativeSizeAxes = Axes.X, + Height = winner_background_half_height, + Anchor = Anchor.Centre, + Origin = Anchor.TopCentre, + Colour = ColourInfo.GradientVertical(Colour4.Black.Opacity(0.4f), Colour4.Black.Opacity(0)) + } + } + }, + (winnerText = new OsuSpriteText + { + Alpha = 0, + Font = OsuFont.Torus.With(size: 80, weight: FontWeight.Bold), + Text = MultiplayerResultsScreenStrings.TeamWins(winner), + Blending = BlendingParameters.Additive + }).WithEffect(new GlowEffect + { + Colour = winnerColour, + }).With(e => + { + e.Anchor = Anchor.Centre; + e.Origin = Anchor.Centre; + }) + }); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + using (BeginDelayedSequence(300)) + { + const double fade_in_duration = 600; + + winnerText.FadeInFromZero(fade_in_duration, Easing.InQuint); + winnerBackground.FadeInFromZero(fade_in_duration, Easing.InQuint); + + winnerText + .ScaleTo(10) + .ScaleTo(1, 600, Easing.InQuad) + .Then() + .ScaleTo(1.02f, 1600, Easing.OutQuint) + .FadeOut(5000, Easing.InQuad); + winnerBackground.Delay(2200).FadeOut(2000); + } + } + } +} From d9190607e43dfea970ea56e97d3b151c4cb9321a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 12 Aug 2021 20:21:53 +0200 Subject: [PATCH 1066/2442] Add test coverage for both teams winning --- .../Multiplayer/TestSceneMultiplayerTeamResults.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs index 8878cbdfcb..d6a2988685 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs @@ -15,8 +15,9 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneMultiplayerTeamResults : ScreenTestScene { - [Test] - public void TestDisplayWithTeams() + [TestCase(7483253, 1048576)] + [TestCase(1048576, 7483253)] + public void TestDisplayWithTeams(int team1Score, int team2Score) { MultiplayerResultsScreen screen = null; @@ -46,8 +47,8 @@ namespace osu.Game.Tests.Visual.Multiplayer SortedDictionary teamScores = new SortedDictionary { - { 0, new BindableInt(7483253) }, - { 1, new BindableInt(1048576) } + { 0, new BindableInt(team1Score) }, + { 1, new BindableInt(team2Score) } }; Stack.Push(screen = new MultiplayerTeamResultsScreen(score, 1, playlistItem, teamScores)); From 53b4cdfb0269f5861c725fc33e8fc467ebe707e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 12 Aug 2021 20:38:24 +0200 Subject: [PATCH 1067/2442] Handle ties in team vs. results screen --- .../TestSceneMultiplayerTeamResults.cs | 1 + ...=> MultiplayerTeamResultsScreenStrings.cs} | 9 +++++-- .../MultiplayerTeamResultsScreen.cs | 27 ++++++++++++++++--- 3 files changed, 31 insertions(+), 6 deletions(-) rename osu.Game/Localisation/{MultiplayerResultsScreenStrings.cs => MultiplayerTeamResultsScreenStrings.cs} (64%) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs index d6a2988685..a21996383f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs @@ -17,6 +17,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { [TestCase(7483253, 1048576)] [TestCase(1048576, 7483253)] + [TestCase(1048576, 1048576)] public void TestDisplayWithTeams(int team1Score, int team2Score) { MultiplayerResultsScreen screen = null; diff --git a/osu.Game/Localisation/MultiplayerResultsScreenStrings.cs b/osu.Game/Localisation/MultiplayerTeamResultsScreenStrings.cs similarity index 64% rename from osu.Game/Localisation/MultiplayerResultsScreenStrings.cs rename to osu.Game/Localisation/MultiplayerTeamResultsScreenStrings.cs index e586faba66..111c068bbd 100644 --- a/osu.Game/Localisation/MultiplayerResultsScreenStrings.cs +++ b/osu.Game/Localisation/MultiplayerTeamResultsScreenStrings.cs @@ -5,15 +5,20 @@ using osu.Framework.Localisation; namespace osu.Game.Localisation { - public static class MultiplayerResultsScreenStrings + public static class MultiplayerTeamResultsScreenStrings { - private const string prefix = @"osu.Game.Resources.Localisation.MultiplayerResultsScreen"; + private const string prefix = @"osu.Game.Resources.Localisation.MultiplayerTeamResultsScreen"; /// /// "Team {0} wins!" /// public static LocalisableString TeamWins(string winner) => new TranslatableString(getKey(@"team_wins"), @"Team {0} wins!", winner); + /// + /// "The teams are tied!" + /// + public static LocalisableString TheTeamsAreTied => new TranslatableString(getKey(@"the_teams_are_tied"), @"The teams are tied!"); + private static string getKey(string key) => $@"{prefix}:{key}"; } } \ No newline at end of file diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs index fc5b72e210..e98898b9da 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Localisation; @@ -52,10 +53,28 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer var redScore = teamScores.First().Value; var blueScore = teamScores.Last().Value; - // eventually this will be replaced by team names coming from the multiplayer match state. - string winner = redScore.Value > blueScore.Value ? @"Red" : @"Blue"; + LocalisableString winner; + Colour4 winnerColour; - var winnerColour = redScore.Value > blueScore.Value ? colours.TeamColourRed : colours.TeamColourBlue; + int comparison = redScore.Value.CompareTo(blueScore.Value); + + if (comparison < 0) + { + // team name should eventually be coming from the multiplayer match state. + winner = MultiplayerTeamResultsScreenStrings.TeamWins(@"Blue"); + winnerColour = colours.TeamColourBlue; + } + else if (comparison > 0) + { + // team name should eventually be coming from the multiplayer match state. + winner = MultiplayerTeamResultsScreenStrings.TeamWins(@"Red"); + winnerColour = colours.TeamColourRed; + } + else + { + winner = MultiplayerTeamResultsScreenStrings.TheTeamsAreTied; + winnerColour = Colour4.White.Opacity(0.5f); + } AddRangeInternal(new Drawable[] { @@ -96,7 +115,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { Alpha = 0, Font = OsuFont.Torus.With(size: 80, weight: FontWeight.Bold), - Text = MultiplayerResultsScreenStrings.TeamWins(winner), + Text = winner, Blending = BlendingParameters.Additive }).WithEffect(new GlowEffect { From 68f454b51a8ecc9753e21e323be29a9067b6b406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 12 Aug 2021 21:09:08 +0200 Subject: [PATCH 1068/2442] Enable NRT in explosion-related classes and streamline null handling --- .../Skinning/Default/DefaultHitExplosion.cs | 3 --- .../Skinning/Legacy/LegacyHitExplosion.cs | 3 --- osu.Game.Rulesets.Catch/UI/HitExplosion.cs | 14 +++++++------- osu.Game.Rulesets.Catch/UI/HitExplosionEntry.cs | 2 ++ osu.Game.Rulesets.Catch/UI/IHitExplosion.cs | 2 ++ 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/DefaultHitExplosion.cs b/osu.Game.Rulesets.Catch/Skinning/Default/DefaultHitExplosion.cs index 680fbc7b15..e1fad564a3 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/DefaultHitExplosion.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/DefaultHitExplosion.cs @@ -72,9 +72,6 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default public void Animate(HitExplosionEntry entry) { - if (entry == null) - return; - X = entry.Position; Scale = new Vector2(entry.HitObject.Scale); setColour(entry.ObjectColour); diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs index 78b0e1e327..08f86df2f3 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs @@ -62,9 +62,6 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy public void Animate(HitExplosionEntry entry) { - if (entry == null) - return; - Colour = entry.ObjectColour; using (BeginAbsoluteSequence(entry.LifetimeStart)) diff --git a/osu.Game.Rulesets.Catch/UI/HitExplosion.cs b/osu.Game.Rulesets.Catch/UI/HitExplosion.cs index 35af39348d..955b1e6edb 100644 --- a/osu.Game.Rulesets.Catch/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Catch/UI/HitExplosion.cs @@ -1,28 +1,25 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Rulesets.Catch.Skinning.Default; using osu.Game.Rulesets.Objects.Pooling; using osu.Game.Skinning; +#nullable enable + namespace osu.Game.Rulesets.Catch.UI { public class HitExplosion : PoolableDrawableWithLifetime { - private SkinnableDrawable skinnableExplosion; + private readonly SkinnableDrawable skinnableExplosion; public HitExplosion() { RelativeSizeAxes = Axes.Both; Anchor = Anchor.BottomCentre; Origin = Anchor.BottomCentre; - } - [BackgroundDependencyLoader] - private void load() - { InternalChild = skinnableExplosion = new SkinnableDrawable(new CatchSkinComponent(CatchSkinComponents.HitExplosion), _ => new DefaultHitExplosion()) { CentreComponent = false, @@ -44,8 +41,11 @@ namespace osu.Game.Rulesets.Catch.UI apply(Entry); } - private void apply(HitExplosionEntry entry) + private void apply(HitExplosionEntry? entry) { + if (entry == null) + return; + ApplyTransformsAt(double.MinValue, true); ClearTransforms(true); diff --git a/osu.Game.Rulesets.Catch/UI/HitExplosionEntry.cs b/osu.Game.Rulesets.Catch/UI/HitExplosionEntry.cs index 749a448314..815a0d8c98 100644 --- a/osu.Game.Rulesets.Catch/UI/HitExplosionEntry.cs +++ b/osu.Game.Rulesets.Catch/UI/HitExplosionEntry.cs @@ -6,6 +6,8 @@ using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Judgements; using osuTK.Graphics; +#nullable enable + namespace osu.Game.Rulesets.Catch.UI { public class HitExplosionEntry : LifetimeEntry diff --git a/osu.Game.Rulesets.Catch/UI/IHitExplosion.cs b/osu.Game.Rulesets.Catch/UI/IHitExplosion.cs index 4a9d7e8ac0..c744c00d9a 100644 --- a/osu.Game.Rulesets.Catch/UI/IHitExplosion.cs +++ b/osu.Game.Rulesets.Catch/UI/IHitExplosion.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. +#nullable enable + namespace osu.Game.Rulesets.Catch.UI { /// From f3045b315285c1d82ce144c635055e47236df4a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 12 Aug 2021 21:13:45 +0200 Subject: [PATCH 1069/2442] Add comment about swapped sprite names --- osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs index 08f86df2f3..c2570c4d1f 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs @@ -56,6 +56,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy { var defaultLegacySkin = skins.DefaultLegacySkin; + // sprite names intentionally swapped to match stable member naming / ease of cross-referencing explosion1.Texture = defaultLegacySkin.GetTexture("scoreboard-explosion-2"); explosion2.Texture = defaultLegacySkin.GetTexture("scoreboard-explosion-1"); } From e79150d4da09d35870380e2d92156ec9b011728e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 12 Aug 2021 21:14:46 +0200 Subject: [PATCH 1070/2442] Reorder constructor arguments for `HitExplosionEntry` --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 2 +- osu.Game.Rulesets.Catch/UI/HitExplosionEntry.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index aec8e752a7..5cd85aac56 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -367,7 +367,7 @@ namespace osu.Game.Rulesets.Catch.UI } private void addLighting(JudgementResult judgementResult, Color4 colour, float x) => - hitExplosionContainer.Add(new HitExplosionEntry(judgementResult, colour, x, Time.Current)); + hitExplosionContainer.Add(new HitExplosionEntry(Time.Current, judgementResult, colour, x)); private CaughtObject getCaughtObject(PalpableCatchHitObject source) { diff --git a/osu.Game.Rulesets.Catch/UI/HitExplosionEntry.cs b/osu.Game.Rulesets.Catch/UI/HitExplosionEntry.cs index 815a0d8c98..88871c77f6 100644 --- a/osu.Game.Rulesets.Catch/UI/HitExplosionEntry.cs +++ b/osu.Game.Rulesets.Catch/UI/HitExplosionEntry.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Catch.UI /// public float Position { get; } - public HitExplosionEntry(JudgementResult judgementResult, Color4 objectColour, float position, double startTime) + public HitExplosionEntry(double startTime, JudgementResult judgementResult, Color4 objectColour, float position) { LifetimeStart = startTime; Position = position; From b84f2381065a00021c88361714387bf715441d1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 12 Aug 2021 22:33:09 +0200 Subject: [PATCH 1071/2442] Adjust scaling numbers to be closer to stable --- osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs index c2570c4d1f..c262b0a4ac 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyHitExplosion.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy Anchor = Anchor.BottomCentre; Origin = Anchor.BottomCentre; RelativeSizeAxes = Axes.Both; - Scale = new Vector2(0.4f); + Scale = new Vector2(0.5f); InternalChildren = new[] { @@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy explosion1.Position = new Vector2(explosionOffset, 0); explosion1.FadeOutFromOne(300); - explosion1.ScaleTo(new Vector2(20 * scale, 1.1f), 160, Easing.Out); + explosion1.ScaleTo(new Vector2(16 * scale, 1.1f), 160, Easing.Out); } explosion2.Scale = new Vector2(0.9f, 1); From 7aa361d77280c14ea53d7a563a59d988a7b7f29c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Aug 2021 09:02:13 +0900 Subject: [PATCH 1072/2442] Remove now-incorrect test --- osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 7ff0368fc2..7f961d4c8b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -405,8 +405,6 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("dialog overlay is hidden", () => DialogOverlay.State.Value == Visibility.Hidden); - testLeave("lounge tab item", () => this.ChildrenOfType.BreadcrumbTabItem>().First().TriggerClick()); - testLeave("back button", () => multiplayerScreen.OnBackButton()); // mimics home button and OS window close From 524102951348592f84312bb946e1eb85b40298c7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Aug 2021 10:27:26 +0900 Subject: [PATCH 1073/2442] Use new FadeExponent shader uniform --- .../Skinning/Legacy/LegacyCursorTrail.cs | 6 ++---- osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs | 17 +++++++++++------ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs index f98d936b48..9493dc2ef1 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs @@ -5,7 +5,6 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Shaders; using osu.Framework.Input.Events; using osu.Game.Configuration; using osu.Game.Rulesets.Osu.UI.Cursor; @@ -31,10 +30,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy } [BackgroundDependencyLoader] - private void load(ShaderManager shaders, OsuConfigManager config) + private void load(OsuConfigManager config) { - Shader = shaders.Load(@"LegacyCursorTrail", FragmentShaderDescriptor.TEXTURE); - Texture = skin.GetTexture("cursortrail"); disjointTrail = skin.GetTexture("cursormiddle") == null; @@ -60,6 +57,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy } protected override double FadeDuration => disjointTrail ? 150 : 500; + protected override float FadeExponent => 1; protected override bool InterpolateMovements => !disjointTrail; diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 66a9302f5f..b05bf5c93e 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -26,11 +26,14 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { private const int max_sprites = 2048; + /// + /// An exponentiating factor to ease the trail fade. + /// + protected virtual float FadeExponent => 1.7f; + private readonly TrailPart[] parts = new TrailPart[max_sprites]; private int currentIndex; - - protected IShader Shader; - + private IShader shader; private double timeOffset; private float time; @@ -65,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor [BackgroundDependencyLoader] private void load(ShaderManager shaders) { - Shader = shaders.Load(@"CursorTrail", FragmentShaderDescriptor.TEXTURE); + shader = shaders.Load(@"CursorTrail", FragmentShaderDescriptor.TEXTURE); } protected override void LoadComplete() @@ -217,10 +220,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private Texture texture; private float time; + private float fadeExponent; private readonly TrailPart[] parts = new TrailPart[max_sprites]; private Vector2 size; - private Vector2 originPosition; private readonly QuadBatch vertexBatch = new QuadBatch(max_sprites, 1); @@ -234,10 +237,11 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { base.ApplyState(); - shader = Source.Shader; + shader = Source.shader; texture = Source.texture; size = Source.partSize; time = Source.time; + fadeExponent = Source.FadeExponent; originPosition = Vector2.Zero; @@ -260,6 +264,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor shader.Bind(); shader.GetUniform("g_FadeClock").UpdateValue(ref time); + shader.GetUniform("g_FadeExponent").UpdateValue(ref fadeExponent); texture.TextureGL.Bind(); From 7cc0a2a76fdb8acecf4502bac1a75279c9b29e49 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Aug 2021 12:10:33 +0900 Subject: [PATCH 1074/2442] Refactor to fix InterpolateMovements=false --- .../Skinning/Legacy/LegacyCursorTrail.cs | 7 +++++- .../UI/Cursor/CursorTrail.cs | 24 +++++++------------ 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs index 9493dc2ef1..587ff4b573 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs @@ -79,8 +79,13 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy protected override bool OnMouseMove(MouseMoveEvent e) { + if (!disjointTrail) + return base.OnMouseMove(e); + currentPosition = e.ScreenSpaceMousePosition; - return base.OnMouseMove(e); + + // Intentionally block the base call as we're adding the trails ourselves. + return false; } } } diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index b05bf5c93e..7a95111c91 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -146,33 +146,25 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor protected override bool OnMouseMove(MouseMoveEvent e) { - Vector2 position = e.ScreenSpaceMousePosition; - - if (lastPosition == null) - { - lastPosition = position; - resampler.AddPosition(lastPosition.Value); - return base.OnMouseMove(e); - } - - if (InterpolateMovements) - AddTrail(position); - + AddTrail(e.ScreenSpaceMousePosition); return base.OnMouseMove(e); } protected void AddTrail(Vector2 position) { - if (!lastPosition.HasValue) - return; - if (InterpolateMovements) { + if (!lastPosition.HasValue) + { + lastPosition = position; + resampler.AddPosition(lastPosition.Value); + return; + } + foreach (Vector2 pos2 in resampler.AddPosition(position)) { Trace.Assert(lastPosition.HasValue); - // ReSharper disable once PossibleInvalidOperationException Vector2 pos1 = lastPosition.Value; Vector2 diff = pos2 - pos1; float distance = diff.Length; From 2b86416cb268e717633b8f6d65d9772bb1ea022b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 13 Aug 2021 06:27:57 +0300 Subject: [PATCH 1075/2442] Hide player settings overlay on multi-spectator player loader --- .../Multiplayer/Spectate/MultiSpectatorPlayerLoader.cs | 7 +++++++ osu.Game/Screens/Play/PlayerLoader.cs | 7 ++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayerLoader.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayerLoader.cs index 5a1d28e9c4..52e4a6e012 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayerLoader.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayerLoader.cs @@ -3,6 +3,7 @@ using System; using JetBrains.Annotations; +using osu.Framework.Allocation; using osu.Game.Scoring; using osu.Game.Screens.Menu; using osu.Game.Screens.Play; @@ -19,6 +20,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate { } + [BackgroundDependencyLoader] + private void load() + { + PlayerSettingsGroups.Alpha = 0f; + } + protected override void LogoArriving(OsuLogo logo, bool resuming) { } diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 5f6b4ca2b0..1f8387ac67 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -48,6 +48,11 @@ namespace osu.Game.Screens.Play protected BeatmapMetadataDisplay MetadataInfo; + /// + /// A fill flow containing the player settings groups, exposed for the ability to hide it from inheritors of the player loader. + /// + protected FillFlowContainer PlayerSettingsGroups; + protected VisualSettings VisualSettings; protected Task LoadTask { get; private set; } @@ -140,7 +145,7 @@ namespace osu.Game.Screens.Play Anchor = Anchor.Centre, Origin = Anchor.Centre, }, - new FillFlowContainer + PlayerSettingsGroups = new FillFlowContainer { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, From c10320f23914fd8f15ff0c5ee30ca943a5de8519 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 13 Aug 2021 06:28:07 +0300 Subject: [PATCH 1076/2442] Hide and disable player settings overlay on multi-spectator player --- .../Multiplayer/Spectate/MultiSpectatorPlayer.cs | 4 ++++ osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs | 2 +- osu.Game/Screens/Play/HUDOverlay.cs | 8 ++++++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs index 2c157b0564..71defa2e07 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs @@ -4,6 +4,7 @@ using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics.Containers; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Scoring; @@ -34,6 +35,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate private void load() { spectatorPlayerClock.WaitingOnFrames.BindTo(waitingOnFrames); + + HUDOverlay.PlayerSettingsOverlay.State.Value = Visibility.Hidden; + HUDOverlay.PlayerSettingsOverlay.State.Disabled = true; } protected override void UpdateAfterChildren() diff --git a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs index ffcbb06fb3..efd37194d3 100644 --- a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs +++ b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs @@ -57,7 +57,7 @@ namespace osu.Game.Screens.Play.HUD if (e.ControlPressed) { - if (e.Key == Key.H && ReplayLoaded) + if (e.Key == Key.H && ReplayLoaded && !State.Disabled) { ToggleVisibility(); return true; diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 2cf2555b3e..919886cae7 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -240,13 +240,17 @@ namespace osu.Game.Screens.Play if (e.NewValue) { - PlayerSettingsOverlay.Show(); + if (!PlayerSettingsOverlay.State.Disabled) + PlayerSettingsOverlay.Show(); + ModDisplay.FadeIn(200); KeyCounter.Margin = new MarginPadding(10) { Bottom = 30 }; } else { - PlayerSettingsOverlay.Hide(); + if (!PlayerSettingsOverlay.State.Disabled) + PlayerSettingsOverlay.Hide(); + ModDisplay.Delay(2000).FadeOut(200); KeyCounter.Margin = new MarginPadding(10); } From 1892db7f37e0d916826fef0d4788da4d78ce2531 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 13 Aug 2021 06:31:39 +0300 Subject: [PATCH 1077/2442] Add test coverage --- .../Multiplayer/TestSceneMultiSpectatorScreen.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index 65b1d6d53a..ad60582ace 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -6,6 +6,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Online.Multiplayer; @@ -13,6 +14,8 @@ using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; using osu.Game.Rulesets.UI; using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate; using osu.Game.Screens.Play; +using osu.Game.Screens.Play.HUD; +using osu.Game.Screens.Play.PlayerSettings; using osu.Game.Tests.Beatmaps.IO; using osu.Game.Users; @@ -80,6 +83,19 @@ namespace osu.Game.Tests.Visual.Multiplayer AddWaitStep("wait a bit", 20); } + [Test] + public void TestSpectatorPlayerSettingsHidden() + { + start(new[] { PLAYER_1_ID, PLAYER_2_ID }); + loadSpectateScreen(false); + + AddUntilStep("wait for player loaders", () => this.ChildrenOfType().Count() == 2); + AddAssert("all player loader settings hidden", () => this.ChildrenOfType().All(l => l.ChildrenOfType>().Single().Alpha == 0f)); + + AddUntilStep("wait for players to load", () => spectatorScreen.AllPlayersLoaded); + AddAssert("all player settings hidden", () => this.ChildrenOfType().All(p => p.ChildrenOfType().Single().State.Value == Visibility.Hidden)); + } + [Test] public void TestTeamDisplay() { From e913c8f92fdff9cd6f76c5b63cd079e15b8bbc8a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Aug 2021 13:07:02 +0900 Subject: [PATCH 1078/2442] Change strings to verbatim --- .../OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs | 2 +- .../Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs index a7eaa37faa..ad7882abc2 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs @@ -24,7 +24,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override FilterCriteria CreateFilterCriteria() { var criteria = base.CreateFilterCriteria(); - criteria.Category = "realtime"; + criteria.Category = @"realtime"; return criteria; } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs index 254769d06b..eee4d4f407 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs @@ -42,11 +42,11 @@ namespace osu.Game.Screens.OnlinePlay.Playlists switch (categoryDropdown.Current.Value) { case PlaylistsCategory.Normal: - criteria.Category = "normal"; + criteria.Category = @"normal"; break; case PlaylistsCategory.Spotlight: - criteria.Category = "spotlight"; + criteria.Category = @"spotlight"; break; } From cbeecff347b350458f4858ab97722f39c875bccf Mon Sep 17 00:00:00 2001 From: LiangXiang Shen Date: Fri, 13 Aug 2021 12:17:38 +0800 Subject: [PATCH 1079/2442] Apply suggestions from code review Co-authored-by: Joseph Madamba --- osu.Game/Localisation/GraphicsSettingsStrings.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Localisation/GraphicsSettingsStrings.cs b/osu.Game/Localisation/GraphicsSettingsStrings.cs index 989eaf19b9..0e384f983f 100644 --- a/osu.Game/Localisation/GraphicsSettingsStrings.cs +++ b/osu.Game/Localisation/GraphicsSettingsStrings.cs @@ -55,14 +55,14 @@ namespace osu.Game.Localisation public static LocalisableString Resolution => new TranslatableString(getKey(@"resolution"), @"Resolution"); /// - /// "UI Scaling" + /// "UI scaling" /// - public static LocalisableString UIScaling => new TranslatableString(getKey(@"ui_scaling"), @"UI Scaling"); + public static LocalisableString UIScaling => new TranslatableString(getKey(@"ui_scaling"), @"UI scaling"); /// - /// "Screen Scaling" + /// "Screen scaling" /// - public static LocalisableString ScreenScaling => new TranslatableString(getKey(@"screen_scaling"), @"Screen Scaling"); + public static LocalisableString ScreenScaling => new TranslatableString(getKey(@"screen_scaling"), @"Screen scaling"); /// /// "Horizontal position" @@ -95,14 +95,14 @@ namespace osu.Game.Localisation public static LocalisableString DetailSettingsHeader => new TranslatableString(getKey(@"detail_settings_header"), @"Detail Settings"); /// - /// "Storyboard / Video" + /// "Storyboard / video" /// - public static LocalisableString StoryboardVideo => new TranslatableString(getKey(@"storyboard_video"), @"Storyboard / Video"); + public static LocalisableString StoryboardVideo => new TranslatableString(getKey(@"storyboard_video"), @"Storyboard / video"); /// - /// "Hit Lighting" + /// "Hit lighting" /// - public static LocalisableString HitLighting => new TranslatableString(getKey(@"hit_lighting"), @"Hit Lighting"); + public static LocalisableString HitLighting => new TranslatableString(getKey(@"hit_lighting"), @"Hit lighting"); /// /// "Screenshot format" From e93660c0f4f0936828548479711d2c3031514128 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Aug 2021 13:19:34 +0900 Subject: [PATCH 1080/2442] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 454bb46059..33d3a623ed 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index e6219fcb85..6ccd34dd48 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 9904946363..b6623da540 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From 2850f6ce95d9f9f258a68dce0874e75cdcc17ab2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Aug 2021 13:24:05 +0900 Subject: [PATCH 1081/2442] Privatise counter again --- .../Visual/Online/TestSceneOfflineCommentsContainer.cs | 4 +++- osu.Game/Overlays/Comments/CommentsContainer.cs | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneOfflineCommentsContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneOfflineCommentsContainer.cs index af0d06dce3..628ae0971b 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneOfflineCommentsContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneOfflineCommentsContainer.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using NUnit.Framework; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics; @@ -12,6 +13,7 @@ using osu.Framework.Allocation; using osu.Game.Online.API.Requests.Responses; using osu.Game.Users; using JetBrains.Annotations; +using osu.Framework.Testing; namespace osu.Game.Tests.Visual.Online { @@ -186,7 +188,7 @@ namespace osu.Game.Tests.Visual.Online public void ShowComments(CommentBundle bundle) { - CommentCounter.Current.Value = 0; + this.ChildrenOfType().Single().Current.Value = 0; ClearComments(); OnSuccess(bundle); } diff --git a/osu.Game/Overlays/Comments/CommentsContainer.cs b/osu.Game/Overlays/Comments/CommentsContainer.cs index ee24c4086b..fe8d6f0178 100644 --- a/osu.Game/Overlays/Comments/CommentsContainer.cs +++ b/osu.Game/Overlays/Comments/CommentsContainer.cs @@ -41,7 +41,7 @@ namespace osu.Game.Overlays.Comments private FillFlowContainer content; private DeletedCommentsCounter deletedCommentsCounter; private CommentsShowMoreButton moreButton; - protected TotalCommentsCounter CommentCounter; + private TotalCommentsCounter commentCounter; [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) @@ -62,7 +62,7 @@ namespace osu.Game.Overlays.Comments Direction = FillDirection.Vertical, Children = new Drawable[] { - CommentCounter = new TotalCommentsCounter(), + commentCounter = new TotalCommentsCounter(), new CommentsHeader { Sort = { BindTarget = Sort }, @@ -143,7 +143,7 @@ namespace osu.Game.Overlays.Comments return; // only reset when changing ID/type. other refetch ops are generally just changing sort order. - CommentCounter.Current.Value = 0; + commentCounter.Current.Value = 0; refetchComments(); } @@ -181,7 +181,7 @@ namespace osu.Game.Overlays.Comments protected void OnSuccess(CommentBundle response) { - CommentCounter.Current.Value = response.Total; + commentCounter.Current.Value = response.Total; if (!response.Comments.Any()) { From 8dc7a925e7e4a05de0f288fa9bee5dc684169f54 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 13 Aug 2021 07:28:57 +0300 Subject: [PATCH 1082/2442] Expire instead of hiding and disabling visibility state Since it's a temporary change until the spectator interface gets improved, no need to add further logic. --- .../Multiplayer/Spectate/MultiSpectatorPlayer.cs | 4 +--- .../Multiplayer/Spectate/MultiSpectatorPlayerLoader.cs | 2 +- osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs | 2 +- osu.Game/Screens/Play/HUDOverlay.cs | 8 ++------ 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs index 71defa2e07..b0ea361dbb 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs @@ -4,7 +4,6 @@ using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Graphics.Containers; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Scoring; @@ -36,8 +35,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate { spectatorPlayerClock.WaitingOnFrames.BindTo(waitingOnFrames); - HUDOverlay.PlayerSettingsOverlay.State.Value = Visibility.Hidden; - HUDOverlay.PlayerSettingsOverlay.State.Disabled = true; + HUDOverlay.PlayerSettingsOverlay.Expire(); } protected override void UpdateAfterChildren() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayerLoader.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayerLoader.cs index 52e4a6e012..f28c2a1d48 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayerLoader.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayerLoader.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate [BackgroundDependencyLoader] private void load() { - PlayerSettingsGroups.Alpha = 0f; + PlayerSettingsGroups.Expire(); } protected override void LogoArriving(OsuLogo logo, bool resuming) diff --git a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs index efd37194d3..ffcbb06fb3 100644 --- a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs +++ b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs @@ -57,7 +57,7 @@ namespace osu.Game.Screens.Play.HUD if (e.ControlPressed) { - if (e.Key == Key.H && ReplayLoaded && !State.Disabled) + if (e.Key == Key.H && ReplayLoaded) { ToggleVisibility(); return true; diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 919886cae7..2cf2555b3e 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -240,17 +240,13 @@ namespace osu.Game.Screens.Play if (e.NewValue) { - if (!PlayerSettingsOverlay.State.Disabled) - PlayerSettingsOverlay.Show(); - + PlayerSettingsOverlay.Show(); ModDisplay.FadeIn(200); KeyCounter.Margin = new MarginPadding(10) { Bottom = 30 }; } else { - if (!PlayerSettingsOverlay.State.Disabled) - PlayerSettingsOverlay.Hide(); - + PlayerSettingsOverlay.Hide(); ModDisplay.Delay(2000).FadeOut(200); KeyCounter.Margin = new MarginPadding(10); } From e7cf6b2d2337168ecfd0442e3518efa87d7d2c07 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 13 Aug 2021 07:29:55 +0300 Subject: [PATCH 1083/2442] Expire hold-to-quit button on multi-spectator player --- .../OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs index b0ea361dbb..20381e0c48 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs @@ -36,6 +36,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate spectatorPlayerClock.WaitingOnFrames.BindTo(waitingOnFrames); HUDOverlay.PlayerSettingsOverlay.Expire(); + HUDOverlay.HoldToQuit.Expire(); } protected override void UpdateAfterChildren() From 34c2b317e240821b5c7038fa0b3d1afb45ac3be5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 13 Aug 2021 07:30:09 +0300 Subject: [PATCH 1084/2442] Hide song progress bar on multi-spectator player --- .../Multiplayer/Spectate/MultiSpectatorPlayer.cs | 2 ++ osu.Game/Screens/Play/Player.cs | 12 +++++++++++- osu.Game/Screens/Play/SongProgress.cs | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs index 20381e0c48..1b1dee5ae2 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs @@ -35,6 +35,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate { spectatorPlayerClock.WaitingOnFrames.BindTo(waitingOnFrames); + AllowUserSeekingState.Value = false; + AllowUserSeekingState.Disabled = true; HUDOverlay.PlayerSettingsOverlay.Expire(); HUDOverlay.HoldToQuit.Expire(); } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 09eaf1c543..dc37464a61 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -77,6 +77,10 @@ namespace osu.Game.Screens.Play protected readonly Bindable LocalUserPlaying = new Bindable(); + protected readonly Bindable AllowUserSeekingState = new Bindable(); + + public IBindable AllowUserSeeking => AllowUserSeekingState; + public int RestartCount; [Resolved] @@ -269,7 +273,13 @@ namespace osu.Game.Screens.Play DrawableRuleset.FrameStableClock.IsCatchingUp.BindValueChanged(_ => updateSampleDisabledState()); - DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updateGameplayState()); + DrawableRuleset.HasReplayLoaded.BindValueChanged(r => + { + if (!AllowUserSeekingState.Disabled) + AllowUserSeekingState.Value = r.NewValue; + + updateGameplayState(); + }); // bind clock into components that require it DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused); diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index f28622f42e..8debe6243d 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -119,7 +119,7 @@ namespace osu.Game.Screens.Play if (drawableRuleset != null) { - AllowSeeking.BindTo(drawableRuleset.HasReplayLoaded); + ((IBindable)AllowSeeking).BindTo(player.AllowUserSeeking); referenceClock = drawableRuleset.FrameStableClock; Objects = drawableRuleset.Objects; From fc22e806f44abddf493ab87bbe15f91a14e7c8c2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 13 Aug 2021 07:30:24 +0300 Subject: [PATCH 1085/2442] Cover newly hidden/expired elements in existing test --- .../Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index ad60582ace..a9004987df 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -90,10 +90,13 @@ namespace osu.Game.Tests.Visual.Multiplayer loadSpectateScreen(false); AddUntilStep("wait for player loaders", () => this.ChildrenOfType().Count() == 2); - AddAssert("all player loader settings hidden", () => this.ChildrenOfType().All(l => l.ChildrenOfType>().Single().Alpha == 0f)); + AddAssert("all player loader settings hidden", () => this.ChildrenOfType().All(l => !l.ChildrenOfType>().Any())); AddUntilStep("wait for players to load", () => spectatorScreen.AllPlayersLoaded); - AddAssert("all player settings hidden", () => this.ChildrenOfType().All(p => p.ChildrenOfType().Single().State.Value == Visibility.Hidden)); + AddUntilStep("all interactive elements removed", () => this.ChildrenOfType().All(p => + !p.ChildrenOfType().Any() && + !p.ChildrenOfType().Any() && + !p.ChildrenOfType().Single().ShowHandle)); } [Test] From 8fc0edb283390c9f27955a0ecc8149312692877a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Aug 2021 13:54:52 +0900 Subject: [PATCH 1086/2442] Fix some multiplayer test failures --- osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs | 4 ++++ .../Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs | 2 ++ 2 files changed, 6 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 0ffa5209e3..01e40e0247 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -312,6 +312,8 @@ namespace osu.Game.Tests.Visual.Multiplayer InputManager.Click(MouseButton.Left); }); + AddUntilStep("wait for spectating user state", () => client.LocalUser?.State == MultiplayerUserState.Spectating); + AddStep("start match externally", () => client.StartMatch()); AddAssert("play not started", () => multiplayerScreen.IsCurrentScreen()); @@ -348,6 +350,8 @@ namespace osu.Game.Tests.Visual.Multiplayer InputManager.Click(MouseButton.Left); }); + AddUntilStep("wait for spectating user state", () => client.LocalUser?.State == MultiplayerUserState.Spectating); + AddStep("start match externally", () => client.StartMatch()); AddStep("restore beatmap", () => diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index 955be6ca21..ea10fc1b8b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -129,6 +129,8 @@ namespace osu.Game.Tests.Visual.Multiplayer InputManager.Click(MouseButton.Left); }); + AddUntilStep("wait for spectating user state", () => Client.LocalUser?.State == MultiplayerUserState.Spectating); + AddUntilStep("wait for ready button to be enabled", () => this.ChildrenOfType().Single().ChildrenOfType().Single().Enabled.Value); AddStep("click ready button", () => From 8fd6a9eb4b403c33d656f79bea54df49782eb562 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Aug 2021 14:01:38 +0900 Subject: [PATCH 1087/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 454bb46059..ecfaff0547 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index e6219fcb85..9ee1ad167e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 9904946363..c378f24b69 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 755b6460b624c164f63f9e2419f38effce2124e0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Aug 2021 14:04:43 +0900 Subject: [PATCH 1088/2442] Fix multiplayer navigation test failure --- osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs | 4 ++-- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 5 +++++ .../OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 2 +- .../Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 0ffa5209e3..35beefaa2a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -25,7 +25,7 @@ using osu.Game.Screens; using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.OnlinePlay.Lounge.Components; -using osu.Game.Screens.OnlinePlay.Match.Components; +using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; using osu.Game.Tests.Resources; @@ -396,7 +396,7 @@ namespace osu.Game.Tests.Visual.Multiplayer } }); - AddStep("open mod overlay", () => this.ChildrenOfType().ElementAt(2).TriggerClick()); + AddStep("open mod overlay", () => this.ChildrenOfType().Single().TriggerClick()); AddStep("invoke on back button", () => multiplayerScreen.OnBackButton()); diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index a53e253581..2616abf825 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -17,6 +17,7 @@ using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Overlays.Mods; using osu.Game.Rulesets.Mods; +using osu.Game.Screens.OnlinePlay.Match.Components; namespace osu.Game.Screens.OnlinePlay.Match { @@ -250,5 +251,9 @@ namespace osu.Game.Screens.OnlinePlay.Match private class UserModSelectOverlay : LocalPlayerModSelectOverlay { } + + public class UserModSelectButton : PurpleTriangleButton + { + } } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 9fa19aaf21..a8e44dd56c 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -176,7 +176,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer Spacing = new Vector2(10, 0), Children = new Drawable[] { - new PurpleTriangleButton + new UserModSelectButton { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 45aca24ab2..953c687087 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -163,7 +163,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists Spacing = new Vector2(10, 0), Children = new Drawable[] { - new PurpleTriangleButton + new UserModSelectButton { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, From 89eded457c094701a1c4532a546cfe96df50db80 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Aug 2021 14:27:28 +0900 Subject: [PATCH 1089/2442] Fix weird margins on loading display in lounge --- osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 122b30b1d2..910afb5540 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -63,6 +63,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { OsuScrollContainer scrollContainer; + Container filterContainer; + InternalChildren = new Drawable[] { new Box @@ -93,7 +95,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { new Drawable[] { - new Container + filterContainer = new Container { RelativeSizeAxes = Axes.X, Height = 70, @@ -123,13 +125,14 @@ namespace osu.Game.Screens.OnlinePlay.Lounge ScrollbarOverlapsContent = false, Child = roomsContainer = new RoomsContainer() }, - loadingLayer = new LoadingLayer(true), } }, } } }, - } + }, + loadingLayer = new LoadingLayer(true), + filterContainer.CreateProxy() }; // scroll selected room into view on selection. From 0f45155b8e1b25a7a64698af618b1dcd7cde57e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Aug 2021 14:29:28 +0900 Subject: [PATCH 1090/2442] Fix remaining cases of invalid bindable operations during lease --- .../Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index 3b0f2c1eba..46d9850fde 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -154,7 +154,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components protected override bool OnClick(ClickEvent e) { - selectedRoom.Value = null; + if (!selectedRoom.Disabled) + selectedRoom.Value = null; return base.OnClick(e); } @@ -216,6 +217,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private void selectNext(int direction) { + if (selectedRoom.Disabled) + return; + var visibleRooms = Rooms.AsEnumerable().Where(r => r.IsPresent); Room room; From b9721f5261c52a42e6eabec465a55e6b6d024431 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Aug 2021 14:37:43 +0900 Subject: [PATCH 1091/2442] Centralise screen exit logic and guard against non-current screen --- .../Multiplayer/MultiplayerMatchSubScreen.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 36b86737bc..265b68afea 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -274,7 +274,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer isConnected.BindValueChanged(connected => { if (!connected.NewValue) - Schedule(this.Exit); + handleRoomLost(); }, true); currentRoom.BindValueChanged(room => @@ -284,7 +284,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer // the room has gone away. // this could mean something happened during the join process, or an external connection issue occurred. // one specific scenario is where the underlying room is created, but the signalr server returns an error during the join process. this triggers a PartRoom operation (see https://github.com/ppy/osu/blob/7654df94f6f37b8382be7dfcb4f674e03bd35427/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs#L97) - Schedule(this.Exit); + handleRoomLost(); } }, true); } @@ -451,13 +451,21 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer // may happen if the client is kicked or otherwise removed from the room. if (client.Room == null) { - Schedule(this.Exit); + handleRoomLost(); return; } Scheduler.AddOnce(UpdateMods); } + private void handleRoomLost() => Schedule(() => + { + if (this.IsCurrentScreen()) + this.Exit(); + else + ValidForResume = false; + }); + private void onLoadRequested() { if (BeatmapAvailability.Value.State != DownloadState.LocallyAvailable) From a1b72e7f97731b072a3a1fb204fd12310e239bdb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Aug 2021 14:41:07 +0900 Subject: [PATCH 1092/2442] Remove redundant array type specification --- osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 910afb5540..fb0b963167 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -65,7 +65,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge Container filterContainer; - InternalChildren = new Drawable[] + InternalChildren = new[] { new Box { From 641d57e5e1657cc8ca26aea5031e47de7d3256ab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Aug 2021 14:46:28 +0900 Subject: [PATCH 1093/2442] Change scroll container subclass name to hopefully read better --- osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index 14fcaa91fa..0f1ddedcf0 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -43,7 +43,7 @@ namespace osu.Game.Screens.Play.HUD InternalChildren = new Drawable[] { - scroll = new ManualScrollScrollContainer + scroll = new InputDisabledScrollContainer { RelativeSizeAxes = Axes.Both, Child = Flow = new FillFlowContainer @@ -174,9 +174,9 @@ namespace osu.Game.Screens.Play.HUD sorting.Validate(); } - private class ManualScrollScrollContainer : OsuScrollContainer + private class InputDisabledScrollContainer : OsuScrollContainer { - public ManualScrollScrollContainer() + public InputDisabledScrollContainer() { ScrollbarVisible = false; } From 93574acf729981a8e925fc3edd0342bb444e56f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Aug 2021 14:46:57 +0900 Subject: [PATCH 1094/2442] Fix exception messaging --- osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index 0f1ddedcf0..312eeec949 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -82,7 +82,7 @@ namespace osu.Game.Screens.Play.HUD if (isTracked) { if (trackedScore != null) - throw new InvalidOperationException("Cannot track more than one scores."); + throw new InvalidOperationException("Cannot track more than one score."); trackedScore = drawable; } From 90755c0307b8af7de3c7c3d624be9d0b012e8d9b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Aug 2021 14:50:59 +0900 Subject: [PATCH 1095/2442] Replace condition with matching precalculated bool --- osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index 312eeec949..871555e5a3 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -142,7 +142,7 @@ namespace osu.Game.Screens.Play.HUD c.Colour = Color4.Transparent; else { - if (bottomY - fadeBottom > 0 && requiresScroll) + if (requireBottomFade) { c.Colour = ColourInfo.GradientVertical( Color4.White.Opacity(Math.Min(1 - (topY - fadeBottom) / panel_height, 1)), From 0901333ef3d1cda32dca3fe26e8513679ed01147 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 8 Aug 2021 17:20:46 +0700 Subject: [PATCH 1096/2442] add pinned property in comment --- osu.Game/Online/API/Requests/Responses/Comment.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Online/API/Requests/Responses/Comment.cs b/osu.Game/Online/API/Requests/Responses/Comment.cs index 05a24cec0e..32d489432d 100644 --- a/osu.Game/Online/API/Requests/Responses/Comment.cs +++ b/osu.Game/Online/API/Requests/Responses/Comment.cs @@ -58,6 +58,9 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"edited_by_id")] public long? EditedById { get; set; } + [JsonProperty(@"pinned")] + public bool Pinned { get; set; } + public User EditedUser { get; set; } public bool IsTopLevel => !ParentId.HasValue; From ff860b90a936d89cf0b251e8c8e866e52a884bd7 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 8 Aug 2021 17:21:02 +0700 Subject: [PATCH 1097/2442] add pinned test case for drawable comment --- osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs b/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs index 7b741accbb..5d1f3affc6 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs @@ -43,6 +43,9 @@ namespace osu.Game.Tests.Visual.Online { AddStep(description, () => { + if (description == "Pinned") + comment.Pinned = true; + comment.Message = text; container.Add(new DrawableComment(comment)); }); @@ -59,6 +62,7 @@ namespace osu.Game.Tests.Visual.Online private static object[] comments = { new[] { "Plain", "This is plain comment" }, + new[] { "Pinned", "This is pinned comment" }, new[] { "Link", "Please visit https://osu.ppy.sh" }, new[] From 1859e651b67760356d5ada53fc36570cc69acaa5 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 8 Aug 2021 17:21:29 +0700 Subject: [PATCH 1098/2442] add pinned mark in drawable comment --- osu.Game/Overlays/Comments/DrawableComment.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/osu.Game/Overlays/Comments/DrawableComment.cs b/osu.Game/Overlays/Comments/DrawableComment.cs index 3520b15b1e..389e61bc30 100644 --- a/osu.Game/Overlays/Comments/DrawableComment.cs +++ b/osu.Game/Overlays/Comments/DrawableComment.cs @@ -21,6 +21,7 @@ using osu.Framework.Extensions.IEnumerableExtensions; using System.Collections.Specialized; using osu.Framework.Localisation; using osu.Game.Overlays.Comments.Buttons; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Comments { @@ -143,6 +144,26 @@ namespace osu.Game.Overlays.Comments { AutoSizeAxes = Axes.Both }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(3, 0), + Alpha = Comment.Pinned ? 1 : 0, + Children = new Drawable[] + { + new SpriteIcon + { + Icon = FontAwesome.Solid.Thumbtack, + Size = new Vector2(14), + }, + new OsuSpriteText + { + Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold), + Text = CommentsStrings.Pinned, + } + }, + }, new ParentUsername(Comment), new OsuSpriteText { From 39b13efdd58953eaf1ae0548d8be87ab89c6d68e Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 13 Aug 2021 13:13:28 +0700 Subject: [PATCH 1099/2442] add pinned comments property in comment bundle --- osu.Game/Online/API/Requests/Responses/CommentBundle.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Online/API/Requests/Responses/CommentBundle.cs b/osu.Game/Online/API/Requests/Responses/CommentBundle.cs index d76ede67cd..0585d75f0c 100644 --- a/osu.Game/Online/API/Requests/Responses/CommentBundle.cs +++ b/osu.Game/Online/API/Requests/Responses/CommentBundle.cs @@ -24,6 +24,9 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"included_comments")] public List IncludedComments { get; set; } + [JsonProperty(@"pinned_comments")] + public List PinnedComments { get; set; } + private List userVotes; [JsonProperty(@"user_votes")] From c5ee8753b43a3713073707aeb799d20fc1d82378 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 13 Aug 2021 09:20:52 +0300 Subject: [PATCH 1100/2442] Notify users to read OpenTabletDriver's FAQ when tablet not detected --- .../Settings/Sections/Input/TabletSettings.cs | 47 ++++++++++++++++--- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index c7342c251d..7595ca59cb 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -9,9 +10,12 @@ using osu.Framework.Input.Handlers.Tablet; using osu.Framework.Localisation; using osu.Framework.Platform; using osu.Framework.Threading; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osuTK; using osu.Game.Localisation; +using osu.Game.Online.Chat; namespace osu.Game.Overlays.Settings.Sections.Input { @@ -52,7 +56,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input private FillFlowContainer mainSettings; - private OsuSpriteText noTabletMessage; + private FillFlowContainer noTabletMessage; protected override LocalisableString Header => TabletSettingsStrings.Tablet; @@ -62,7 +66,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input } [BackgroundDependencyLoader] - private void load() + private void load(OsuColour colours) { Children = new Drawable[] { @@ -73,12 +77,41 @@ namespace osu.Game.Overlays.Settings.Sections.Input Origin = Anchor.TopCentre, Current = tabletHandler.Enabled }, - noTabletMessage = new OsuSpriteText + noTabletMessage = new FillFlowContainer { - Text = TabletSettingsStrings.NoTabletDetected, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Padding = new MarginPadding { Horizontal = SettingsPanel.CONTENT_MARGINS } + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Padding = new MarginPadding { Horizontal = SettingsPanel.CONTENT_MARGINS }, + Spacing = new Vector2(5f), + Children = new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = TabletSettingsStrings.NoTabletDetected, + }, + new LinkFlowContainer + { + TextAnchor = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }.With(t => + { + t.NewLine(); + t.AddText("If your tablet is not getting detected properly,", s => s.Colour = colours.Yellow); + t.NewParagraph(); + t.AddText("read ", s => s.Colour = colours.Yellow); + t.AddLink("the driver's FAQ", LinkAction.External, RuntimeInfo.OS == RuntimeInfo.Platform.Windows + ? @"https://github.com/OpenTabletDriver/OpenTabletDriver/wiki/Windows-FAQ" + : @"https://github.com/OpenTabletDriver/OpenTabletDriver/wiki/Linux-FAQ"); + + t.AddText(" to troubleshoot the problem further.", s => s.Colour = colours.Yellow); + }), + } }, mainSettings = new FillFlowContainer { From 5cec50bdd14ad16b4b07e14605cb9d5d3dd2eddf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Aug 2021 15:24:43 +0900 Subject: [PATCH 1101/2442] Add comment mentioning why event bindings are not unbound --- osu.Game/Screens/OnlinePlay/Header.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/OnlinePlay/Header.cs b/osu.Game/Screens/OnlinePlay/Header.cs index 58c67a51e8..b0db9256f5 100644 --- a/osu.Game/Screens/OnlinePlay/Header.cs +++ b/osu.Game/Screens/OnlinePlay/Header.cs @@ -35,6 +35,7 @@ namespace osu.Game.Screens.OnlinePlay Origin = Anchor.CentreLeft, }; + // unnecessary to unbind these as this header has the same lifetime as the screen stack we are attaching to. stack.ScreenPushed += (_, __) => updateSubScreenTitle(); stack.ScreenExited += (_, __) => updateSubScreenTitle(); } From 3b6a8a2bae61e342da76a735cc49c97cf9c5d99d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Aug 2021 15:24:53 +0900 Subject: [PATCH 1102/2442] Rename background sprite and reduce load delay --- osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 978b35e4b1..c2ad0285b1 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -108,7 +108,7 @@ namespace osu.Game.Screens.OnlinePlay { RelativeSizeAxes = Axes.Both, BlurSigma = new Vector2(10), - Child = new HeaderBackgroundSprite + Child = new BeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both } @@ -304,13 +304,13 @@ namespace osu.Game.Screens.OnlinePlay } } - private class HeaderBackgroundSprite : OnlinePlayBackgroundSprite + private class BeatmapBackgroundSprite : OnlinePlayBackgroundSprite { protected override UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new BackgroundSprite { RelativeSizeAxes = Axes.Both }; private class BackgroundSprite : UpdateableBeatmapBackgroundSprite { - protected override double TransformDuration => 200; + protected override double LoadDelay => 200; } } From 543f6039e24071c28e18ae838185d863257638cb Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 13 Aug 2021 09:26:53 +0300 Subject: [PATCH 1103/2442] Display on Windows and Linux only --- .../Settings/Sections/Input/TabletSettings.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index 7595ca59cb..9ed85e44ec 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -101,15 +101,18 @@ namespace osu.Game.Overlays.Settings.Sections.Input AutoSizeAxes = Axes.Y, }.With(t => { - t.NewLine(); - t.AddText("If your tablet is not getting detected properly,", s => s.Colour = colours.Yellow); - t.NewParagraph(); - t.AddText("read ", s => s.Colour = colours.Yellow); - t.AddLink("the driver's FAQ", LinkAction.External, RuntimeInfo.OS == RuntimeInfo.Platform.Windows - ? @"https://github.com/OpenTabletDriver/OpenTabletDriver/wiki/Windows-FAQ" - : @"https://github.com/OpenTabletDriver/OpenTabletDriver/wiki/Linux-FAQ"); + if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows || RuntimeInfo.OS == RuntimeInfo.Platform.Linux) + { + t.NewLine(); + t.AddText("If your tablet is not getting detected properly,", s => s.Colour = colours.Yellow); + t.NewParagraph(); + t.AddText("read ", s => s.Colour = colours.Yellow); + t.AddLink("the driver's FAQ", LinkAction.External, RuntimeInfo.OS == RuntimeInfo.Platform.Windows + ? @"https://github.com/OpenTabletDriver/OpenTabletDriver/wiki/Windows-FAQ" + : @"https://github.com/OpenTabletDriver/OpenTabletDriver/wiki/Linux-FAQ"); - t.AddText(" to troubleshoot the problem further.", s => s.Colour = colours.Yellow); + t.AddText(" to troubleshoot the problem further.", s => s.Colour = colours.Yellow); + } }), } }, From dd7ca4b77b45ae0f69078afd1fdbedf7e82cad80 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Aug 2021 15:35:45 +0900 Subject: [PATCH 1104/2442] Increase "create room" button height --- osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 1c28405e40..7249ccdd93 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -30,8 +30,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge [Cached] public abstract class LoungeSubScreen : OnlinePlaySubScreen { - private const float button_height = 25; - public override string Title => "Lounge"; protected override UserActivity InitialActivity => new UserActivity.SearchingForLobby(); @@ -93,7 +91,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge RowDimensions = new[] { new Dimension(GridSizeMode.Absolute, Header.HEIGHT), - new Dimension(GridSizeMode.Absolute, button_height), + new Dimension(GridSizeMode.Absolute, 25), new Dimension(GridSizeMode.Absolute, 20) }, Content = new[] @@ -118,7 +116,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { Buttons.WithChild(CreateNewRoomButton().With(d => { - d.Size = new Vector2(150, button_height); + d.Anchor = Anchor.BottomLeft; + d.Origin = Anchor.BottomLeft; + d.Size = new Vector2(150, 37.5f); d.Action = () => Open(); })), new FillFlowContainer From f9f3339885152bed5bf6eaf46ba4bbf9e024b396 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Aug 2021 16:14:23 +0900 Subject: [PATCH 1105/2442] Fix vertical offset not being handled correctly during score panel detach process --- .../Multiplayer/MultiplayerTeamResultsScreen.cs | 6 +++--- osu.Game/Screens/Ranking/ResultsScreen.cs | 8 ++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs index e98898b9da..14a779dedf 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs @@ -46,9 +46,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { const float winner_background_half_height = 250; - ScorePanelList.Anchor = ScorePanelList.Origin = Anchor.TopCentre; - ScorePanelList.Scale = new Vector2(0.9f); - ScorePanelList.Y = 75; + VerticalScrollContent.Anchor = VerticalScrollContent.Origin = Anchor.TopCentre; + VerticalScrollContent.Scale = new Vector2(0.9f); + VerticalScrollContent.Y = 75; var redScore = teamScores.First().Value; var blueScore = teamScores.Last().Value; diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index b458d7c17f..d44d1f2cc9 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -40,6 +40,8 @@ namespace osu.Game.Screens.Ranking protected ScorePanelList ScorePanelList { get; private set; } + protected VerticalScrollContainer VerticalScrollContent { get; private set; } + [Resolved(CanBeNull = true)] private Player player { get; set; } @@ -77,7 +79,7 @@ namespace osu.Game.Screens.Ranking { new Drawable[] { - new VerticalScrollContainer + VerticalScrollContent = new VerticalScrollContainer { RelativeSizeAxes = Axes.Both, ScrollbarVisible = false, @@ -343,7 +345,7 @@ namespace osu.Game.Screens.Ranking { } - private class VerticalScrollContainer : OsuScrollContainer + protected class VerticalScrollContainer : OsuScrollContainer { protected override Container Content => content; @@ -351,6 +353,8 @@ namespace osu.Game.Screens.Ranking public VerticalScrollContainer() { + Masking = false; + base.Content.Add(content = new Container { RelativeSizeAxes = Axes.X }); } From cd842ccef8f1221eb59a71defb2dc19348e9ccd9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 13 Aug 2021 10:15:50 +0300 Subject: [PATCH 1106/2442] Improve message Co-authored-by: Dean Herbert --- .../Overlays/Settings/Sections/Input/TabletSettings.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index 9ed85e44ec..af94dcdaca 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -104,14 +104,11 @@ namespace osu.Game.Overlays.Settings.Sections.Input if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows || RuntimeInfo.OS == RuntimeInfo.Platform.Linux) { t.NewLine(); - t.AddText("If your tablet is not getting detected properly,", s => s.Colour = colours.Yellow); - t.NewParagraph(); - t.AddText("read ", s => s.Colour = colours.Yellow); - t.AddLink("the driver's FAQ", LinkAction.External, RuntimeInfo.OS == RuntimeInfo.Platform.Windows + t.AddText("If your tablet is not detected, please read ", s => s.Colour = colours.Yellow); + t.AddLink("this FAQ", LinkAction.External, RuntimeInfo.OS == RuntimeInfo.Platform.Windows ? @"https://github.com/OpenTabletDriver/OpenTabletDriver/wiki/Windows-FAQ" : @"https://github.com/OpenTabletDriver/OpenTabletDriver/wiki/Linux-FAQ"); - - t.AddText(" to troubleshoot the problem further.", s => s.Colour = colours.Yellow); + t.AddText(" for troubleshooting steps.", s => s.Colour = colours.Yellow); } }), } From db52549152f56e28cf22bb018ff22f32d19bb8de Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Aug 2021 16:20:53 +0900 Subject: [PATCH 1107/2442] Move below everything rather than proxying (works better with new design) --- osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 43da62d466..ceec609f6d 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -75,10 +75,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge OsuScrollContainer scrollContainer; - Container filterContainer; - InternalChildren = new[] { + loadingLayer = new LoadingLayer(true), new Container { RelativeSizeAxes = Axes.Both, @@ -159,8 +158,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge } }, }, - loadingLayer = new LoadingLayer(true), - filterContainer.CreateProxy() }; // scroll selected room into view on selection. From c1d67976e6accf5ca2980a6aa116e791e8e9432a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Aug 2021 16:29:36 +0900 Subject: [PATCH 1108/2442] Rename const, add xmldoc and make protected --- osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs | 4 ++-- osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs | 2 ++ osu.Game/OsuGame.cs | 9 ++++++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs b/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs index 21db7e2802..e58f85b0b3 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs @@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.Menus public void TestScreenOffsettingOnSettingsOverlay() { AddStep("open settings", () => Game.Settings.Show()); - AddUntilStep("right screen offset applied", () => Game.ScreenOffsetContainer.X == SettingsPanel.WIDTH * OsuGame.SCREEN_OFFSET_RATIO); + AddUntilStep("right screen offset applied", () => Game.ScreenOffsetContainer.X == SettingsPanel.WIDTH * TestOsuGame.SIDE_OVERLAY_OFFSET_RATIO); AddStep("hide settings", () => Game.Settings.Hide()); AddUntilStep("screen offset removed", () => Game.ScreenOffsetContainer.X == 0f); @@ -33,7 +33,7 @@ namespace osu.Game.Tests.Visual.Menus public void TestScreenOffsettingOnNotificationOverlay() { AddStep("open notifications", () => Game.Notifications.Show()); - AddUntilStep("right screen offset applied", () => Game.ScreenOffsetContainer.X == -NotificationOverlay.WIDTH * OsuGame.SCREEN_OFFSET_RATIO); + AddUntilStep("right screen offset applied", () => Game.ScreenOffsetContainer.X == -NotificationOverlay.WIDTH * TestOsuGame.SIDE_OVERLAY_OFFSET_RATIO); AddStep("hide notifications", () => Game.Notifications.Hide()); AddUntilStep("screen offset removed", () => Game.ScreenOffsetContainer.X == 0f); diff --git a/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs b/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs index 5cd55ed233..c9a1471e41 100644 --- a/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs +++ b/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs @@ -96,6 +96,8 @@ namespace osu.Game.Tests.Visual.Navigation public class TestOsuGame : OsuGame { + public new const float SIDE_OVERLAY_OFFSET_RATIO = OsuGame.SIDE_OVERLAY_OFFSET_RATIO; + public new ScreenStack ScreenStack => base.ScreenStack; public new BackButton BackButton => base.BackButton; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index f92f7e9e8c..6d76fec7c1 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -64,7 +64,10 @@ namespace osu.Game /// public class OsuGame : OsuGameBase, IKeyBindingHandler { - public const float SCREEN_OFFSET_RATIO = 0.125f; + /// + /// The amount of global offset to apply when a left/right anchored overlay is displayed (ie. settings or notifications). + /// + protected const float SIDE_OVERLAY_OFFSET_RATIO = 0.125f; public Toolbar Toolbar; @@ -1016,9 +1019,9 @@ namespace osu.Game var horizontalOffset = 0f; if (Settings.IsLoaded && Settings.IsPresent) - horizontalOffset += ToLocalSpace(Settings.ScreenSpaceDrawQuad.TopRight).X * SCREEN_OFFSET_RATIO; + horizontalOffset += ToLocalSpace(Settings.ScreenSpaceDrawQuad.TopRight).X * SIDE_OVERLAY_OFFSET_RATIO; if (Notifications.IsLoaded && Notifications.IsPresent) - horizontalOffset += (ToLocalSpace(Notifications.ScreenSpaceDrawQuad.TopLeft).X - DrawWidth) * SCREEN_OFFSET_RATIO; + horizontalOffset += (ToLocalSpace(Notifications.ScreenSpaceDrawQuad.TopLeft).X - DrawWidth) * SIDE_OVERLAY_OFFSET_RATIO; ScreenOffsetContainer.X = horizontalOffset; From da18c399e2d7cd531e019c8b140155a02135a4dd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Aug 2021 16:33:00 +0900 Subject: [PATCH 1109/2442] Remove unnecessary `IsPresent` override --- osu.Game/Overlays/SettingsPanel.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index 8b953e8655..f1c41c4b50 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -42,12 +42,6 @@ namespace osu.Game.Overlays protected override Container Content => ContentContainer; - /// - /// The always needs to be present for to process transforms while overlay is masked away. - /// todo: there may be a better solution for this and the existing , likely requires a refactor. - /// - public override bool IsPresent => true; - protected Sidebar Sidebar; private SidebarButton selectedSidebarButton; From 93b97e5110774787b27ba91cb595158a65491cbf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Aug 2021 16:35:22 +0900 Subject: [PATCH 1110/2442] Adjust ratio to match previous behaviour --- osu.Game/OsuGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 6d76fec7c1..fb682e0909 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -67,7 +67,7 @@ namespace osu.Game /// /// The amount of global offset to apply when a left/right anchored overlay is displayed (ie. settings or notifications). /// - protected const float SIDE_OVERLAY_OFFSET_RATIO = 0.125f; + protected const float SIDE_OVERLAY_OFFSET_RATIO = 0.05f; public Toolbar Toolbar; From 5a60b39643b172dd679cd5e81712c99422fff024 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Aug 2021 16:42:58 +0900 Subject: [PATCH 1111/2442] Remove unnecessary delimiters from song select filter splitting --- osu.Game/Screens/Select/FilterCriteria.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index b9e912df8e..f47bc5f466 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -56,7 +56,7 @@ namespace osu.Game.Screens.Select set { searchText = value; - SearchTerms = searchText.Split(new[] { ',', ' ', '!' }, StringSplitOptions.RemoveEmptyEntries).ToArray(); + SearchTerms = searchText.Split(' ', StringSplitOptions.RemoveEmptyEntries).ToArray(); SearchNumber = null; From f43ab323ffad425b250c0ed761c8c00c8895349b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 13 Aug 2021 11:12:26 +0300 Subject: [PATCH 1112/2442] Add shared class for notice text in settings --- .../Settings/Sections/Input/TabletSettings.cs | 9 +++----- osu.Game/Overlays/Settings/SettingsItem.cs | 8 +------ .../Overlays/Settings/SettingsNoticeText.cs | 21 +++++++++++++++++++ 3 files changed, 25 insertions(+), 13 deletions(-) create mode 100644 osu.Game/Overlays/Settings/SettingsNoticeText.cs diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index af94dcdaca..2816eb7037 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -11,7 +11,6 @@ using osu.Framework.Localisation; using osu.Framework.Platform; using osu.Framework.Threading; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osuTK; using osu.Game.Localisation; @@ -92,23 +91,21 @@ namespace osu.Game.Overlays.Settings.Sections.Input Origin = Anchor.TopCentre, Text = TabletSettingsStrings.NoTabletDetected, }, - new LinkFlowContainer + new SettingsNoticeText { TextAnchor = Anchor.TopCentre, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, }.With(t => { if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows || RuntimeInfo.OS == RuntimeInfo.Platform.Linux) { t.NewLine(); - t.AddText("If your tablet is not detected, please read ", s => s.Colour = colours.Yellow); + t.AddText("If your tablet is not detected, please read "); t.AddLink("this FAQ", LinkAction.External, RuntimeInfo.OS == RuntimeInfo.Platform.Windows ? @"https://github.com/OpenTabletDriver/OpenTabletDriver/wiki/Windows-FAQ" : @"https://github.com/OpenTabletDriver/OpenTabletDriver/wiki/Linux-FAQ"); - t.AddText(" for troubleshooting steps.", s => s.Colour = colours.Yellow); + t.AddText(" for troubleshooting steps."); } }), } diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index bd17c02af9..5d25605360 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -73,13 +73,7 @@ namespace osu.Game.Overlays.Settings return; // construct lazily for cases where the label is not needed (may be provided by the Control). - FlowContent.Add(warningText = new OsuTextFlowContainer - { - Colour = colours.Yellow, - Margin = new MarginPadding { Bottom = 5 }, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - }); + FlowContent.Add(warningText = new SettingsNoticeText { Margin = new MarginPadding { Bottom = 5 } }); } warningText.Alpha = hasValue ? 0 : 1; diff --git a/osu.Game/Overlays/Settings/SettingsNoticeText.cs b/osu.Game/Overlays/Settings/SettingsNoticeText.cs new file mode 100644 index 0000000000..6bb4a3f11c --- /dev/null +++ b/osu.Game/Overlays/Settings/SettingsNoticeText.cs @@ -0,0 +1,21 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Overlays.Settings +{ + public class SettingsNoticeText : LinkFlowContainer + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Colour = colours.Yellow; + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + } + } +} From 8910781bcdb4dac3a18e17c1962c6dd34421b550 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Aug 2021 17:39:09 +0900 Subject: [PATCH 1113/2442] Move listing polling component to LoungeSubScreen --- .../TestSceneLoungeRoomsContainer.cs | 2 +- .../TestSceneMultiplayerRoomManager.cs | 314 +++++++++--------- .../TestScenePlaylistsMatchSettingsOverlay.cs | 12 + osu.Game/Online/Rooms/Room.cs | 2 +- .../Components/ListingPollingComponent.cs | 23 +- .../OnlinePlay/Components/RoomManager.cs | 73 ++-- .../Components/RoomPollingComponent.cs | 15 +- .../Components/SelectionPollingComponent.cs | 14 +- osu.Game/Screens/OnlinePlay/IRoomManager.cs | 11 +- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 53 ++- .../OnlinePlay/Multiplayer/Multiplayer.cs | 30 -- .../Multiplayer/MultiplayerLoungeSubScreen.cs | 33 ++ .../Multiplayer/MultiplayerMatchSubScreen.cs | 9 +- .../Multiplayer/MultiplayerRoomManager.cs | 73 ---- .../Screens/OnlinePlay/OnlinePlayScreen.cs | 22 +- .../Screens/OnlinePlay/Playlists/Playlists.cs | 39 --- .../Playlists/PlaylistsLoungeSubScreen.cs | 3 + .../Playlists/PlaylistsRoomManager.cs | 21 -- .../Visual/OnlinePlay/BasicTestRoomManager.cs | 10 +- 19 files changed, 308 insertions(+), 451 deletions(-) delete mode 100644 osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomManager.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs index bcbdcd2a4f..f3d961a646 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs @@ -74,7 +74,7 @@ namespace osu.Game.Tests.Visual.Multiplayer var room = RoomManager.Rooms[1]; RoomManager.RemoveRoom(room); - RoomManager.AddRoom(room); + RoomManager.AddOrUpdateRoom(room); }); AddAssert("no selection", () => checkRoomSelected(null)); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs index b17427a30b..15e9112c47 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs @@ -1,157 +1,157 @@ -// 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 NUnit.Framework; -using osu.Framework.Testing; -using osu.Game.Online.Rooms; -using osu.Game.Screens.OnlinePlay.Components; -using osu.Game.Tests.Beatmaps; -using osu.Game.Tests.Visual.OnlinePlay; - -namespace osu.Game.Tests.Visual.Multiplayer -{ - [HeadlessTest] - public class TestSceneMultiplayerRoomManager : MultiplayerTestScene - { - protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new TestDependencies(); - - public TestSceneMultiplayerRoomManager() - : base(false) - { - } - - [Test] - public void TestPollsInitially() - { - AddStep("create room manager with a few rooms", () => - { - RoomManager.CreateRoom(createRoom(r => r.Name.Value = "1")); - RoomManager.PartRoom(); - RoomManager.CreateRoom(createRoom(r => r.Name.Value = "2")); - RoomManager.PartRoom(); - RoomManager.ClearRooms(); - }); - - AddAssert("manager polled for rooms", () => ((RoomManager)RoomManager).Rooms.Count == 2); - AddAssert("initial rooms received", () => RoomManager.InitialRoomsReceived.Value); - } - - [Test] - public void TestRoomsClearedOnDisconnection() - { - AddStep("create room manager with a few rooms", () => - { - RoomManager.CreateRoom(createRoom()); - RoomManager.PartRoom(); - RoomManager.CreateRoom(createRoom()); - RoomManager.PartRoom(); - }); - - AddStep("disconnect", () => Client.Disconnect()); - - AddAssert("rooms cleared", () => ((RoomManager)RoomManager).Rooms.Count == 0); - AddAssert("initial rooms not received", () => !RoomManager.InitialRoomsReceived.Value); - } - - [Test] - public void TestRoomsPolledOnReconnect() - { - AddStep("create room manager with a few rooms", () => - { - RoomManager.CreateRoom(createRoom()); - RoomManager.PartRoom(); - RoomManager.CreateRoom(createRoom()); - RoomManager.PartRoom(); - }); - - AddStep("disconnect", () => Client.Disconnect()); - AddStep("connect", () => Client.Connect()); - - AddAssert("manager polled for rooms", () => ((RoomManager)RoomManager).Rooms.Count == 2); - AddAssert("initial rooms received", () => RoomManager.InitialRoomsReceived.Value); - } - - [Test] - public void TestRoomsNotPolledWhenJoined() - { - AddStep("create room manager with a room", () => - { - RoomManager.CreateRoom(createRoom()); - RoomManager.ClearRooms(); - }); - - AddAssert("manager not polled for rooms", () => ((RoomManager)RoomManager).Rooms.Count == 0); - AddAssert("initial rooms not received", () => !RoomManager.InitialRoomsReceived.Value); - } - - [Test] - public void TestMultiplayerRoomJoinedWhenCreated() - { - AddStep("create room manager with a room", () => - { - RoomManager.CreateRoom(createRoom()); - }); - - AddUntilStep("multiplayer room joined", () => Client.Room != null); - } - - [Test] - public void TestMultiplayerRoomPartedWhenAPIRoomParted() - { - AddStep("create room manager with a room", () => - { - RoomManager.CreateRoom(createRoom()); - RoomManager.PartRoom(); - }); - - AddAssert("multiplayer room parted", () => Client.Room == null); - } - - [Test] - public void TestMultiplayerRoomJoinedWhenAPIRoomJoined() - { - AddStep("create room manager with a room", () => - { - var r = createRoom(); - RoomManager.CreateRoom(r); - RoomManager.PartRoom(); - RoomManager.JoinRoom(r); - }); - - AddUntilStep("multiplayer room joined", () => Client.Room != null); - } - - private Room createRoom(Action initFunc = null) - { - var room = new Room - { - Name = - { - Value = "test room" - }, - Playlist = - { - new PlaylistItem - { - Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo }, - Ruleset = { Value = Ruleset.Value } - } - } - }; - - initFunc?.Invoke(room); - return room; - } - - private class TestDependencies : MultiplayerTestSceneDependencies - { - public TestDependencies() - { - // Need to set these values as early as possible. - RoomManager.TimeBetweenListingPolls.Value = 1; - RoomManager.TimeBetweenSelectionPolls.Value = 1; - } - } - } -} +// // 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 NUnit.Framework; +// using osu.Framework.Testing; +// using osu.Game.Online.Rooms; +// using osu.Game.Screens.OnlinePlay.Components; +// using osu.Game.Tests.Beatmaps; +// using osu.Game.Tests.Visual.OnlinePlay; +// +// namespace osu.Game.Tests.Visual.Multiplayer +// { +// [HeadlessTest] +// public class TestSceneMultiplayerRoomManager : MultiplayerTestScene +// { +// protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new TestDependencies(); +// +// public TestSceneMultiplayerRoomManager() +// : base(false) +// { +// } +// +// [Test] +// public void TestPollsInitially() +// { +// AddStep("create room manager with a few rooms", () => +// { +// RoomManager.CreateRoom(createRoom(r => r.Name.Value = "1")); +// RoomManager.PartRoom(); +// RoomManager.CreateRoom(createRoom(r => r.Name.Value = "2")); +// RoomManager.PartRoom(); +// RoomManager.ClearRooms(); +// }); +// +// AddAssert("manager polled for rooms", () => ((RoomManager)RoomManager).Rooms.Count == 2); +// AddAssert("initial rooms received", () => RoomManager.InitialRoomsReceived.Value); +// } +// +// [Test] +// public void TestRoomsClearedOnDisconnection() +// { +// AddStep("create room manager with a few rooms", () => +// { +// RoomManager.CreateRoom(createRoom()); +// RoomManager.PartRoom(); +// RoomManager.CreateRoom(createRoom()); +// RoomManager.PartRoom(); +// }); +// +// AddStep("disconnect", () => Client.Disconnect()); +// +// AddAssert("rooms cleared", () => ((RoomManager)RoomManager).Rooms.Count == 0); +// AddAssert("initial rooms not received", () => !RoomManager.InitialRoomsReceived.Value); +// } +// +// [Test] +// public void TestRoomsPolledOnReconnect() +// { +// AddStep("create room manager with a few rooms", () => +// { +// RoomManager.CreateRoom(createRoom()); +// RoomManager.PartRoom(); +// RoomManager.CreateRoom(createRoom()); +// RoomManager.PartRoom(); +// }); +// +// AddStep("disconnect", () => Client.Disconnect()); +// AddStep("connect", () => Client.Connect()); +// +// AddAssert("manager polled for rooms", () => ((RoomManager)RoomManager).Rooms.Count == 2); +// AddAssert("initial rooms received", () => RoomManager.InitialRoomsReceived.Value); +// } +// +// [Test] +// public void TestRoomsNotPolledWhenJoined() +// { +// AddStep("create room manager with a room", () => +// { +// RoomManager.CreateRoom(createRoom()); +// RoomManager.ClearRooms(); +// }); +// +// AddAssert("manager not polled for rooms", () => ((RoomManager)RoomManager).Rooms.Count == 0); +// AddAssert("initial rooms not received", () => !RoomManager.InitialRoomsReceived.Value); +// } +// +// [Test] +// public void TestMultiplayerRoomJoinedWhenCreated() +// { +// AddStep("create room manager with a room", () => +// { +// RoomManager.CreateRoom(createRoom()); +// }); +// +// AddUntilStep("multiplayer room joined", () => Client.Room != null); +// } +// +// [Test] +// public void TestMultiplayerRoomPartedWhenAPIRoomParted() +// { +// AddStep("create room manager with a room", () => +// { +// RoomManager.CreateRoom(createRoom()); +// RoomManager.PartRoom(); +// }); +// +// AddAssert("multiplayer room parted", () => Client.Room == null); +// } +// +// [Test] +// public void TestMultiplayerRoomJoinedWhenAPIRoomJoined() +// { +// AddStep("create room manager with a room", () => +// { +// var r = createRoom(); +// RoomManager.CreateRoom(r); +// RoomManager.PartRoom(); +// RoomManager.JoinRoom(r); +// }); +// +// AddUntilStep("multiplayer room joined", () => Client.Room != null); +// } +// +// private Room createRoom(Action initFunc = null) +// { +// var room = new Room +// { +// Name = +// { +// Value = "test room" +// }, +// Playlist = +// { +// new PlaylistItem +// { +// Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo }, +// Ruleset = { Value = Ruleset.Value } +// } +// } +// }; +// +// initFunc?.Invoke(room); +// return room; +// } +// +// private class TestDependencies : MultiplayerTestSceneDependencies +// { +// public TestDependencies() +// { +// // Need to set these values as early as possible. +// RoomManager.TimeBetweenListingPolls.Value = 1; +// RoomManager.TimeBetweenSelectionPolls.Value = 1; +// } +// } +// } +// } diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs index cdc655500d..79af2d3099 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs @@ -141,6 +141,18 @@ namespace osu.Game.Tests.Visual.Playlists public IBindableList Rooms => null; + public void AddOrUpdateRoom(Room room) + { + } + + public void RemoveRoom(Room room) + { + } + + public void ClearRooms() + { + } + public void CreateRoom(Room room, Action onSuccess = null, Action onError = null) { if (CreateRequested == null) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index 4bd5b1a788..364336783d 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -134,7 +134,7 @@ namespace osu.Game.Online.Rooms /// The position of this in the list. This is not read from or written to the API. /// [JsonIgnore] - public readonly Bindable Position = new Bindable(-1); + public readonly Bindable Position = new Bindable(-1); public Room() { diff --git a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs index e50784fcbe..f686326d08 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -14,6 +15,9 @@ namespace osu.Game.Screens.OnlinePlay.Components /// public class ListingPollingComponent : RoomPollingComponent { + public IBindable HasPolledOnce => hasPolledOnce; + private readonly Bindable hasPolledOnce = new Bindable(); + [Resolved] private Bindable currentFilter { get; set; } @@ -25,7 +29,9 @@ namespace osu.Game.Screens.OnlinePlay.Components { currentFilter.BindValueChanged(_ => { - NotifyRoomsReceived(null); + RoomManager.ClearRooms(); + hasPolledOnce.Value = false; + if (IsLoaded) PollImmediately(); }); @@ -45,17 +51,16 @@ namespace osu.Game.Screens.OnlinePlay.Components pollReq.Success += result => { - for (int i = 0; i < result.Count; i++) + foreach (var existing in RoomManager.Rooms.ToArray()) { - if (result[i].RoomID.Value == selectedRoom.Value?.RoomID.Value) - { - // The listing request always has less information than the opened room, so don't include it. - result[i] = selectedRoom.Value; - break; - } + if (result.All(r => r.RoomID.Value != existing.RoomID.Value)) + RoomManager.RemoveRoom(existing); } - NotifyRoomsReceived(result); + foreach (var incoming in result) + RoomManager.AddOrUpdateRoom(incoming); + + hasPolledOnce.Value = true; tcs.SetResult(true); }; diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs index 422576648c..ab92adcac7 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs @@ -8,7 +8,6 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Logging; using osu.Game.Beatmaps; using osu.Game.Online.API; @@ -17,15 +16,12 @@ using osu.Game.Rulesets; namespace osu.Game.Screens.OnlinePlay.Components { - public abstract class RoomManager : CompositeDrawable, IRoomManager + public class RoomManager : Component, IRoomManager { public event Action RoomsUpdated; private readonly BindableList rooms = new BindableList(); - public IBindable InitialRoomsReceived => initialRoomsReceived; - private readonly Bindable initialRoomsReceived = new Bindable(); - public IBindableList Rooms => rooms; protected IBindable JoinedRoom => joinedRoom; @@ -40,15 +36,9 @@ namespace osu.Game.Screens.OnlinePlay.Components [Resolved] private IAPIProvider api { get; set; } - protected RoomManager() + public RoomManager() { RelativeSizeAxes = Axes.Both; - - InternalChildren = CreatePollingComponents().Select(p => - { - p.RoomsReceived = onRoomsReceived; - return p; - }).ToList(); } protected override void Dispose(bool isDisposing) @@ -118,56 +108,41 @@ namespace osu.Game.Screens.OnlinePlay.Components private readonly HashSet ignoredRooms = new HashSet(); - private void onRoomsReceived(List received) + public void AddOrUpdateRoom(Room room) { - if (received == null) - { - ClearRooms(); + Debug.Assert(room.RoomID.Value != null); + + if (ignoredRooms.Contains(room.RoomID.Value.Value)) return; - } - // Remove past matches - foreach (var r in rooms.ToList()) + room.Position.Value = -room.RoomID.Value.Value; + + try { - if (received.All(e => e.RoomID.Value != r.RoomID.Value)) - rooms.Remove(r); + update(room, room); + addRoom(room); } - - for (int i = 0; i < received.Count; i++) + catch (Exception ex) { - var room = received[i]; + Logger.Error(ex, $"Failed to update room: {room.Name.Value}."); - Debug.Assert(room.RoomID.Value != null); - - if (ignoredRooms.Contains(room.RoomID.Value.Value)) - continue; - - room.Position.Value = i; - - try - { - update(room, room); - addRoom(room); - } - catch (Exception ex) - { - Logger.Error(ex, $"Failed to update room: {room.Name.Value}."); - - ignoredRooms.Add(room.RoomID.Value.Value); - rooms.Remove(room); - } + ignoredRooms.Add(room.RoomID.Value.Value); + rooms.Remove(room); } - RoomsUpdated?.Invoke(); - initialRoomsReceived.Value = true; + notifyRoomsUpdated(); } - protected void RemoveRoom(Room room) => rooms.Remove(room); + public void RemoveRoom(Room room) + { + rooms.Remove(room); + notifyRoomsUpdated(); + } - protected void ClearRooms() + public void ClearRooms() { rooms.Clear(); - initialRoomsReceived.Value = false; + notifyRoomsUpdated(); } /// @@ -196,6 +171,6 @@ namespace osu.Game.Screens.OnlinePlay.Components existing.CopyFrom(room); } - protected abstract IEnumerable CreatePollingComponents(); + private void notifyRoomsUpdated() => Scheduler.AddOnce(() => RoomsUpdated?.Invoke()); } } diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/RoomPollingComponent.cs index b2ea3a05d6..95b4c4284c 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomPollingComponent.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomPollingComponent.cs @@ -1,29 +1,18 @@ // 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 osu.Framework.Allocation; using osu.Game.Online; using osu.Game.Online.API; -using osu.Game.Online.Rooms; namespace osu.Game.Screens.OnlinePlay.Components { public abstract class RoomPollingComponent : PollingComponent { - /// - /// Invoked when any s have been received from the API. - /// - /// Any s present locally but not returned by this event are to be removed from display. - /// If null, the display of local rooms is reset to an initial state. - /// - /// - public Action> RoomsReceived; - [Resolved] protected IAPIProvider API { get; private set; } - protected void NotifyRoomsReceived(List rooms) => RoomsReceived?.Invoke(rooms); + [Resolved] + protected IRoomManager RoomManager { get; set; } } } diff --git a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs index dcf3c94b76..88d9469f8c 100644 --- a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs +++ b/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs @@ -1,8 +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.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -48,17 +46,7 @@ namespace osu.Game.Screens.OnlinePlay.Components pollReq.Success += result => { - // existing rooms need to be ordered by their position because the received of NotifyRoomsReceives expects to be able to sort them based on this order. - var rooms = new List(roomManager.Rooms.OrderBy(r => r.Position.Value)); - - int index = rooms.FindIndex(r => r.RoomID.Value == result.RoomID.Value); - - if (index < 0) - return; - - rooms[index] = result; - - NotifyRoomsReceived(rooms); + RoomManager.AddOrUpdateRoom(result); tcs.SetResult(true); }; diff --git a/osu.Game/Screens/OnlinePlay/IRoomManager.cs b/osu.Game/Screens/OnlinePlay/IRoomManager.cs index 34c1393ff1..baf84e25f9 100644 --- a/osu.Game/Screens/OnlinePlay/IRoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/IRoomManager.cs @@ -18,16 +18,17 @@ namespace osu.Game.Screens.OnlinePlay /// event Action RoomsUpdated; - /// - /// Whether an initial listing of rooms has been received. - /// - IBindable InitialRoomsReceived { get; } - /// /// All the active s. /// IBindableList Rooms { get; } + void AddOrUpdateRoom(Room room); + + void RemoveRoom(Room room); + + void ClearRooms(); + /// /// Creates a new . /// diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 7249ccdd93..50f7baa44c 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -12,14 +12,17 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; +using osu.Framework.Logging; using osu.Framework.Screens; using osu.Framework.Threading; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; +using osu.Game.Input; using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Rulesets; +using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Users; @@ -41,10 +44,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge AutoSizeAxes = Axes.Both }; - private readonly IBindable initialRoomsReceived = new Bindable(); - private readonly IBindable operationInProgress = new Bindable(); - - private LoadingLayer loadingLayer; + protected ListingPollingComponent ListingPollingComponent { get; private set; } [Resolved] private Bindable selectedRoom { get; set; } @@ -61,9 +61,15 @@ namespace osu.Game.Screens.OnlinePlay.Lounge [Resolved] private IBindable ruleset { get; set; } + [Resolved(CanBeNull = true)] + private IdleTracker idleTracker { get; set; } + [CanBeNull] private IDisposable joiningRoomOperation { get; set; } + private readonly IBindable operationInProgress = new Bindable(); + private readonly IBindable isIdle = new BindableBool(); + private LoadingLayer loadingLayer; private RoomsContainer roomsContainer; private SearchTextBox searchTextBox; private Dropdown statusDropdown; @@ -77,6 +83,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge InternalChildren = new Drawable[] { + ListingPollingComponent = CreatePollingComponent(), new Container { RelativeSizeAxes = Axes.Both, @@ -176,8 +183,13 @@ namespace osu.Game.Screens.OnlinePlay.Lounge searchTextBox.Current.BindValueChanged(_ => updateFilterDebounced()); ruleset.BindValueChanged(_ => UpdateFilter()); - initialRoomsReceived.BindTo(RoomManager.InitialRoomsReceived); - initialRoomsReceived.BindValueChanged(_ => updateLoadingLayer()); + ListingPollingComponent.HasPolledOnce.BindValueChanged(_ => updateLoadingLayer()); + + if (idleTracker != null) + { + isIdle.BindTo(idleTracker.IsIdle); + isIdle.BindValueChanged(_ => updatePollingRate(), true); + } if (ongoingOperationTracker != null) { @@ -231,7 +243,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge public override void OnEntering(IScreen last) { base.OnEntering(last); - onReturning(); } @@ -266,11 +277,13 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private void onReturning() { + updatePollingRate(); searchTextBox.HoldFocus = true; } private void onLeaving() { + updatePollingRate(); searchTextBox.HoldFocus = false; // ensure any password prompt is dismissed. @@ -316,6 +329,24 @@ namespace osu.Game.Screens.OnlinePlay.Lounge this.Push(CreateRoomSubScreen(room)); } + private void updateLoadingLayer() + { + if (operationInProgress.Value || !ListingPollingComponent.HasPolledOnce.Value) + loadingLayer.Show(); + else + loadingLayer.Hide(); + } + + private void updatePollingRate() + { + if (!this.IsCurrentScreen()) + ListingPollingComponent.TimeBetweenPolls.Value = 0; + else + ListingPollingComponent.TimeBetweenPolls.Value = isIdle.Value ? 120000 : 15000; + + Logger.Log($"Polling adjusted (listing: {ListingPollingComponent.TimeBetweenPolls.Value})"); + } + protected abstract OsuButton CreateNewRoomButton(); /// @@ -326,13 +357,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge protected abstract RoomSubScreen CreateRoomSubScreen(Room room); - private void updateLoadingLayer() - { - if (operationInProgress.Value || !initialRoomsReceived.Value) - loadingLayer.Show(); - else - loadingLayer.Hide(); - } + protected abstract ListingPollingComponent CreatePollingComponent(); private class LoungeSearchTextBox : SearchTextBox { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs index 45928505bb..58b5b7bbeb 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Online.Multiplayer; using osu.Game.Screens.OnlinePlay.Components; @@ -23,35 +22,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer client.ChangeState(MultiplayerUserState.Idle); } - protected override void UpdatePollingRate(bool isIdle) - { - var multiplayerRoomManager = (MultiplayerRoomManager)RoomManager; - - if (!this.IsCurrentScreen()) - { - multiplayerRoomManager.TimeBetweenListingPolls.Value = 0; - multiplayerRoomManager.TimeBetweenSelectionPolls.Value = 0; - } - else - { - switch (CurrentSubScreen) - { - case LoungeSubScreen _: - multiplayerRoomManager.TimeBetweenListingPolls.Value = isIdle ? 120000 : 15000; - multiplayerRoomManager.TimeBetweenSelectionPolls.Value = isIdle ? 120000 : 15000; - break; - - // Don't poll inside the match or anywhere else. - default: - multiplayerRoomManager.TimeBetweenListingPolls.Value = 0; - multiplayerRoomManager.TimeBetweenSelectionPolls.Value = 0; - break; - } - } - - Logger.Log($"Polling adjusted (listing: {multiplayerRoomManager.TimeBetweenListingPolls.Value}, selection: {multiplayerRoomManager.TimeBetweenSelectionPolls.Value})"); - } - protected override string ScreenTitle => "Multiplayer"; protected override RoomManager CreateRoomManager() => new MultiplayerRoomManager(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs index ad7882abc2..db6096e93b 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs @@ -1,12 +1,16 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Threading.Tasks; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Logging; +using osu.Framework.Screens; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; +using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Match; @@ -21,6 +25,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private MultiplayerClient client { get; set; } + public override void OnResuming(IScreen last) + { + base.OnResuming(last); + ListingPollingComponent.PollImmediately(); + } + protected override FilterCriteria CreateFilterCriteria() { var criteria = base.CreateFilterCriteria(); @@ -39,6 +49,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override RoomSubScreen CreateRoomSubScreen(Room room) => new MultiplayerMatchSubScreen(room); + protected override ListingPollingComponent CreatePollingComponent() => new MultiplayerListingPollingComponent(); + protected override void OpenNewRoom(Room room) { if (client?.IsConnected.Value != true) @@ -49,5 +61,26 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer base.OpenNewRoom(room); } + + private class MultiplayerListingPollingComponent : ListingPollingComponent + { + public readonly IBindable AllowPolling = new Bindable(); + + protected override void LoadComplete() + { + base.LoadComplete(); + + AllowPolling.BindValueChanged(allowPolling => + { + if (!allowPolling.NewValue) + return; + + if (IsLoaded) + PollImmediately(); + }); + } + + protected override Task Poll() => !AllowPolling.Value ? Task.CompletedTask : base.Poll(); + } } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index a8e44dd56c..ec011634b1 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -49,9 +49,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private OngoingOperationTracker ongoingOperationTracker { get; set; } [Resolved] - private Bindable currentRoom { get; set; } - - private MultiplayerMatchSettingsOverlay settingsOverlay; + private Bindable currentRoom { get; set; } // Todo: This should not exist. private readonly IBindable isConnected = new Bindable(); @@ -59,6 +57,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private IDisposable readyClickOperation; private GridContainer mainContent; + private MultiplayerMatchSettingsOverlay settingsOverlay; public MultiplayerMatchSubScreen(Room room) { @@ -324,6 +323,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public override bool OnExiting(IScreen next) { + // We don't know whether we're the only participant in the room, and whether the room will close after we leave it as a result. + // To work around this, temporarily remove the room until the next listing poll retrieves it. + RoomManager?.RemoveRoom(currentRoom.Value); + // the room may not be left immediately after a disconnection due to async flow, // so checking the IsConnected status is also required. if (client.Room == null || !client.IsConnected.Value) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs index cbba4babe5..ae8c3113ff 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs @@ -2,9 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using System.Diagnostics; -using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.ExceptionExtensions; @@ -21,13 +19,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private MultiplayerClient multiplayerClient { get; set; } - public readonly Bindable TimeBetweenListingPolls = new Bindable(); - public readonly Bindable TimeBetweenSelectionPolls = new Bindable(); private readonly IBindable isConnected = new Bindable(); private readonly Bindable allowPolling = new Bindable(); - private ListingPollingComponent listingPollingComponent; - protected override void LoadComplete() { base.LoadComplete(); @@ -64,19 +58,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer if (JoinedRoom.Value == null) return; - var joinedRoom = JoinedRoom.Value; - base.PartRoom(); - multiplayerClient.LeaveRoom(); - - // Todo: This is not the way to do this. Basically when we're the only participant and the room closes, there's no way to know if this is actually the case. - // This is delayed one frame because upon exiting the match subscreen, multiplayer updates the polling rate and messes with polling. - Schedule(() => - { - RemoveRoom(joinedRoom); - listingPollingComponent.PollImmediately(); - }); } private void joinMultiplayerRoom(Room room, string password, Action onSuccess = null, Action onError = null) @@ -108,61 +91,5 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer // Don't poll when not connected or when a room has been joined. allowPolling.Value = isConnected.Value && JoinedRoom.Value == null; } - - protected override IEnumerable CreatePollingComponents() => new RoomPollingComponent[] - { - listingPollingComponent = new MultiplayerListingPollingComponent - { - TimeBetweenPolls = { BindTarget = TimeBetweenListingPolls }, - AllowPolling = { BindTarget = allowPolling } - }, - new MultiplayerSelectionPollingComponent - { - TimeBetweenPolls = { BindTarget = TimeBetweenSelectionPolls }, - AllowPolling = { BindTarget = allowPolling } - } - }; - - private class MultiplayerListingPollingComponent : ListingPollingComponent - { - public readonly IBindable AllowPolling = new Bindable(); - - protected override void LoadComplete() - { - base.LoadComplete(); - - AllowPolling.BindValueChanged(allowPolling => - { - if (!allowPolling.NewValue) - return; - - if (IsLoaded) - PollImmediately(); - }); - } - - protected override Task Poll() => !AllowPolling.Value ? Task.CompletedTask : base.Poll(); - } - - private class MultiplayerSelectionPollingComponent : SelectionPollingComponent - { - public readonly IBindable AllowPolling = new Bindable(); - - protected override void LoadComplete() - { - base.LoadComplete(); - - AllowPolling.BindValueChanged(allowPolling => - { - if (!allowPolling.NewValue) - return; - - if (IsLoaded) - PollImmediately(); - }); - } - - protected override Task Poll() => !AllowPolling.Value ? Task.CompletedTask : base.Poll(); - } } } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index c2ad0285b1..cfb19ca3d7 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -13,7 +13,6 @@ using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics.Containers; -using osu.Game.Input; using osu.Game.Online.API; using osu.Game.Online.Rooms; using osu.Game.Overlays; @@ -43,8 +42,6 @@ namespace osu.Game.Screens.OnlinePlay private LoungeSubScreen loungeSubScreen; private ScreenStack screenStack; - private readonly IBindable isIdle = new BindableBool(); - [Cached(Type = typeof(IRoomManager))] protected RoomManager RoomManager { get; private set; } @@ -66,9 +63,6 @@ namespace osu.Game.Screens.OnlinePlay [Resolved] protected IAPIProvider API { get; private set; } - [Resolved(CanBeNull = true)] - private IdleTracker idleTracker { get; set; } - [Resolved(CanBeNull = true)] private OsuLogo logo { get; set; } @@ -151,12 +145,6 @@ namespace osu.Game.Screens.OnlinePlay apiState.BindTo(API.State); apiState.BindValueChanged(onlineStateChanged, true); - - if (idleTracker != null) - { - isIdle.BindTo(idleTracker.IsIdle); - isIdle.BindValueChanged(idle => UpdatePollingRate(idle.NewValue), true); - } } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) @@ -166,8 +154,6 @@ namespace osu.Game.Screens.OnlinePlay return dependencies; } - protected abstract void UpdatePollingRate(bool isIdle); - private void forcefullyExit() { // This is temporary since we don't currently have a way to force screens to be exited @@ -203,8 +189,6 @@ namespace osu.Game.Screens.OnlinePlay screenStack.CurrentScreen.OnResuming(last); base.OnResuming(last); - - UpdatePollingRate(isIdle.Value); } public override void OnSuspending(IScreen next) @@ -214,8 +198,6 @@ namespace osu.Game.Screens.OnlinePlay Debug.Assert(screenStack.CurrentScreen != null); screenStack.CurrentScreen.OnSuspending(next); - - UpdatePollingRate(isIdle.Value); } public override bool OnExiting(IScreen next) @@ -279,15 +261,13 @@ namespace osu.Game.Screens.OnlinePlay if (newScreen is IOsuScreen newOsuScreen) ((IBindable)Activity).BindTo(newOsuScreen.Activity); - - UpdatePollingRate(isIdle.Value); } protected IScreen CurrentSubScreen => screenStack.CurrentScreen; protected abstract string ScreenTitle { get; } - protected abstract RoomManager CreateRoomManager(); + protected virtual RoomManager CreateRoomManager() => new RoomManager(); protected abstract LoungeSubScreen CreateLounge(); diff --git a/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs b/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs index 6a78e24ba1..1edeef77df 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs @@ -1,53 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Logging; -using osu.Framework.Screens; -using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Lounge; -using osu.Game.Screens.OnlinePlay.Match; namespace osu.Game.Screens.OnlinePlay.Playlists { public class Playlists : OnlinePlayScreen { - protected override void UpdatePollingRate(bool isIdle) - { - var playlistsManager = (PlaylistsRoomManager)RoomManager; - - if (!this.IsCurrentScreen()) - { - playlistsManager.TimeBetweenListingPolls.Value = 0; - playlistsManager.TimeBetweenSelectionPolls.Value = 0; - } - else - { - switch (CurrentSubScreen) - { - case LoungeSubScreen _: - playlistsManager.TimeBetweenListingPolls.Value = isIdle ? 120000 : 15000; - playlistsManager.TimeBetweenSelectionPolls.Value = isIdle ? 120000 : 15000; - break; - - case RoomSubScreen _: - playlistsManager.TimeBetweenListingPolls.Value = 0; - playlistsManager.TimeBetweenSelectionPolls.Value = isIdle ? 30000 : 5000; - break; - - default: - playlistsManager.TimeBetweenListingPolls.Value = 0; - playlistsManager.TimeBetweenSelectionPolls.Value = 0; - break; - } - } - - Logger.Log($"Polling adjusted (listing: {playlistsManager.TimeBetweenListingPolls.Value}, selection: {playlistsManager.TimeBetweenSelectionPolls.Value})"); - } - protected override string ScreenTitle => "Playlists"; - protected override RoomManager CreateRoomManager() => new PlaylistsRoomManager(); - protected override LoungeSubScreen CreateLounge() => new PlaylistsLoungeSubScreen(); } } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs index eee4d4f407..dced9b8691 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.Rooms; +using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Match; @@ -66,6 +67,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists protected override RoomSubScreen CreateRoomSubScreen(Room room) => new PlaylistsRoomSubScreen(room); + protected override ListingPollingComponent CreatePollingComponent() => new ListingPollingComponent(); + private enum PlaylistsCategory { Any, diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomManager.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomManager.cs deleted file mode 100644 index c55d1c3e94..0000000000 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomManager.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using osu.Framework.Bindables; -using osu.Game.Screens.OnlinePlay.Components; - -namespace osu.Game.Screens.OnlinePlay.Playlists -{ - public class PlaylistsRoomManager : RoomManager - { - public readonly Bindable TimeBetweenListingPolls = new Bindable(); - public readonly Bindable TimeBetweenSelectionPolls = new Bindable(); - - protected override IEnumerable CreatePollingComponents() => new RoomPollingComponent[] - { - new ListingPollingComponent { TimeBetweenPolls = { BindTarget = TimeBetweenListingPolls } }, - new SelectionPollingComponent { TimeBetweenPolls = { BindTarget = TimeBetweenSelectionPolls } } - }; - } -} diff --git a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs index d37a64fa4b..2e9c0d1d53 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs @@ -33,10 +33,10 @@ namespace osu.Game.Tests.Visual.OnlinePlay room.RoomID.Value ??= Rooms.Select(r => r.RoomID.Value).Where(id => id != null).Select(id => id.Value).DefaultIfEmpty().Max() + 1; onSuccess?.Invoke(room); - AddRoom(room); + AddOrUpdateRoom(room); } - public void AddRoom(Room room) + public void AddOrUpdateRoom(Room room) { Rooms.Add(room); RoomsUpdated?.Invoke(); @@ -48,6 +48,12 @@ namespace osu.Game.Tests.Visual.OnlinePlay RoomsUpdated?.Invoke(); } + public void ClearRooms() + { + Rooms.Clear(); + RoomsUpdated?.Invoke(); + } + public void JoinRoom(Room room, string password, Action onSuccess = null, Action onError = null) { JoinRoomRequested?.Invoke(room, password); From 7cbf4c48edc94af45f10468eb34ca24565d55e28 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Aug 2021 17:59:18 +0900 Subject: [PATCH 1114/2442] Fix multiplayer polling when not connected --- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 15 ++++---- .../Multiplayer/MultiplayerLoungeSubScreen.cs | 37 ++++++++++++++----- .../Multiplayer/MultiplayerRoomManager.cs | 22 ----------- 3 files changed, 34 insertions(+), 40 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 50f7baa44c..4525599a52 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -44,8 +44,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge AutoSizeAxes = Axes.Both }; - protected ListingPollingComponent ListingPollingComponent { get; private set; } - [Resolved] private Bindable selectedRoom { get; set; } @@ -73,6 +71,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private RoomsContainer roomsContainer; private SearchTextBox searchTextBox; private Dropdown statusDropdown; + private ListingPollingComponent listingPollingComponent; [BackgroundDependencyLoader] private void load() @@ -83,7 +82,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge InternalChildren = new Drawable[] { - ListingPollingComponent = CreatePollingComponent(), + listingPollingComponent = CreatePollingComponent(), new Container { RelativeSizeAxes = Axes.Both, @@ -183,7 +182,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge searchTextBox.Current.BindValueChanged(_ => updateFilterDebounced()); ruleset.BindValueChanged(_ => UpdateFilter()); - ListingPollingComponent.HasPolledOnce.BindValueChanged(_ => updateLoadingLayer()); + listingPollingComponent.HasPolledOnce.BindValueChanged(_ => updateLoadingLayer()); if (idleTracker != null) { @@ -331,7 +330,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private void updateLoadingLayer() { - if (operationInProgress.Value || !ListingPollingComponent.HasPolledOnce.Value) + if (operationInProgress.Value || !listingPollingComponent.HasPolledOnce.Value) loadingLayer.Show(); else loadingLayer.Hide(); @@ -340,11 +339,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private void updatePollingRate() { if (!this.IsCurrentScreen()) - ListingPollingComponent.TimeBetweenPolls.Value = 0; + listingPollingComponent.TimeBetweenPolls.Value = 0; else - ListingPollingComponent.TimeBetweenPolls.Value = isIdle.Value ? 120000 : 15000; + listingPollingComponent.TimeBetweenPolls.Value = isIdle.Value ? 120000 : 15000; - Logger.Log($"Polling adjusted (listing: {ListingPollingComponent.TimeBetweenPolls.Value})"); + Logger.Log($"Polling adjusted (listing: {listingPollingComponent.TimeBetweenPolls.Value})"); } protected abstract OsuButton CreateNewRoomButton(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs index db6096e93b..3f20202ec7 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs @@ -25,10 +25,23 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private MultiplayerClient client { get; set; } + private MultiplayerListingPollingComponent listingPollingComponent; + + private readonly IBindable isConnected = new Bindable(); + private readonly Bindable allowPolling = new Bindable(); + + protected override void LoadComplete() + { + base.LoadComplete(); + + isConnected.BindTo(client.IsConnected); + isConnected.BindValueChanged(c => Scheduler.AddOnce(() => listingPollingComponent.AllowPolling = c.NewValue)); + } + public override void OnResuming(IScreen last) { base.OnResuming(last); - ListingPollingComponent.PollImmediately(); + listingPollingComponent.PollImmediately(); } protected override FilterCriteria CreateFilterCriteria() @@ -49,7 +62,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override RoomSubScreen CreateRoomSubScreen(Room room) => new MultiplayerMatchSubScreen(room); - protected override ListingPollingComponent CreatePollingComponent() => new MultiplayerListingPollingComponent(); + protected override ListingPollingComponent CreatePollingComponent() => listingPollingComponent = new MultiplayerListingPollingComponent(); protected override void OpenNewRoom(Room room) { @@ -64,23 +77,27 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private class MultiplayerListingPollingComponent : ListingPollingComponent { - public readonly IBindable AllowPolling = new Bindable(); + private bool allowPolling; - protected override void LoadComplete() + public bool AllowPolling { - base.LoadComplete(); - - AllowPolling.BindValueChanged(allowPolling => + get => allowPolling; + set { - if (!allowPolling.NewValue) + if (allowPolling == value) + return; + + allowPolling = value; + + if (!allowPolling) return; if (IsLoaded) PollImmediately(); - }); + } } - protected override Task Poll() => !AllowPolling.Value ? Task.CompletedTask : base.Poll(); + protected override Task Poll() => !AllowPolling ? Task.CompletedTask : base.Poll(); } } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs index ae8c3113ff..2d94b2328d 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Extensions.ExceptionExtensions; using osu.Framework.Logging; using osu.Game.Online.Multiplayer; @@ -19,18 +18,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private MultiplayerClient multiplayerClient { get; set; } - private readonly IBindable isConnected = new Bindable(); - private readonly Bindable allowPolling = new Bindable(); - - protected override void LoadComplete() - { - base.LoadComplete(); - - isConnected.BindTo(multiplayerClient.IsConnected); - isConnected.BindValueChanged(_ => Scheduler.AddOnce(updatePolling)); - JoinedRoom.BindValueChanged(_ => Scheduler.AddOnce(updatePolling), true); - } - public override void CreateRoom(Room room, Action onSuccess = null, Action onError = null) => base.CreateRoom(room, r => joinMultiplayerRoom(r, r.Password.Value, onSuccess, onError), onError); @@ -82,14 +69,5 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } }); } - - private void updatePolling() - { - if (!isConnected.Value) - ClearRooms(); - - // Don't poll when not connected or when a room has been joined. - allowPolling.Value = isConnected.Value && JoinedRoom.Value == null; - } } } From 83935540caf400882750283724326934c556110b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Aug 2021 18:11:52 +0900 Subject: [PATCH 1115/2442] Add selection polling component to PlaylistsRoomSubScreen --- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 16 +++++-------- .../Playlists/PlaylistsRoomSubScreen.cs | 24 +++++++++++++++---- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 4525599a52..06db462205 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -59,9 +59,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge [Resolved] private IBindable ruleset { get; set; } - [Resolved(CanBeNull = true)] - private IdleTracker idleTracker { get; set; } - [CanBeNull] private IDisposable joiningRoomOperation { get; set; } @@ -73,9 +70,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private Dropdown statusDropdown; private ListingPollingComponent listingPollingComponent; - [BackgroundDependencyLoader] - private void load() + [BackgroundDependencyLoader(true)] + private void load([CanBeNull] IdleTracker idleTracker) { + if (idleTracker != null) + isIdle.BindTo(idleTracker.IsIdle); + filter ??= new Bindable(new FilterCriteria()); OsuScrollContainer scrollContainer; @@ -184,11 +184,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge listingPollingComponent.HasPolledOnce.BindValueChanged(_ => updateLoadingLayer()); - if (idleTracker != null) - { - isIdle.BindTo(idleTracker.IsIdle); - isIdle.BindValueChanged(_ => updatePollingRate(), true); - } + isIdle.BindValueChanged(_ => updatePollingRate(), true); if (ongoingOperationTracker != null) { diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 953c687087..682b055766 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -3,11 +3,14 @@ using System.Diagnostics; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Logging; using osu.Framework.Screens; +using osu.Game.Input; using osu.Game.Online.API; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Components; @@ -33,12 +36,13 @@ namespace osu.Game.Screens.OnlinePlay.Playlists [Resolved(typeof(Room), nameof(Room.Playlist))] private BindableList playlist { get; set; } + private readonly IBindable isIdle = new BindableBool(); + private MatchSettingsOverlay settingsOverlay; private MatchLeaderboard leaderboard; - private OverlinedHeader participantsHeader; - private GridContainer mainContent; + private SelectionPollingComponent selectionPollingComponent; public PlaylistsRoomSubScreen(Room room) { @@ -46,11 +50,15 @@ namespace osu.Game.Screens.OnlinePlay.Playlists Activity.Value = new UserActivity.InLobby(room); } - [BackgroundDependencyLoader] - private void load() + [BackgroundDependencyLoader(true)] + private void load([CanBeNull] IdleTracker idleTracker) { + if (idleTracker != null) + isIdle.BindTo(idleTracker.IsIdle); + AddRangeInternal(new Drawable[] { + selectionPollingComponent = new SelectionPollingComponent(), mainContent = new GridContainer { RelativeSizeAxes = Axes.Both, @@ -260,6 +268,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { base.LoadComplete(); + isIdle.BindValueChanged(_ => updatePollingRate(), true); + roomId.BindValueChanged(id => { if (id.NewValue == null) @@ -275,6 +285,12 @@ namespace osu.Game.Screens.OnlinePlay.Playlists }, true); } + private void updatePollingRate() + { + selectionPollingComponent.TimeBetweenPolls.Value = isIdle.Value ? 30000 : 5000; + Logger.Log($"Polling adjusted (selection: {selectionPollingComponent.TimeBetweenPolls.Value})"); + } + protected override Screen CreateGameplayScreen() => new PlayerLoader(() => new PlaylistsPlayer(SelectedItem.Value) { Exited = () => leaderboard.RefreshScores() From f5cea0cacd34535f58b43c216f03ed60b1bb31ec Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 13 Aug 2021 12:12:20 +0300 Subject: [PATCH 1116/2442] Fix failing test and rename to match new behaviour --- .../Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index a9004987df..080a1502b0 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -84,7 +84,7 @@ namespace osu.Game.Tests.Visual.Multiplayer } [Test] - public void TestSpectatorPlayerSettingsHidden() + public void TestSpectatorPlayerInteractiveElementsHidden() { start(new[] { PLAYER_1_ID, PLAYER_2_ID }); loadSpectateScreen(false); @@ -96,7 +96,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("all interactive elements removed", () => this.ChildrenOfType().All(p => !p.ChildrenOfType().Any() && !p.ChildrenOfType().Any() && - !p.ChildrenOfType().Single().ShowHandle)); + p.ChildrenOfType().SingleOrDefault()?.ShowHandle == false)); } [Test] From 6a46105b5efe9b6a2fcaa95838f53d1793daeb90 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Aug 2021 18:12:32 +0900 Subject: [PATCH 1117/2442] Fix incorrect dependency --- osu.Game/Screens/OnlinePlay/Components/RoomPollingComponent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/RoomPollingComponent.cs index 95b4c4284c..cd224a7347 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomPollingComponent.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomPollingComponent.cs @@ -13,6 +13,6 @@ namespace osu.Game.Screens.OnlinePlay.Components protected IAPIProvider API { get; private set; } [Resolved] - protected IRoomManager RoomManager { get; set; } + protected IRoomManager RoomManager { get; private set; } } } From 1bae7173d3794d1cecca63fb31fecbc4a64ef3f9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Aug 2021 18:13:55 +0900 Subject: [PATCH 1118/2442] Fix initial multiplayer poll --- .../OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs index 3f20202ec7..d36462f482 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs @@ -28,7 +28,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private MultiplayerListingPollingComponent listingPollingComponent; private readonly IBindable isConnected = new Bindable(); - private readonly Bindable allowPolling = new Bindable(); protected override void LoadComplete() { @@ -36,6 +35,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer isConnected.BindTo(client.IsConnected); isConnected.BindValueChanged(c => Scheduler.AddOnce(() => listingPollingComponent.AllowPolling = c.NewValue)); + listingPollingComponent.AllowPolling = isConnected.Value; } public override void OnResuming(IScreen last) From d527eb3d8b8f89bafa8870fe691f3ef6127fca67 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Fri, 13 Aug 2021 17:15:18 +0800 Subject: [PATCH 1119/2442] Apply suggestions from code review Co-authored-by: Dean Herbert --- osu.Game/Localisation/GameplaySettingsStrings.cs | 6 +++--- osu.Game/Localisation/GeneralSettingsStrings.cs | 2 +- .../Overlays/Settings/Sections/Gameplay/GeneralSettings.cs | 6 +++--- .../Overlays/Settings/Sections/General/LanguageSettings.cs | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Localisation/GameplaySettingsStrings.cs b/osu.Game/Localisation/GameplaySettingsStrings.cs index 4354272ab5..c63fa5ddcc 100644 --- a/osu.Game/Localisation/GameplaySettingsStrings.cs +++ b/osu.Game/Localisation/GameplaySettingsStrings.cs @@ -22,12 +22,12 @@ namespace osu.Game.Localisation /// /// "Background dim" /// - public static LocalisableString Dim => new TranslatableString(getKey(@"dim"), @"Background dim"); + public static LocalisableString BackgroundDim => new TranslatableString(getKey(@"dim"), @"Background dim"); /// /// "Background blur" /// - public static LocalisableString Blur => new TranslatableString(getKey(@"blur"), @"Background blur"); + public static LocalisableString BackgroundBlur => new TranslatableString(getKey(@"blur"), @"Background blur"); /// /// "Lighten playfield during breaks" @@ -57,7 +57,7 @@ namespace osu.Game.Localisation /// /// "Always show key overlay" /// - public static LocalisableString KeyOverlay => new TranslatableString(getKey(@"key_overlay"), @"Always show key overlay"); + public static LocalisableString AlwaysShowKeyOverlay => new TranslatableString(getKey(@"key_overlay"), @"Always show key overlay"); /// /// "Positional hitsounds" diff --git a/osu.Game/Localisation/GeneralSettingsStrings.cs b/osu.Game/Localisation/GeneralSettingsStrings.cs index 19fb8de972..a60e4891f4 100644 --- a/osu.Game/Localisation/GeneralSettingsStrings.cs +++ b/osu.Game/Localisation/GeneralSettingsStrings.cs @@ -27,7 +27,7 @@ namespace osu.Game.Localisation /// /// "Prefer metadata in original language" /// - public static LocalisableString PreferOriginal => new TranslatableString(getKey(@"prefer_original"), @"Prefer metadata in original language"); + public static LocalisableString PreferOriginalMetadataLanguage => new TranslatableString(getKey(@"prefer_original"), @"Prefer metadata in original language"); /// /// "Updates" diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index efb586fdca..04332fcdd3 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -22,14 +22,14 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay { new SettingsSlider { - LabelText = GameplaySettingsStrings.Dim, + LabelText = GameplaySettingsStrings.BackgroundDim, Current = config.GetBindable(OsuSetting.DimLevel), KeyboardStep = 0.01f, DisplayAsPercentage = true }, new SettingsSlider { - LabelText = GameplaySettingsStrings.Blur, + LabelText = GameplaySettingsStrings.BackgroundBlur, Current = config.GetBindable(OsuSetting.BlurLevel), KeyboardStep = 0.01f, DisplayAsPercentage = true @@ -62,7 +62,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay }, new SettingsCheckbox { - LabelText = GameplaySettingsStrings.KeyOverlay, + LabelText = GameplaySettingsStrings.AlwaysShowKeyOverlay, Current = config.GetBindable(OsuSetting.KeyOverlay) }, new SettingsCheckbox diff --git a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs index ac95a713bf..200618c469 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs @@ -31,7 +31,7 @@ namespace osu.Game.Overlays.Settings.Sections.General }, new SettingsCheckbox { - LabelText = GeneralSettingsStrings.PreferOriginal, + LabelText = GeneralSettingsStrings.PreferOriginalMetadataLanguage, Current = frameworkConfig.GetBindable(FrameworkSetting.ShowUnicode) }, }; From 1f992e67f3b29f63a0f965eb6c4e3ed26a6699c8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Aug 2021 18:17:18 +0900 Subject: [PATCH 1120/2442] Fix listing polling rate when entering room --- osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 06db462205..abe06bae9e 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -184,7 +184,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge listingPollingComponent.HasPolledOnce.BindValueChanged(_ => updateLoadingLayer()); - isIdle.BindValueChanged(_ => updatePollingRate(), true); + isIdle.BindValueChanged(_ => updatePollingRate(this.IsCurrentScreen()), true); if (ongoingOperationTracker != null) { @@ -272,13 +272,13 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private void onReturning() { - updatePollingRate(); + updatePollingRate(true); searchTextBox.HoldFocus = true; } private void onLeaving() { - updatePollingRate(); + updatePollingRate(false); searchTextBox.HoldFocus = false; // ensure any password prompt is dismissed. @@ -332,9 +332,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge loadingLayer.Hide(); } - private void updatePollingRate() + private void updatePollingRate(bool isCurrentScreen) { - if (!this.IsCurrentScreen()) + if (!isCurrentScreen) listingPollingComponent.TimeBetweenPolls.Value = 0; else listingPollingComponent.TimeBetweenPolls.Value = isIdle.Value ? 120000 : 15000; From c71a581106f1640a4303ead14755820cfc1c29d7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Aug 2021 18:24:19 +0900 Subject: [PATCH 1121/2442] Fix exception when leaving match --- .../OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs | 9 ++++++++- .../OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 8 ++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs index d36462f482..0756c78211 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs @@ -41,7 +41,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public override void OnResuming(IScreen last) { base.OnResuming(last); - listingPollingComponent.PollImmediately(); + + // Upon having left a room, we don't know whether we were the only participant, and whether the room is now closed as a result of leaving it. + // To work around this, temporarily clear all rooms until the next listing poll. + if (last is MultiplayerMatchSubScreen match) + { + RoomManager.RemoveRoom(match.Room); + listingPollingComponent.PollImmediately(); + } } protected override FilterCriteria CreateFilterCriteria() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index ec011634b1..72ba4d62fb 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -42,6 +42,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public override string ShortTitle => "room"; + public readonly Room Room; + [Resolved] private MultiplayerClient client { get; set; } @@ -61,6 +63,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public MultiplayerMatchSubScreen(Room room) { + Room = room; + Title = room.RoomID.Value == null ? "New room" : room.Name.Value; Activity.Value = new UserActivity.InLobby(room); } @@ -323,10 +327,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public override bool OnExiting(IScreen next) { - // We don't know whether we're the only participant in the room, and whether the room will close after we leave it as a result. - // To work around this, temporarily remove the room until the next listing poll retrieves it. - RoomManager?.RemoveRoom(currentRoom.Value); - // the room may not be left immediately after a disconnection due to async flow, // so checking the IsConnected status is also required. if (client.Room == null || !client.IsConnected.Value) From fbadc4897e211db035eb495d19cc797535e4fbc1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Aug 2021 18:28:20 +0900 Subject: [PATCH 1122/2442] Remove test scene --- .../TestSceneMultiplayerRoomManager.cs | 157 ------------------ 1 file changed, 157 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs deleted file mode 100644 index 15e9112c47..0000000000 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs +++ /dev/null @@ -1,157 +0,0 @@ -// // 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 NUnit.Framework; -// using osu.Framework.Testing; -// using osu.Game.Online.Rooms; -// using osu.Game.Screens.OnlinePlay.Components; -// using osu.Game.Tests.Beatmaps; -// using osu.Game.Tests.Visual.OnlinePlay; -// -// namespace osu.Game.Tests.Visual.Multiplayer -// { -// [HeadlessTest] -// public class TestSceneMultiplayerRoomManager : MultiplayerTestScene -// { -// protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new TestDependencies(); -// -// public TestSceneMultiplayerRoomManager() -// : base(false) -// { -// } -// -// [Test] -// public void TestPollsInitially() -// { -// AddStep("create room manager with a few rooms", () => -// { -// RoomManager.CreateRoom(createRoom(r => r.Name.Value = "1")); -// RoomManager.PartRoom(); -// RoomManager.CreateRoom(createRoom(r => r.Name.Value = "2")); -// RoomManager.PartRoom(); -// RoomManager.ClearRooms(); -// }); -// -// AddAssert("manager polled for rooms", () => ((RoomManager)RoomManager).Rooms.Count == 2); -// AddAssert("initial rooms received", () => RoomManager.InitialRoomsReceived.Value); -// } -// -// [Test] -// public void TestRoomsClearedOnDisconnection() -// { -// AddStep("create room manager with a few rooms", () => -// { -// RoomManager.CreateRoom(createRoom()); -// RoomManager.PartRoom(); -// RoomManager.CreateRoom(createRoom()); -// RoomManager.PartRoom(); -// }); -// -// AddStep("disconnect", () => Client.Disconnect()); -// -// AddAssert("rooms cleared", () => ((RoomManager)RoomManager).Rooms.Count == 0); -// AddAssert("initial rooms not received", () => !RoomManager.InitialRoomsReceived.Value); -// } -// -// [Test] -// public void TestRoomsPolledOnReconnect() -// { -// AddStep("create room manager with a few rooms", () => -// { -// RoomManager.CreateRoom(createRoom()); -// RoomManager.PartRoom(); -// RoomManager.CreateRoom(createRoom()); -// RoomManager.PartRoom(); -// }); -// -// AddStep("disconnect", () => Client.Disconnect()); -// AddStep("connect", () => Client.Connect()); -// -// AddAssert("manager polled for rooms", () => ((RoomManager)RoomManager).Rooms.Count == 2); -// AddAssert("initial rooms received", () => RoomManager.InitialRoomsReceived.Value); -// } -// -// [Test] -// public void TestRoomsNotPolledWhenJoined() -// { -// AddStep("create room manager with a room", () => -// { -// RoomManager.CreateRoom(createRoom()); -// RoomManager.ClearRooms(); -// }); -// -// AddAssert("manager not polled for rooms", () => ((RoomManager)RoomManager).Rooms.Count == 0); -// AddAssert("initial rooms not received", () => !RoomManager.InitialRoomsReceived.Value); -// } -// -// [Test] -// public void TestMultiplayerRoomJoinedWhenCreated() -// { -// AddStep("create room manager with a room", () => -// { -// RoomManager.CreateRoom(createRoom()); -// }); -// -// AddUntilStep("multiplayer room joined", () => Client.Room != null); -// } -// -// [Test] -// public void TestMultiplayerRoomPartedWhenAPIRoomParted() -// { -// AddStep("create room manager with a room", () => -// { -// RoomManager.CreateRoom(createRoom()); -// RoomManager.PartRoom(); -// }); -// -// AddAssert("multiplayer room parted", () => Client.Room == null); -// } -// -// [Test] -// public void TestMultiplayerRoomJoinedWhenAPIRoomJoined() -// { -// AddStep("create room manager with a room", () => -// { -// var r = createRoom(); -// RoomManager.CreateRoom(r); -// RoomManager.PartRoom(); -// RoomManager.JoinRoom(r); -// }); -// -// AddUntilStep("multiplayer room joined", () => Client.Room != null); -// } -// -// private Room createRoom(Action initFunc = null) -// { -// var room = new Room -// { -// Name = -// { -// Value = "test room" -// }, -// Playlist = -// { -// new PlaylistItem -// { -// Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo }, -// Ruleset = { Value = Ruleset.Value } -// } -// } -// }; -// -// initFunc?.Invoke(room); -// return room; -// } -// -// private class TestDependencies : MultiplayerTestSceneDependencies -// { -// public TestDependencies() -// { -// // Need to set these values as early as possible. -// RoomManager.TimeBetweenListingPolls.Value = 1; -// RoomManager.TimeBetweenSelectionPolls.Value = 1; -// } -// } -// } -// } From 7cf6b551d353ffd8393b3ed4d7489f5c6d23bca3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 13 Aug 2021 13:01:17 +0300 Subject: [PATCH 1123/2442] Replace until step with wait step with explanatory comment --- .../Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index 080a1502b0..62a9b597ad 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -93,10 +93,15 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("all player loader settings hidden", () => this.ChildrenOfType().All(l => !l.ChildrenOfType>().Any())); AddUntilStep("wait for players to load", () => spectatorScreen.AllPlayersLoaded); - AddUntilStep("all interactive elements removed", () => this.ChildrenOfType().All(p => + + // components wrapped in skinnable target containers load asynchronously, potentially taking more than one frame to load. + // wait once to properly execute the assert. + AddWaitStep("wait for async load", 1); + + AddAssert("all interactive elements removed", () => this.ChildrenOfType().All(p => !p.ChildrenOfType().Any() && !p.ChildrenOfType().Any() && - p.ChildrenOfType().SingleOrDefault()?.ShowHandle == false)); + !p.ChildrenOfType().Single().ShowHandle)); } [Test] From e3f91413cf0397c68c37b043a10fe5a04a2e89fe Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 13 Aug 2021 13:23:34 +0700 Subject: [PATCH 1124/2442] add pinned comment test case for comment container --- .../Online/TestSceneCommentsContainer.cs | 106 ++++++++++++------ 1 file changed, 71 insertions(+), 35 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs index cd22bb2513..3ad3474fc0 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs @@ -42,19 +42,21 @@ namespace osu.Game.Tests.Visual.Online () => commentsContainer.ChildrenOfType().Single().IsLoading); } - [Test] - public void TestSingleCommentsPage() + [TestCase(false)] + [TestCase(true)] + public void TestSingleCommentsPage(bool withPinned) { - setUpCommentsResponse(exampleComments); + setUpCommentsResponse(getExampleComments(withPinned)); AddStep("show comments", () => commentsContainer.ShowComments(CommentableType.Beatmapset, 123)); AddUntilStep("show more button hidden", () => commentsContainer.ChildrenOfType().Single().Alpha == 0); } - [Test] - public void TestMultipleCommentPages() + [TestCase(false)] + [TestCase(true)] + public void TestMultipleCommentPages(bool withPinned) { - var comments = exampleComments; + var comments = getExampleComments(withPinned); comments.HasMore = true; comments.TopLevelCount = 10; @@ -64,11 +66,12 @@ namespace osu.Game.Tests.Visual.Online () => commentsContainer.ChildrenOfType().Single().Alpha == 1); } - [Test] - public void TestMultipleLoads() + [TestCase(false)] + [TestCase(true)] + public void TestMultipleLoads(bool withPinned) { - var comments = exampleComments; - int topLevelCommentCount = exampleComments.Comments.Count; + var comments = getExampleComments(withPinned); + int topLevelCommentCount = comments.Comments.Count; AddStep("hide container", () => commentsContainer.Hide()); setUpCommentsResponse(comments); @@ -92,38 +95,71 @@ namespace osu.Game.Tests.Visual.Online }; }); - private CommentBundle exampleComments => new CommentBundle + private CommentBundle getExampleComments(bool withPinned = false) { - Comments = new List + var bundle = new CommentBundle { - new Comment + Comments = new List { - Id = 1, - Message = "This is a comment", - LegacyName = "FirstUser", - CreatedAt = DateTimeOffset.Now, - VotesCount = 19, - RepliesCount = 1 + new Comment + { + Id = 1, + Message = "This is a comment", + LegacyName = "FirstUser", + CreatedAt = DateTimeOffset.Now, + VotesCount = 19, + RepliesCount = 1 + }, + new Comment + { + Id = 5, + ParentId = 1, + Message = "This is a child comment", + LegacyName = "SecondUser", + CreatedAt = DateTimeOffset.Now, + VotesCount = 4, + }, + new Comment + { + Id = 10, + Message = "This is another comment", + LegacyName = "ThirdUser", + CreatedAt = DateTimeOffset.Now, + VotesCount = 0 + }, }, - new Comment + IncludedComments = new List(), + PinnedComments = new List(), + }; + + if (withPinned) + { + var pinnedComment = new Comment { - Id = 5, - ParentId = 1, - Message = "This is a child comment", - LegacyName = "SecondUser", + Id = 15, + Message = "This is pinned comment", + LegacyName = "PinnedUser", CreatedAt = DateTimeOffset.Now, - VotesCount = 4, - }, - new Comment + VotesCount = 999, + Pinned = true, + RepliesCount = 1, + }; + + bundle.Comments.Add(pinnedComment); + bundle.PinnedComments.Add(pinnedComment); + + bundle.Comments.Add(new Comment { - Id = 10, - Message = "This is another comment", - LegacyName = "ThirdUser", + Id = 20, + Message = "Reply to pinned comment", + LegacyName = "AbandonedUser", CreatedAt = DateTimeOffset.Now, - VotesCount = 0 - }, - }, - IncludedComments = new List(), - }; + VotesCount = 0, + ParentId = 15, + }); + } + + return bundle; + } } } From c32ba9e38fe45f6200c8305fcc0938046cb792e6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 13 Aug 2021 15:06:11 +0300 Subject: [PATCH 1125/2442] Remove arbitrarily-set wait step with until step instead, keeping the comment --- .../Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index 62a9b597ad..f1ddfefe33 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -95,10 +95,8 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for players to load", () => spectatorScreen.AllPlayersLoaded); // components wrapped in skinnable target containers load asynchronously, potentially taking more than one frame to load. - // wait once to properly execute the assert. - AddWaitStep("wait for async load", 1); - - AddAssert("all interactive elements removed", () => this.ChildrenOfType().All(p => + // therefore use until step rather than direct assert to account for that. + AddUntilStep("all interactive elements removed", () => this.ChildrenOfType().All(p => !p.ChildrenOfType().Any() && !p.ChildrenOfType().Any() && !p.ChildrenOfType().Single().ShowHandle)); From 1fed9193f86c4052393c1a58ea09c0f8c2756272 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 13 Aug 2021 15:24:10 +0300 Subject: [PATCH 1126/2442] Revert reverted segment to fix failure --- .../Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index f1ddfefe33..9bb9c24c6b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -99,7 +99,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("all interactive elements removed", () => this.ChildrenOfType().All(p => !p.ChildrenOfType().Any() && !p.ChildrenOfType().Any() && - !p.ChildrenOfType().Single().ShowHandle)); + p.ChildrenOfType().SingleOrDefault()?.ShowHandle == false)); } [Test] From 480d5ffa5d1370539f10c69ca3a05450e8a7a5ad Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 13 Aug 2021 19:22:46 +0700 Subject: [PATCH 1127/2442] add pinned comment to users setter in comment bundle --- .../API/Requests/Responses/CommentBundle.cs | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/CommentBundle.cs b/osu.Game/Online/API/Requests/Responses/CommentBundle.cs index 0585d75f0c..4070df493d 100644 --- a/osu.Game/Online/API/Requests/Responses/CommentBundle.cs +++ b/osu.Game/Online/API/Requests/Responses/CommentBundle.cs @@ -4,6 +4,7 @@ using Newtonsoft.Json; using osu.Game.Users; using System.Collections.Generic; +using System.Linq; namespace osu.Game.Online.API.Requests.Responses { @@ -52,26 +53,17 @@ namespace osu.Game.Online.API.Requests.Responses { users = value; - value.ForEach(u => + foreach (var user in value) { - Comments.ForEach(c => + foreach (var comment in Comments.Concat(IncludedComments).Concat(PinnedComments)) { - if (c.UserId == u.Id) - c.User = u; + if (comment.UserId == user.Id) + comment.User = user; - if (c.EditedById == u.Id) - c.EditedUser = u; - }); - - IncludedComments.ForEach(c => - { - if (c.UserId == u.Id) - c.User = u; - - if (c.EditedById == u.Id) - c.EditedUser = u; - }); - }); + if (comment.EditedById == user.Id) + comment.EditedUser = user; + } + } } } From cf0e9a1eec242658bddb152bd47ae51adc3e29d5 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 13 Aug 2021 19:01:52 +0700 Subject: [PATCH 1128/2442] add pinned content section --- .../Overlays/Comments/CommentsContainer.cs | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Comments/CommentsContainer.cs b/osu.Game/Overlays/Comments/CommentsContainer.cs index fe8d6f0178..9abae63f93 100644 --- a/osu.Game/Overlays/Comments/CommentsContainer.cs +++ b/osu.Game/Overlays/Comments/CommentsContainer.cs @@ -38,6 +38,7 @@ namespace osu.Game.Overlays.Comments private CancellationTokenSource loadCancellation; private int currentPage; + private FillFlowContainer pinnedContent; private FillFlowContainer content; private DeletedCommentsCounter deletedCommentsCounter; private CommentsShowMoreButton moreButton; @@ -63,6 +64,25 @@ namespace osu.Game.Overlays.Comments Children = new Drawable[] { commentCounter = new TotalCommentsCounter(), + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background4, + }, + pinnedContent = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + }, + }, + }, new CommentsHeader { Sort = { BindTarget = Sort }, @@ -173,6 +193,7 @@ namespace osu.Game.Overlays.Comments deletedCommentsCounter.Count.Value = 0; moreButton.Show(); moreButton.IsLoading = true; + pinnedContent.Clear(); content.Clear(); CommentDictionary.Clear(); } @@ -202,7 +223,7 @@ namespace osu.Game.Overlays.Comments var topLevelComments = new List(); var orphaned = new List(); - foreach (var comment in bundle.Comments.Concat(bundle.IncludedComments)) + foreach (var comment in bundle.Comments.Concat(bundle.IncludedComments).Concat(bundle.PinnedComments)) { // Exclude possible duplicated comments. if (CommentDictionary.ContainsKey(comment.Id)) @@ -219,13 +240,15 @@ namespace osu.Game.Overlays.Comments { LoadComponentsAsync(topLevelComments, loaded => { - content.AddRange(loaded); + pinnedContent.AddRange(loaded.Where(d => d.Comment.Pinned)); + content.AddRange(loaded.Where(d => !d.Comment.Pinned)); deletedCommentsCounter.Count.Value += topLevelComments.Select(d => d.Comment).Count(c => c.IsDeleted && c.IsTopLevel); if (bundle.HasMore) { int loadedTopLevelComments = 0; + pinnedContent.Children.OfType().ForEach(p => loadedTopLevelComments++); content.Children.OfType().ForEach(p => loadedTopLevelComments++); moreButton.Current.Value = bundle.TopLevelCount - loadedTopLevelComments; From 1fcb1cdb108fb4049a8aa3f0d0de78408e50e187 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Aug 2021 22:01:47 +0900 Subject: [PATCH 1129/2442] Add todo --- osu.Game/Online/Rooms/Room.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index 364336783d..d964060f10 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -134,7 +134,7 @@ namespace osu.Game.Online.Rooms /// The position of this in the list. This is not read from or written to the API. /// [JsonIgnore] - public readonly Bindable Position = new Bindable(-1); + public readonly Bindable Position = new Bindable(-1); // Todo: This does not need to exist. public Room() { From 155e9e16a5d08d2bcb52dfaeda81af6903e478f2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Aug 2021 22:08:34 +0900 Subject: [PATCH 1130/2442] Refactorings --- osu.Game/Screens/OnlinePlay/IRoomManager.cs | 12 ++++++++++++ .../Multiplayer/MultiplayerLoungeSubScreen.cs | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/IRoomManager.cs b/osu.Game/Screens/OnlinePlay/IRoomManager.cs index baf84e25f9..6e1ffbda74 100644 --- a/osu.Game/Screens/OnlinePlay/IRoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/IRoomManager.cs @@ -23,10 +23,22 @@ namespace osu.Game.Screens.OnlinePlay /// IBindableList Rooms { get; } + /// + /// Adds a to this . + /// If already existing, the local room will be updated with the given one. + /// + /// The incoming . void AddOrUpdateRoom(Room room); + /// + /// Removes a from this . + /// + /// The to remove. void RemoveRoom(Room room); + /// + /// Removes all s from this . + /// void ClearRooms(); /// diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs index 0756c78211..77db955f0a 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs @@ -43,7 +43,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer base.OnResuming(last); // Upon having left a room, we don't know whether we were the only participant, and whether the room is now closed as a result of leaving it. - // To work around this, temporarily clear all rooms until the next listing poll. + // To work around this, temporarily remove the room and trigger an immediate listing poll. if (last is MultiplayerMatchSubScreen match) { RoomManager.RemoveRoom(match.Room); From 0e66a0596340722b6175438a28f3682825b38d46 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Aug 2021 22:29:22 +0900 Subject: [PATCH 1131/2442] Hide left border of beatmap wedge --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 3 ++- osu.Game/Screens/Select/SongSelect.cs | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 3779523094..5b4e077100 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -35,6 +35,7 @@ namespace osu.Game.Screens.Select { public class BeatmapInfoWedge : VisibilityContainer { + public const float BORDER_THICKNESS = 2.5f; private const float shear_width = 36.75f; private static readonly Vector2 wedged_container_shear = new Vector2(shear_width / SongSelect.WEDGE_HEIGHT, 0); @@ -59,7 +60,7 @@ namespace osu.Game.Screens.Select Shear = wedged_container_shear; Masking = true; BorderColour = new Color4(221, 255, 255, 255); - BorderThickness = 2.5f; + BorderThickness = BORDER_THICKNESS; Alpha = 0; EdgeEffect = new EdgeEffectParameters { diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 0e113a71bc..bb3df0d4e0 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -203,6 +203,7 @@ namespace osu.Game.Screens.Select Margin = new MarginPadding { Right = left_area_padding, + Left = -BeatmapInfoWedge.BORDER_THICKNESS, // Hide the left border }, }, new Container From 6d726bf0465233ba78665fa3a5a888eeca833301 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 13 Aug 2021 22:54:41 +0700 Subject: [PATCH 1132/2442] Change spacing Co-authored-by: Salman Ahmed --- osu.Game/Overlays/Comments/DrawableComment.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Comments/DrawableComment.cs b/osu.Game/Overlays/Comments/DrawableComment.cs index 389e61bc30..9937623418 100644 --- a/osu.Game/Overlays/Comments/DrawableComment.cs +++ b/osu.Game/Overlays/Comments/DrawableComment.cs @@ -148,7 +148,7 @@ namespace osu.Game.Overlays.Comments { AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - Spacing = new Vector2(3, 0), + Spacing = new Vector2(2, 0), Alpha = Comment.Pinned ? 1 : 0, Children = new Drawable[] { From 1c32993fe51c56876ce0797d8a5151af471b87c3 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 13 Aug 2021 23:03:42 +0700 Subject: [PATCH 1133/2442] initialize pinned comments in test offline comment --- .../Visual/Online/TestSceneOfflineCommentsContainer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneOfflineCommentsContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneOfflineCommentsContainer.cs index 628ae0971b..4f7947b69c 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneOfflineCommentsContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneOfflineCommentsContainer.cs @@ -149,6 +149,7 @@ namespace osu.Game.Tests.Visual.Online } }, IncludedComments = new List(), + PinnedComments = new List(), UserVotes = new List { 5 @@ -178,6 +179,7 @@ namespace osu.Game.Tests.Visual.Online }, }, IncludedComments = new List(), + PinnedComments = new List(), }; private class TestCommentsContainer : CommentsContainer From 48b84db7b978337329083af3c7e9fdd73012c318 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 13 Aug 2021 23:48:14 +0700 Subject: [PATCH 1134/2442] add no comment and single comment test --- .../Online/TestSceneCommentsContainer.cs | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs index 3ad3474fc0..4809a737d9 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs @@ -82,6 +82,48 @@ namespace osu.Game.Tests.Visual.Online () => commentsContainer.ChildrenOfType().Count() == topLevelCommentCount); } + [Test] + public void TestNoComment() + { + var comments = getExampleComments(); + comments.Comments.Clear(); + + setUpCommentsResponse(comments); + AddStep("show comments", () => commentsContainer.ShowComments(CommentableType.Beatmapset, 123)); + AddAssert("no comment showed", () => !commentsContainer.ChildrenOfType().Any()); + } + + [TestCase(false)] + [TestCase(true)] + public void TestSingleComment(bool withPinned) + { + var comment = new Comment + { + Id = 1, + Message = "This is a single comment", + LegacyName = "SingleUser", + CreatedAt = DateTimeOffset.Now, + VotesCount = 0, + Pinned = withPinned, + }; + + var bundle = new CommentBundle + { + Comments = new List { comment }, + IncludedComments = new List(), + PinnedComments = new List(), + }; + + if (withPinned) + bundle.PinnedComments.Add(comment); + + setUpCommentsResponse(bundle); + AddStep("show comments", () => commentsContainer.ShowComments(CommentableType.Beatmapset, 123)); + AddUntilStep("wait comment load", () => commentsContainer.ChildrenOfType().Any()); + AddAssert("only one comment showed", () => + commentsContainer.ChildrenOfType().Count(d => d.Comment.Pinned == withPinned) == 1); + } + private void setUpCommentsResponse(CommentBundle commentBundle) => AddStep("set up response", () => { From a710bbf6aec07f404d149a686552694b54aa227e Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sat, 14 Aug 2021 00:03:05 +0700 Subject: [PATCH 1135/2442] remove background colour for no comment placeholder --- osu.Game/Overlays/Comments/CommentsContainer.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game/Overlays/Comments/CommentsContainer.cs b/osu.Game/Overlays/Comments/CommentsContainer.cs index 9abae63f93..6b3d816e84 100644 --- a/osu.Game/Overlays/Comments/CommentsContainer.cs +++ b/osu.Game/Overlays/Comments/CommentsContainer.cs @@ -323,11 +323,6 @@ namespace osu.Game.Overlays.Comments RelativeSizeAxes = Axes.X; AddRangeInternal(new Drawable[] { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background4 - }, new OsuSpriteText { Anchor = Anchor.CentreLeft, From 4494ff55e325f96227e8c278d4ac4d759edc1833 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sat, 14 Aug 2021 00:17:45 +0700 Subject: [PATCH 1136/2442] fix pinned drawable comment test --- osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs b/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs index 5d1f3affc6..b26850feb2 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDrawableComment.cs @@ -43,9 +43,7 @@ namespace osu.Game.Tests.Visual.Online { AddStep(description, () => { - if (description == "Pinned") - comment.Pinned = true; - + comment.Pinned = description == "Pinned"; comment.Message = text; container.Add(new DrawableComment(comment)); }); From 9233f396aa0787ce9843fe3fcfad3dfe6dd8760b Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sat, 14 Aug 2021 00:22:01 +0700 Subject: [PATCH 1137/2442] centered thumbtack icon and text --- osu.Game/Overlays/Comments/DrawableComment.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Overlays/Comments/DrawableComment.cs b/osu.Game/Overlays/Comments/DrawableComment.cs index 9937623418..d80d566f3a 100644 --- a/osu.Game/Overlays/Comments/DrawableComment.cs +++ b/osu.Game/Overlays/Comments/DrawableComment.cs @@ -156,11 +156,15 @@ namespace osu.Game.Overlays.Comments { Icon = FontAwesome.Solid.Thumbtack, Size = new Vector2(14), + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, }, new OsuSpriteText { Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold), Text = CommentsStrings.Pinned, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, } }, }, From 2e9ab78275353624a45fcfcb58347f160fdeea42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 13 Aug 2021 21:30:26 +0200 Subject: [PATCH 1138/2442] Split test scene into two to follow class split --- .../TestSceneMultiplayerResults.cs | 51 +++++++++++++++++++ .../TestSceneMultiplayerTeamResults.cs | 37 +------------- 2 files changed, 52 insertions(+), 36 deletions(-) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerResults.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerResults.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerResults.cs new file mode 100644 index 0000000000..ff06d4d9c7 --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerResults.cs @@ -0,0 +1,51 @@ +// 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 NUnit.Framework; +using osu.Game.Online.Rooms; +using osu.Game.Rulesets.Osu; +using osu.Game.Scoring; +using osu.Game.Screens.OnlinePlay.Multiplayer; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneMultiplayerResults : ScreenTestScene + { + [Test] + public void TestDisplayResults() + { + MultiplayerResultsScreen screen = null; + + AddStep("show results screen", () => + { + var rulesetInfo = new OsuRuleset().RulesetInfo; + var beatmapInfo = CreateBeatmap(rulesetInfo).BeatmapInfo; + + var score = new ScoreInfo + { + Rank = ScoreRank.B, + TotalScore = 987654, + Accuracy = 0.8, + MaxCombo = 500, + Combo = 250, + Beatmap = beatmapInfo, + User = new User { Username = "Test user" }, + Date = DateTimeOffset.Now, + OnlineScoreID = 12345, + Ruleset = rulesetInfo, + }; + + PlaylistItem playlistItem = new PlaylistItem + { + BeatmapID = beatmapInfo.ID, + }; + + Stack.Push(screen = new MultiplayerResultsScreen(score, 1, playlistItem)); + }); + + AddUntilStep("wait for loaded", () => screen.IsLoaded); + } + } +} diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs index a21996383f..0a8bda7ec0 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs @@ -18,7 +18,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [TestCase(7483253, 1048576)] [TestCase(1048576, 7483253)] [TestCase(1048576, 1048576)] - public void TestDisplayWithTeams(int team1Score, int team2Score) + public void TestDisplayTeamResults(int team1Score, int team2Score) { MultiplayerResultsScreen screen = null; @@ -57,40 +57,5 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for loaded", () => screen.IsLoaded); } - - [Test] - public void TestDisplayWithoutTeams() - { - MultiplayerResultsScreen screen = null; - - AddStep("show results screen", () => - { - var rulesetInfo = new OsuRuleset().RulesetInfo; - var beatmapInfo = CreateBeatmap(rulesetInfo).BeatmapInfo; - - var score = new ScoreInfo - { - Rank = ScoreRank.B, - TotalScore = 987654, - Accuracy = 0.8, - MaxCombo = 500, - Combo = 250, - Beatmap = beatmapInfo, - User = new User { Username = "Test user" }, - Date = DateTimeOffset.Now, - OnlineScoreID = 12345, - Ruleset = rulesetInfo, - }; - - PlaylistItem playlistItem = new PlaylistItem - { - BeatmapID = beatmapInfo.ID, - }; - - Stack.Push(screen = new MultiplayerResultsScreen(score, 1, playlistItem)); - }); - - AddUntilStep("wait for loaded", () => screen.IsLoaded); - } } } From 00317c0e30b4896f5086ec1d202013799e174cbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 13 Aug 2021 23:44:07 +0200 Subject: [PATCH 1139/2442] Round when totalling up team scores instead of truncating Matches score handling in `ScoreManager`. --- osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 3f9258930e..362ee4a373 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -184,7 +184,7 @@ namespace osu.Game.Screens.Play.HUD continue; if (TeamScores.TryGetValue(u.Team.Value, out var team)) - team.Value += (int)u.Score.Value; + team.Value += (int)Math.Round(u.Score.Value); } } From e2cc96097f1d53591bb8656bd4da2175b150d76d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 13 Aug 2021 23:48:03 +0200 Subject: [PATCH 1140/2442] Unify match score display formatting Commas are already applied on the multiplayer leaderboard, as well as the results screen. --- osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs index 607d08b8ed..68e3f0df7d 100644 --- a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs +++ b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs @@ -7,6 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -171,6 +172,8 @@ namespace osu.Game.Screens.Play.HUD => displayedSpriteText.Font = winning ? OsuFont.Torus.With(weight: FontWeight.Bold, size: font_size, fixedWidth: true) : OsuFont.Torus.With(weight: FontWeight.Regular, size: font_size * 0.8f, fixedWidth: true); + + protected override LocalisableString FormatCount(double count) => count.ToLocalisableString(@"N0"); } } } From 5cfb89f18ac02d26168569846255e73703dde9d2 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sat, 14 Aug 2021 10:56:52 +0800 Subject: [PATCH 1141/2442] Apply suggestions from code review Co-authored-by: Joseph Madamba --- osu.Game/Localisation/UserInterfaceStrings.cs | 2 +- .../Settings/Sections/UserInterface/SongSelectSettings.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Localisation/UserInterfaceStrings.cs b/osu.Game/Localisation/UserInterfaceStrings.cs index 18a9257732..fcfaf661dc 100644 --- a/osu.Game/Localisation/UserInterfaceStrings.cs +++ b/osu.Game/Localisation/UserInterfaceStrings.cs @@ -77,7 +77,7 @@ namespace osu.Game.Localisation /// /// "Song Select" /// - public static LocalisableString SoneSelectHeader => new TranslatableString(getKey(@"song_select_header"), @"Song Select"); + public static LocalisableString SongSelectHeader => new TranslatableString(getKey(@"song_select_header"), @"Song Select"); /// /// "Right mouse drag to absolute scroll" diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs index 8596cecbb8..6290046987 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface private Bindable minStars; private Bindable maxStars; - protected override LocalisableString Header => UserInterfaceStrings.SoneSelectHeader; + protected override LocalisableString Header => UserInterfaceStrings.SongSelectHeader; [BackgroundDependencyLoader] private void load(OsuConfigManager config) From e26ccf786e6812a8aca382f061e346f3b5b64a76 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sat, 14 Aug 2021 11:04:38 +0800 Subject: [PATCH 1142/2442] code style format --- osu.Game/Localisation/OnlineSettingsStrings.cs | 4 ++-- osu.Game/Localisation/SkinSettingsStrings.cs | 2 +- .../Settings/Sections/Online/AlertsAndPrivacySettings.cs | 2 +- osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs | 2 +- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Localisation/OnlineSettingsStrings.cs b/osu.Game/Localisation/OnlineSettingsStrings.cs index 0ef98a720c..0173cc7cbb 100644 --- a/osu.Game/Localisation/OnlineSettingsStrings.cs +++ b/osu.Game/Localisation/OnlineSettingsStrings.cs @@ -27,7 +27,7 @@ namespace osu.Game.Localisation /// /// "Show a notification when you receive a private message" /// - public static LocalisableString NotifyOnPM => new TranslatableString(getKey(@"notify_on_pm"), @"Show a notification when you receive a private message"); + public static LocalisableString NotifyOnPrivateMessage => new TranslatableString(getKey(@"notify_on_private_message"), @"Show a notification when you receive a private message"); /// /// "Integrations" @@ -57,7 +57,7 @@ namespace osu.Game.Localisation /// /// "Automatically download beatmaps when spectating" /// - public static LocalisableString AutomaticallyDownload => new TranslatableString(getKey(@"automatically_download"), @"Automatically download beatmaps when spectating"); + public static LocalisableString AutomaticallyDownloadWhenSpectating => new TranslatableString(getKey(@"automatically_download_when_spectating"), @"Automatically download beatmaps when spectating"); /// /// "Show explicit content in search results" diff --git a/osu.Game/Localisation/SkinSettingsStrings.cs b/osu.Game/Localisation/SkinSettingsStrings.cs index 848c01b93a..f22b4d6bf5 100644 --- a/osu.Game/Localisation/SkinSettingsStrings.cs +++ b/osu.Game/Localisation/SkinSettingsStrings.cs @@ -22,7 +22,7 @@ namespace osu.Game.Localisation /// /// "Gameplay cursor size" /// - public static LocalisableString CursorSize => new TranslatableString(getKey(@"cursor_size"), @"Gameplay cursor size"); + public static LocalisableString GameplayCursorSize => new TranslatableString(getKey(@"gameplay_cursor_size"), @"Gameplay cursor size"); /// /// "Adjust gameplay cursor size based on current beatmap" diff --git a/osu.Game/Overlays/Settings/Sections/Online/AlertsAndPrivacySettings.cs b/osu.Game/Overlays/Settings/Sections/Online/AlertsAndPrivacySettings.cs index 1728b565c2..351a32c72e 100644 --- a/osu.Game/Overlays/Settings/Sections/Online/AlertsAndPrivacySettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Online/AlertsAndPrivacySettings.cs @@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Settings.Sections.Online }, new SettingsCheckbox { - LabelText = OnlineSettingsStrings.NotifyOnPM, + LabelText = OnlineSettingsStrings.NotifyOnPrivateMessage, Current = config.GetBindable(OsuSetting.NotifyOnPrivateMessage) }, }; diff --git a/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs b/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs index 4200ddda3d..e864260cc6 100644 --- a/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs @@ -31,7 +31,7 @@ namespace osu.Game.Overlays.Settings.Sections.Online }, new SettingsCheckbox { - LabelText = OnlineSettingsStrings.AutomaticallyDownload, + LabelText = OnlineSettingsStrings.AutomaticallyDownloadWhenSpectating, Keywords = new[] { "spectator" }, Current = config.GetBindable(OsuSetting.AutomaticallyDownloadWhenSpectating), }, diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 007302c584..e0d8252930 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -76,7 +76,7 @@ namespace osu.Game.Overlays.Settings.Sections new ExportSkinButton(), new SettingsSlider { - LabelText = SkinSettingsStrings.CursorSize, + LabelText = SkinSettingsStrings.GameplayCursorSize, Current = config.GetBindable(OsuSetting.GameplayCursorSize), KeyboardStep = 0.01f }, From e6b3aba6e19557e2f2d493fb6d5c57a9c71d3b23 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 14 Aug 2021 14:08:29 +0900 Subject: [PATCH 1143/2442] Fix incorrectly directed call in `TestMultiplayerClient` --- osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index a28b4140ca..67b79d7390 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -178,7 +178,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Debug.Assert(Room != null); - return ((IMultiplayerClient)this).UserLeft(Room.Users.Single(u => u.UserID == userId)); + return ((IMultiplayerClient)this).UserKicked(Room.Users.Single(u => u.UserID == userId)); } public override async Task ChangeSettings(MultiplayerRoomSettings settings) From a553942a7ff141ecf4aee4a75bcbc5e58f80816e Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Sat, 14 Aug 2021 13:20:36 +0800 Subject: [PATCH 1144/2442] update `InitialActivity` on multiplayer `Player` and `SongSelect` --- .../OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs | 6 ++++++ .../Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs | 3 +++ osu.Game/Users/UserActivity.cs | 9 +++++++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 3733b85a5e..7efcec50db 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -11,6 +11,7 @@ using osu.Game.Online.Rooms; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Select; +using osu.Game.Users; namespace osu.Game.Screens.OnlinePlay.Multiplayer { @@ -19,8 +20,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private MultiplayerClient client { get; set; } + [Resolved] + private Room room { get; set; } + private LoadingLayer loadingLayer; + protected override UserActivity InitialActivity => new UserActivity.InLobby(room); + /// /// Construct a new instance of multiplayer song select. /// diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index ca1a3710ab..7e9d0a423f 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -17,6 +17,7 @@ using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Ranking; +using osu.Game.Users; using osuTK; namespace osu.Game.Screens.OnlinePlay.Multiplayer @@ -25,6 +26,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { protected override bool PauseOnFocusLost => false; + protected override UserActivity InitialActivity => new UserActivity.MultiplayerGame(Beatmap.Value.BeatmapInfo, Ruleset.Value); + // Disallow fails in multiplayer for now. protected override bool CheckModsAllowFailure() => false; diff --git a/osu.Game/Users/UserActivity.cs b/osu.Game/Users/UserActivity.cs index f633773d11..17c028af7d 100644 --- a/osu.Game/Users/UserActivity.cs +++ b/osu.Game/Users/UserActivity.cs @@ -25,9 +25,14 @@ namespace osu.Game.Users public override string Status => "Choosing a beatmap"; } - public class MultiplayerGame : UserActivity + public class MultiplayerGame : SoloGame { - public override string Status => "Playing with others"; + public MultiplayerGame(BeatmapInfo beatmap, RulesetInfo ruleset) + : base(beatmap, ruleset) + { + } + + public override string Status => $@"{base.Status} with others"; } public class Editing : UserActivity From f87f86e671190c2836b95b801fee7be50aa52b1a Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sat, 14 Aug 2021 21:52:09 +0800 Subject: [PATCH 1145/2442] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Localisation/GameplaySettingsStrings.cs | 2 +- osu.Game/Localisation/OnlineSettingsStrings.cs | 2 +- osu.Game/Localisation/UserInterfaceStrings.cs | 2 +- .../Overlays/Settings/Sections/Gameplay/GeneralSettings.cs | 2 +- .../Overlays/Settings/Sections/Graphics/LayoutSettings.cs | 2 +- .../Overlays/Settings/Sections/Graphics/RendererSettings.cs | 2 +- .../Overlays/Settings/Sections/Online/IntegrationSettings.cs | 2 +- .../Settings/Sections/UserInterface/GeneralSettings.cs | 2 +- .../Settings/Sections/UserInterface/MainMenuSettings.cs | 2 +- osu.Game/Overlays/Settings/SettingsSubsection.cs | 5 +++++ 10 files changed, 14 insertions(+), 9 deletions(-) diff --git a/osu.Game/Localisation/GameplaySettingsStrings.cs b/osu.Game/Localisation/GameplaySettingsStrings.cs index c63fa5ddcc..868eb6b98a 100644 --- a/osu.Game/Localisation/GameplaySettingsStrings.cs +++ b/osu.Game/Localisation/GameplaySettingsStrings.cs @@ -42,7 +42,7 @@ namespace osu.Game.Localisation /// /// "Show difficulty graph on progress bar" /// - public static LocalisableString ShowProgressGraph => new TranslatableString(getKey(@"show_progress_graph"), @"Show difficulty graph on progress bar"); + public static LocalisableString ShowDifficultyGraph => new TranslatableString(getKey(@"show_progress_graph"), @"Show difficulty graph on progress bar"); /// /// "Show health display even when you can't fail" diff --git a/osu.Game/Localisation/OnlineSettingsStrings.cs b/osu.Game/Localisation/OnlineSettingsStrings.cs index 0173cc7cbb..6862f4ac2c 100644 --- a/osu.Game/Localisation/OnlineSettingsStrings.cs +++ b/osu.Game/Localisation/OnlineSettingsStrings.cs @@ -32,7 +32,7 @@ namespace osu.Game.Localisation /// /// "Integrations" /// - public static LocalisableString IntegrationHeader => new TranslatableString(getKey(@"integration_header"), @"Integrations"); + public static LocalisableString IntegrationsHeader => new TranslatableString(getKey(@"integrations_header"), @"Integrations"); /// /// "Discord Rich Presence" diff --git a/osu.Game/Localisation/UserInterfaceStrings.cs b/osu.Game/Localisation/UserInterfaceStrings.cs index fcfaf661dc..4be403edb4 100644 --- a/osu.Game/Localisation/UserInterfaceStrings.cs +++ b/osu.Game/Localisation/UserInterfaceStrings.cs @@ -37,7 +37,7 @@ namespace osu.Game.Localisation /// /// "Hold-to-confirm activation time" /// - public static LocalisableString HoldActivationDelay => new TranslatableString(getKey(@"hold_activation_delay"), @"Hold-to-confirm activation time"); + public static LocalisableString HoldToConfirmActivationTime => new TranslatableString(getKey(@"hold_to_confirm_activation_time"), @"Hold-to-confirm activation time"); /// /// "Main Menu" diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index 04332fcdd3..3a0265e453 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -46,7 +46,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay }, new SettingsCheckbox { - LabelText = GameplaySettingsStrings.ShowProgressGraph, + LabelText = GameplaySettingsStrings.ShowDifficultyGraph, Current = config.GetBindable(OsuSetting.ShowProgressGraph) }, new SettingsCheckbox diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 50bd8184ee..124b3b804c 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -146,7 +146,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { updateResolutionDropdown(); - windowModeDropdown.WarningText = mode.NewValue != WindowMode.Fullscreen ? GraphicsSettingsStrings.NotFullscreenNote : string.Empty; + windowModeDropdown.WarningText = mode.NewValue != WindowMode.Fullscreen ? GraphicsSettingsStrings.NotFullscreenNote : default; }, true); windowModes.BindCollectionChanged((sender, args) => diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index 9747f6b373..653f30a018 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -48,7 +48,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics frameLimiterDropdown.Current.BindValueChanged(limit => { - frameLimiterDropdown.WarningText = limit.NewValue == FrameSync.Unlimited ? GraphicsSettingsStrings.UnlimitedFramesNote : string.Empty; + frameLimiterDropdown.WarningText = limit.NewValue == FrameSync.Unlimited ? GraphicsSettingsStrings.UnlimitedFramesNote : default; }, true); } } diff --git a/osu.Game/Overlays/Settings/Sections/Online/IntegrationSettings.cs b/osu.Game/Overlays/Settings/Sections/Online/IntegrationSettings.cs index f2a516feef..0207f2fd01 100644 --- a/osu.Game/Overlays/Settings/Sections/Online/IntegrationSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Online/IntegrationSettings.cs @@ -11,7 +11,7 @@ namespace osu.Game.Overlays.Settings.Sections.Online { public class IntegrationSettings : SettingsSubsection { - protected override LocalisableString Header => OnlineSettingsStrings.IntegrationHeader; + protected override LocalisableString Header => OnlineSettingsStrings.IntegrationsHeader; [BackgroundDependencyLoader] private void load(OsuConfigManager config) diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs index b26363a9da..0afbed5df5 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/GeneralSettings.cs @@ -37,7 +37,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface }, new SettingsSlider { - LabelText = UserInterfaceStrings.HoldActivationDelay, + LabelText = UserInterfaceStrings.HoldToConfirmActivationTime, Current = config.GetBindable(OsuSetting.UIHoldActivationDelay), KeyboardStep = 50 }, diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs index 976893bf0a..40485a070c 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs @@ -61,7 +61,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface user.BindValueChanged(u => { - backgroundSourceDropdown.WarningText = u.NewValue?.IsSupporter != true ? UserInterfaceStrings.NotSupporterNote : string.Empty; + backgroundSourceDropdown.WarningText = u.NewValue?.IsSupporter != true ? UserInterfaceStrings.NotSupporterNote : default; }, true); } } diff --git a/osu.Game/Overlays/Settings/SettingsSubsection.cs b/osu.Game/Overlays/Settings/SettingsSubsection.cs index df32424b67..4aa9360452 100644 --- a/osu.Game/Overlays/Settings/SettingsSubsection.cs +++ b/osu.Game/Overlays/Settings/SettingsSubsection.cs @@ -24,6 +24,11 @@ namespace osu.Game.Overlays.Settings protected abstract LocalisableString Header { get; } public IEnumerable FilterableChildren => Children.OfType(); + + // FilterTerms should contains both original string and localised string for user to search. + // Since LocalisableString is unable to get original string at this time (2021-08-14), + // only call .ToString() to use localised one. + // TODO: Update here when FilterTerms accept LocalisableString. public virtual IEnumerable FilterTerms => new[] { Header.ToString() }; public bool MatchingFilter From c2bbe175627ce35f1d63bc7c627b35b4883de5a0 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Sat, 14 Aug 2021 22:35:15 +0800 Subject: [PATCH 1146/2442] Rename element in OsuSettings enum `ShowProgressGraph` -> `ShowDifficultyGraph` --- osu.Game/Configuration/OsuConfigManager.cs | 4 ++-- .../Overlays/Settings/Sections/Gameplay/GeneralSettings.cs | 2 +- osu.Game/Screens/Play/SongProgress.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 60a0d5a0ac..6c7adcc806 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -101,7 +101,7 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.HitLighting, true); SetDefault(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always); - SetDefault(OsuSetting.ShowProgressGraph, true); + SetDefault(OsuSetting.ShowDifficultyGraph, true); SetDefault(OsuSetting.ShowHealthDisplayWhenCantFail, true); SetDefault(OsuSetting.FadePlayfieldWhenHealthLow, true); SetDefault(OsuSetting.KeyOverlay, false); @@ -217,7 +217,7 @@ namespace osu.Game.Configuration AlwaysPlayFirstComboBreak, FloatingComments, HUDVisibilityMode, - ShowProgressGraph, + ShowDifficultyGraph, ShowHealthDisplayWhenCantFail, FadePlayfieldWhenHealthLow, MouseDisableButtons, diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index 353292606f..69aa57082a 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -46,7 +46,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay new SettingsCheckbox { LabelText = "Show difficulty graph on progress bar", - Current = config.GetBindable(OsuSetting.ShowProgressGraph) + Current = config.GetBindable(OsuSetting.ShowDifficultyGraph) }, new SettingsCheckbox { diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index f28622f42e..6aa7e017ce 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -125,7 +125,7 @@ namespace osu.Game.Screens.Play Objects = drawableRuleset.Objects; } - config.BindWith(OsuSetting.ShowProgressGraph, ShowGraph); + config.BindWith(OsuSetting.ShowDifficultyGraph, ShowGraph); graph.FillColour = bar.FillColour = colours.BlueLighter; } From 4ed06a1021a6b38b49fccd47d0d94b28d92c4227 Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Sat, 14 Aug 2021 22:39:12 +0800 Subject: [PATCH 1147/2442] apply suggestions --- osu.Game/Rulesets/Ruleset.cs | 2 +- .../OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs | 8 +------- .../Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs | 3 --- osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs | 6 ++++++ osu.Game/Screens/Play/RoomSubmittingPlayer.cs | 3 +++ osu.Game/Users/UserActivity.cs | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 9fdaca88fd..98eb374a0d 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -226,7 +226,7 @@ namespace osu.Game.Rulesets /// /// The playing verb to be shown in the . /// - public virtual string PlayingVerb => "Playing solo"; + public virtual string PlayingVerb => "Playing"; /// /// A list of available variant ids. diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 7efcec50db..077ab45b50 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -11,22 +11,16 @@ using osu.Game.Online.Rooms; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Select; -using osu.Game.Users; namespace osu.Game.Screens.OnlinePlay.Multiplayer { public class MultiplayerMatchSongSelect : OnlinePlaySongSelect { [Resolved] - private MultiplayerClient client { get; set; } - - [Resolved] - private Room room { get; set; } + private MultiplayerClient client { get; set; } private LoadingLayer loadingLayer; - protected override UserActivity InitialActivity => new UserActivity.InLobby(room); - /// /// Construct a new instance of multiplayer song select. /// diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index 7e9d0a423f..ca1a3710ab 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -17,7 +17,6 @@ using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Ranking; -using osu.Game.Users; using osuTK; namespace osu.Game.Screens.OnlinePlay.Multiplayer @@ -26,8 +25,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { protected override bool PauseOnFocusLost => false; - protected override UserActivity InitialActivity => new UserActivity.MultiplayerGame(Beatmap.Value.BeatmapInfo, Ruleset.Value); - // Disallow fails in multiplayer for now. protected override bool CheckModsAllowFailure() => false; diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index 1502463022..de1b990573 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -17,6 +17,7 @@ using osu.Game.Overlays.Mods; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Select; +using osu.Game.Users; using osu.Game.Utils; namespace osu.Game.Screens.OnlinePlay @@ -32,6 +33,11 @@ namespace osu.Game.Screens.OnlinePlay [Resolved(typeof(Room), nameof(Room.Playlist))] protected BindableList Playlist { get; private set; } + [Resolved] + private Room room { get; set; } + + protected override UserActivity InitialActivity => new UserActivity.InLobby(room); + protected readonly Bindable> FreeMods = new Bindable>(Array.Empty()); [CanBeNull] diff --git a/osu.Game/Screens/Play/RoomSubmittingPlayer.cs b/osu.Game/Screens/Play/RoomSubmittingPlayer.cs index 7ba12f5db6..b6f5fe0205 100644 --- a/osu.Game/Screens/Play/RoomSubmittingPlayer.cs +++ b/osu.Game/Screens/Play/RoomSubmittingPlayer.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osu.Game.Online.API; using osu.Game.Online.Rooms; using osu.Game.Scoring; +using osu.Game.Users; namespace osu.Game.Screens.Play { @@ -19,6 +20,8 @@ namespace osu.Game.Screens.Play protected readonly PlaylistItem PlaylistItem; + protected override UserActivity InitialActivity => new UserActivity.MultiplayerGame(Beatmap.Value.BeatmapInfo, Ruleset.Value); + protected RoomSubmittingPlayer(PlaylistItem playlistItem, PlayerConfiguration configuration = null) : base(configuration) { diff --git a/osu.Game/Users/UserActivity.cs b/osu.Game/Users/UserActivity.cs index 17c028af7d..f48c714a30 100644 --- a/osu.Game/Users/UserActivity.cs +++ b/osu.Game/Users/UserActivity.cs @@ -74,7 +74,7 @@ namespace osu.Game.Users public class InLobby : UserActivity { - public override string Status => @"In a multiplayer lobby"; + public override string Status => @"In a lobby"; public readonly Room Room; From 498462dfd0751fbbe480161d4c66487e4f58cc39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 14 Aug 2021 17:24:14 +0200 Subject: [PATCH 1148/2442] Fix room null-check racing against async schedule --- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 57dbfbb507..2a0635c98c 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -190,8 +190,7 @@ namespace osu.Game.Online.Multiplayer return joinOrLeaveTaskChain.Add(async () => { await scheduledReset.ConfigureAwait(false); - if (Room != null) - await LeaveRoomInternal().ConfigureAwait(false); + await LeaveRoomInternal().ConfigureAwait(false); }); } From 246a8882ce248f35ba238875db35d281473e12f6 Mon Sep 17 00:00:00 2001 From: LiangXiang Shen Date: Sun, 15 Aug 2021 00:23:14 +0800 Subject: [PATCH 1149/2442] Update translation key --- osu.Game/Localisation/GameplaySettingsStrings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Localisation/GameplaySettingsStrings.cs b/osu.Game/Localisation/GameplaySettingsStrings.cs index 868eb6b98a..6d6381b429 100644 --- a/osu.Game/Localisation/GameplaySettingsStrings.cs +++ b/osu.Game/Localisation/GameplaySettingsStrings.cs @@ -42,7 +42,7 @@ namespace osu.Game.Localisation /// /// "Show difficulty graph on progress bar" /// - public static LocalisableString ShowDifficultyGraph => new TranslatableString(getKey(@"show_progress_graph"), @"Show difficulty graph on progress bar"); + public static LocalisableString ShowDifficultyGraph => new TranslatableString(getKey(@"show_difficulty_graph"), @"Show difficulty graph on progress bar"); /// /// "Show health display even when you can't fail" From 2ddf28346aa2873bef9cb9edf313f8fe0b52e721 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 14 Aug 2021 19:58:20 +0300 Subject: [PATCH 1150/2442] PlayerSettingsGroups -> PlayerSettings --- .../Multiplayer/Spectate/MultiSpectatorPlayerLoader.cs | 2 +- osu.Game/Screens/Play/PlayerLoader.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayerLoader.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayerLoader.cs index f28c2a1d48..14bd8fa6dc 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayerLoader.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayerLoader.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate [BackgroundDependencyLoader] private void load() { - PlayerSettingsGroups.Expire(); + PlayerSettings.Expire(); } protected override void LogoArriving(OsuLogo logo, bool resuming) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 1f8387ac67..a6592e4d24 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -51,7 +51,7 @@ namespace osu.Game.Screens.Play /// /// A fill flow containing the player settings groups, exposed for the ability to hide it from inheritors of the player loader. /// - protected FillFlowContainer PlayerSettingsGroups; + protected FillFlowContainer PlayerSettings; protected VisualSettings VisualSettings; @@ -145,7 +145,7 @@ namespace osu.Game.Screens.Play Anchor = Anchor.Centre, Origin = Anchor.Centre, }, - PlayerSettingsGroups = new FillFlowContainer + PlayerSettings = new FillFlowContainer { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, From c8fb79666064f6ead2485aeec1757d1407cda909 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 14 Aug 2021 20:14:21 +0300 Subject: [PATCH 1151/2442] Fix settings notice text class tinting everything with yellow --- osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs | 2 +- osu.Game/Overlays/Settings/SettingsItem.cs | 2 +- osu.Game/Overlays/Settings/SettingsNoticeText.cs | 6 ++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index 2816eb7037..c561b693d8 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -91,7 +91,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input Origin = Anchor.TopCentre, Text = TabletSettingsStrings.NoTabletDetected, }, - new SettingsNoticeText + new SettingsNoticeText(colours) { TextAnchor = Anchor.TopCentre, Anchor = Anchor.TopCentre, diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 5d25605360..ef2027fdab 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -73,7 +73,7 @@ namespace osu.Game.Overlays.Settings return; // construct lazily for cases where the label is not needed (may be provided by the Control). - FlowContent.Add(warningText = new SettingsNoticeText { Margin = new MarginPadding { Bottom = 5 } }); + FlowContent.Add(warningText = new SettingsNoticeText(colours) { Margin = new MarginPadding { Bottom = 5 } }); } warningText.Alpha = hasValue ? 0 : 1; diff --git a/osu.Game/Overlays/Settings/SettingsNoticeText.cs b/osu.Game/Overlays/Settings/SettingsNoticeText.cs index 6bb4a3f11c..76ecf7edd4 100644 --- a/osu.Game/Overlays/Settings/SettingsNoticeText.cs +++ b/osu.Game/Overlays/Settings/SettingsNoticeText.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 osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -10,10 +9,9 @@ namespace osu.Game.Overlays.Settings { public class SettingsNoticeText : LinkFlowContainer { - [BackgroundDependencyLoader] - private void load(OsuColour colours) + public SettingsNoticeText(OsuColour colours) + : base(s => s.Colour = colours.Yellow) { - Colour = colours.Yellow; RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; } From 95943fdb25c763a10849f9dff6dffb4fa7466919 Mon Sep 17 00:00:00 2001 From: Jacob Van Meter Date: Sat, 14 Aug 2021 20:00:26 -0400 Subject: [PATCH 1152/2442] Add glow to supporter promo on changelog Added glow to the supporter promo at the end of the changelog, as it is on the website. --- .../Changelog/ChangelogSupporterPromo.cs | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index f617b4fc82..46fab30d5e 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -138,14 +138,28 @@ namespace osu.Game.Overlays.Changelog FillMode = FillMode.Fill, Texture = textures.Get(@"Online/supporter-pippi"), }, - new Sprite + new Container { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Width = 75, Height = 75, - Margin = new MarginPadding { Top = 70 }, - Texture = textures.Get(@"Online/supporter-heart"), + Margin = new MarginPadding { Top = 83 }, + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.HotPink.Opacity(0.88f), + Offset = new Vector2(0, 0), + Radius = 17, + Roundness = 39f, + }, + Child = new Sprite + { + Width = 75, + Height = 75, + Texture = textures.Get(@"Online/supporter-heart"), + }, }, }; } From 772860232cbbfad9ca85c68d413465405828279b Mon Sep 17 00:00:00 2001 From: Jacob Van Meter Date: Sat, 14 Aug 2021 20:32:38 -0400 Subject: [PATCH 1153/2442] Removed empty offset and corrected colour --- osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index 46fab30d5e..203a371f43 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -149,8 +149,7 @@ namespace osu.Game.Overlays.Changelog EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, - Colour = Color4.HotPink.Opacity(0.88f), - Offset = new Vector2(0, 0), + Colour = colour.Pink, Radius = 17, Roundness = 39f, }, From 71ccd38bb35e6276205152edb953daedc0affb34 Mon Sep 17 00:00:00 2001 From: Jacob Van Meter Date: Sat, 14 Aug 2021 20:36:43 -0400 Subject: [PATCH 1154/2442] Corrected pippi background and promo positioning --- osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index 203a371f43..f8613b101e 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -134,6 +134,7 @@ namespace osu.Game.Overlays.Changelog { Anchor = Anchor.Centre, Origin = Anchor.Centre, + Margin = new MarginPadding { Bottom = 28 }, RelativeSizeAxes = Axes.Both, FillMode = FillMode.Fill, Texture = textures.Get(@"Online/supporter-pippi"), @@ -144,7 +145,7 @@ namespace osu.Game.Overlays.Changelog Origin = Anchor.TopCentre, Width = 75, Height = 75, - Margin = new MarginPadding { Top = 83 }, + Margin = new MarginPadding { Top = 70 }, Masking = true, EdgeEffect = new EdgeEffectParameters { From 7d6f7ac75e496c6cff379c7f471e805267e02047 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 15 Aug 2021 02:57:11 +0200 Subject: [PATCH 1155/2442] Fix mark channel as read error --- osu.Game/Online/Chat/ChannelManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 3136a3960d..1937019ef6 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -553,7 +553,7 @@ namespace osu.Game.Online.Chat if (channel.LastMessageId == channel.LastReadId) return; - var message = channel.Messages.LastOrDefault(); + var message = channel.Messages.FindLast(msg => !(msg is LocalMessage)); if (message == null) return; From 6472d85aae3377c7f37bb233aace478cfad8ab06 Mon Sep 17 00:00:00 2001 From: Jacob Van Meter Date: Sat, 14 Aug 2021 21:48:57 -0400 Subject: [PATCH 1156/2442] Added heart_size constant and adjusted the glow radius value Added heart_size constant and adjusted the glow radius value to be more in line with the website --- .../Overlays/Changelog/ChangelogSupporterPromo.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index f8613b101e..c51f927f9f 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -24,6 +24,7 @@ namespace osu.Game.Overlays.Changelog public class ChangelogSupporterPromo : CompositeDrawable { private const float image_container_width = 164; + private const float heart_size = 75; private readonly FillFlowContainer textContainer; private readonly Container imageContainer; @@ -143,21 +144,21 @@ namespace osu.Game.Overlays.Changelog { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Width = 75, - Height = 75, + Width = heart_size, + Height = heart_size, Margin = new MarginPadding { Top = 70 }, Masking = true, EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, Colour = colour.Pink, - Radius = 17, - Roundness = 39f, + Radius = 10, + Roundness = heart_size / 2, }, Child = new Sprite { - Width = 75, - Height = 75, + Width = heart_size, + Height = heart_size, Texture = textures.Get(@"Online/supporter-heart"), }, }, From 2cc096101efabbe8c2e1fa9b16cdc06b06e1e0c9 Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Sun, 15 Aug 2021 18:56:24 +0800 Subject: [PATCH 1157/2442] trim whitespace --- .../OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 077ab45b50..3733b85a5e 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -17,7 +17,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public class MultiplayerMatchSongSelect : OnlinePlaySongSelect { [Resolved] - private MultiplayerClient client { get; set; } + private MultiplayerClient client { get; set; } private LoadingLayer loadingLayer; From d287db796172a213dce4a079e27b22e42a320bc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20Sch=C3=BCrz?= Date: Sun, 15 Aug 2021 14:48:56 +0200 Subject: [PATCH 1158/2442] Clamping seekTime to beatmap length --- osu.Game/Screens/Edit/EditorClock.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs index 772f6ea192..52c95088f3 100644 --- a/osu.Game/Screens/Edit/EditorClock.cs +++ b/osu.Game/Screens/Edit/EditorClock.cs @@ -118,6 +118,7 @@ namespace osu.Game.Screens.Edit if (!snapped || ControlPointInfo.TimingPoints.Count == 0) { + seekTime = Math.Clamp(seekTime, 0, TrackLength); SeekSmoothlyTo(seekTime); return; } From f9e2e808743403cafd862f1fd1177a1bc0f8b4f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 15 Aug 2021 15:04:14 +0200 Subject: [PATCH 1159/2442] Add testing for auto-restart behaviour --- .../Visual/Mods/TestSceneModFailCondition.cs | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 osu.Game.Tests/Visual/Mods/TestSceneModFailCondition.cs diff --git a/osu.Game.Tests/Visual/Mods/TestSceneModFailCondition.cs b/osu.Game.Tests/Visual/Mods/TestSceneModFailCondition.cs new file mode 100644 index 0000000000..af874cec91 --- /dev/null +++ b/osu.Game.Tests/Visual/Mods/TestSceneModFailCondition.cs @@ -0,0 +1,55 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Screens.Play; + +namespace osu.Game.Tests.Visual.Mods +{ + public class TestSceneModFailCondition : ModTestScene + { + private bool restartRequested; + + protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); + + protected override TestPlayer CreateModPlayer(Ruleset ruleset) + { + var player = base.CreateModPlayer(ruleset); + player.RestartRequested = () => restartRequested = true; + return player; + } + + protected override bool AllowFail => true; + + [SetUpSteps] + public void SetUp() + { + AddStep("reset flag", () => restartRequested = false); + } + + [Test] + public void TestRestartOnFailDisabled() => CreateModTest(new ModTestData + { + Autoplay = false, + Mod = new OsuModSuddenDeath(), + PassCondition = () => !restartRequested && Player.ChildrenOfType().Single().State.Value == Visibility.Visible + }); + + [Test] + public void TestRestartOnFailEnabled() => CreateModTest(new ModTestData + { + Autoplay = false, + Mod = new OsuModSuddenDeath + { + Restart = { Value = true } + }, + PassCondition = () => restartRequested && Player.ChildrenOfType().Single().State.Value == Visibility.Hidden + }); + } +} From 2f9f1ba862cf7c54dca3063e0c33dee9fef55c01 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 15 Aug 2021 15:44:23 +0200 Subject: [PATCH 1160/2442] Add test for `ChannelManager.MarkChannelAsRead` --- .../Chat/TestSceneChannelManager.cs | 57 ++++++++++++++++++- .../API/Requests/MarkChannelAsReadRequest.cs | 10 ++-- 2 files changed, 60 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Chat/TestSceneChannelManager.cs b/osu.Game.Tests/Chat/TestSceneChannelManager.cs index 0ec21a4c7b..7b6d2078ca 100644 --- a/osu.Game.Tests/Chat/TestSceneChannelManager.cs +++ b/osu.Game.Tests/Chat/TestSceneChannelManager.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; @@ -19,6 +20,7 @@ namespace osu.Game.Tests.Chat { private ChannelManager channelManager; private int currentMessageId; + List sentMessages; [SetUp] public void Setup() => Schedule(() => @@ -34,6 +36,7 @@ namespace osu.Game.Tests.Chat AddStep("register request handling", () => { currentMessageId = 0; + sentMessages = new List(); ((DummyAPIAccess)API).HandleRequest = req => { @@ -44,7 +47,7 @@ namespace osu.Game.Tests.Chat return true; case PostMessageRequest postMessage: - postMessage.TriggerSuccess(new Message(++currentMessageId) + var message = new Message(++currentMessageId) { IsAction = postMessage.Message.IsAction, ChannelId = postMessage.Message.ChannelId, @@ -52,7 +55,10 @@ namespace osu.Game.Tests.Chat Links = postMessage.Message.Links, Timestamp = postMessage.Message.Timestamp, Sender = postMessage.Message.Sender - }); + }; + + sentMessages.Add(message); + postMessage.TriggerSuccess(message); return true; } @@ -83,12 +89,59 @@ namespace osu.Game.Tests.Chat AddAssert("/np command received by channel 2", () => channel2.Messages.Last().Content.Contains("is listening to")); } + [Test] + public void TestMarkAsReadIgnoringLocalMessages() { + Channel channel = null; + + AddStep("join channel and select it", () => + { + channelManager.JoinChannel(channel = createChannel(1, ChannelType.Public)); + + channelManager.CurrentChannel.Value = channel; + }); + + AddStep("post message", () => channelManager.PostMessage("Something interesting")); + AddUntilStep("wait until the message is posted", () => channel.Messages.Count == sentMessages.Count); + + AddStep("post /help command", () => channelManager.PostCommand("help", channel)); + AddStep("post /me command with no action", () => channelManager.PostCommand("me", channel)); + AddStep("post /join command with no channel", () => channelManager.PostCommand("join", channel)); + AddStep("post /join command with non-existent channel", () => channelManager.PostCommand("join i-dont-exist", channel)); + AddStep("post non-existent command", () => channelManager.PostCommand("non-existent-cmd arg", channel)); + + AddStep("register mark channel as read request handler", () => { + ((DummyAPIAccess)API).HandleRequest = req => + { + switch (req) + { + case MarkChannelAsReadRequest markRead: + var isSentMessage = sentMessages.Contains(markRead.Message); + + AddAssert("mark channel as read called with a real message", () => isSentMessage); + + if(isSentMessage) { + markRead.TriggerSuccess(); + } else { + markRead.TriggerFailure(new APIException("unknown message!", null)); + } + + return true; + } + + return false; + }; + }); + + AddStep("mark channel as read", () => channelManager.MarkChannelAsRead(channel)); + } + private Channel createChannel(int id, ChannelType type) => new Channel(new User()) { Id = id, Name = $"Channel {id}", Topic = $"Topic of channel {id} with type {type}", Type = type, + LastMessageId = 0, }; private class ChannelManagerContainer : CompositeDrawable diff --git a/osu.Game/Online/API/Requests/MarkChannelAsReadRequest.cs b/osu.Game/Online/API/Requests/MarkChannelAsReadRequest.cs index 95a5d0acbd..594ab2accc 100644 --- a/osu.Game/Online/API/Requests/MarkChannelAsReadRequest.cs +++ b/osu.Game/Online/API/Requests/MarkChannelAsReadRequest.cs @@ -9,16 +9,16 @@ namespace osu.Game.Online.API.Requests { public class MarkChannelAsReadRequest : APIRequest { - private readonly Channel channel; - private readonly Message message; + public readonly Channel Channel; + public readonly Message Message; public MarkChannelAsReadRequest(Channel channel, Message message) { - this.channel = channel; - this.message = message; + this.Channel = channel; + this.Message = message; } - protected override string Target => $"chat/channels/{channel.Id}/mark-as-read/{message.Id}"; + protected override string Target => $"chat/channels/{Channel.Id}/mark-as-read/{Message.Id}"; protected override WebRequest CreateWebRequest() { From 7d7c5c06f06a3d5f987623defcdc878e518fed26 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 15 Aug 2021 16:02:25 +0200 Subject: [PATCH 1161/2442] Fix code formatting --- .../Chat/TestSceneChannelManager.cs | 41 ++++++++++--------- .../API/Requests/MarkChannelAsReadRequest.cs | 4 +- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/osu.Game.Tests/Chat/TestSceneChannelManager.cs b/osu.Game.Tests/Chat/TestSceneChannelManager.cs index 7b6d2078ca..fcb602cb04 100644 --- a/osu.Game.Tests/Chat/TestSceneChannelManager.cs +++ b/osu.Game.Tests/Chat/TestSceneChannelManager.cs @@ -20,7 +20,7 @@ namespace osu.Game.Tests.Chat { private ChannelManager channelManager; private int currentMessageId; - List sentMessages; + private List sentMessages; [SetUp] public void Setup() => Schedule(() => @@ -90,46 +90,49 @@ namespace osu.Game.Tests.Chat } [Test] - public void TestMarkAsReadIgnoringLocalMessages() { + public void TestMarkAsReadIgnoringLocalMessages() + { Channel channel = null; AddStep("join channel and select it", () => { channelManager.JoinChannel(channel = createChannel(1, ChannelType.Public)); - channelManager.CurrentChannel.Value = channel; }); AddStep("post message", () => channelManager.PostMessage("Something interesting")); AddUntilStep("wait until the message is posted", () => channel.Messages.Count == sentMessages.Count); - AddStep("post /help command", () => channelManager.PostCommand("help", channel)); AddStep("post /me command with no action", () => channelManager.PostCommand("me", channel)); AddStep("post /join command with no channel", () => channelManager.PostCommand("join", channel)); AddStep("post /join command with non-existent channel", () => channelManager.PostCommand("join i-dont-exist", channel)); AddStep("post non-existent command", () => channelManager.PostCommand("non-existent-cmd arg", channel)); - AddStep("register mark channel as read request handler", () => { + AddStep("register mark channel as read request handler", () => + { ((DummyAPIAccess)API).HandleRequest = req => + { + switch (req) { - switch (req) - { - case MarkChannelAsReadRequest markRead: - var isSentMessage = sentMessages.Contains(markRead.Message); + case MarkChannelAsReadRequest markRead: + var isSentMessage = sentMessages.Contains(markRead.Message); - AddAssert("mark channel as read called with a real message", () => isSentMessage); + AddAssert("mark channel as read called with a real message", () => isSentMessage); - if(isSentMessage) { - markRead.TriggerSuccess(); - } else { - markRead.TriggerFailure(new APIException("unknown message!", null)); - } + if (isSentMessage) + { + markRead.TriggerSuccess(); + } + else + { + markRead.TriggerFailure(new APIException("unknown message!", null)); + } - return true; - } + return true; + } - return false; - }; + return false; + }; }); AddStep("mark channel as read", () => channelManager.MarkChannelAsRead(channel)); diff --git a/osu.Game/Online/API/Requests/MarkChannelAsReadRequest.cs b/osu.Game/Online/API/Requests/MarkChannelAsReadRequest.cs index 594ab2accc..b24669e6d5 100644 --- a/osu.Game/Online/API/Requests/MarkChannelAsReadRequest.cs +++ b/osu.Game/Online/API/Requests/MarkChannelAsReadRequest.cs @@ -14,8 +14,8 @@ namespace osu.Game.Online.API.Requests public MarkChannelAsReadRequest(Channel channel, Message message) { - this.Channel = channel; - this.Message = message; + Channel = channel; + Message = message; } protected override string Target => $"chat/channels/{Channel.Id}/mark-as-read/{Message.Id}"; From 7c88a1c6dee143dbea7f60530c7fe17acd50cf0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 15 Aug 2021 16:00:22 +0200 Subject: [PATCH 1162/2442] Add a way to change custom combo colours via `IHasComboColours` `IHasComboColours` was already mutable (via a strange `AddComboColours()` method) and exposing a straight list is easier to work with. `IHasCustomColours` is also similarly externally mutable (in a way which is not easily removable). --- osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs | 5 +++-- osu.Game/Beatmaps/Formats/IHasComboColours.cs | 9 +++++++++ osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 2 +- osu.Game/Skinning/DefaultLegacySkin.cs | 6 ++++-- osu.Game/Skinning/SkinConfiguration.cs | 8 ++++---- osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs | 4 ++-- 6 files changed, 23 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs index c15d804a19..aadabec100 100644 --- a/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs +++ b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs @@ -133,11 +133,12 @@ namespace osu.Game.Tests.Skins [Test] public void TestEmptyComboColoursNoFallback() { - AddStep("Add custom combo colours to user skin", () => userSource.Configuration.AddComboColours( + AddStep("Add custom combo colours to user skin", () => userSource.Configuration.CustomComboColours = new List + { new Color4(100, 150, 200, 255), new Color4(55, 110, 166, 255), new Color4(75, 125, 175, 255) - )); + }); AddStep("Disallow default colours fallback in beatmap skin", () => beatmapSource.Configuration.AllowDefaultComboColoursFallback = false); diff --git a/osu.Game/Beatmaps/Formats/IHasComboColours.cs b/osu.Game/Beatmaps/Formats/IHasComboColours.cs index 41c85db063..91d960a2fb 100644 --- a/osu.Game/Beatmaps/Formats/IHasComboColours.cs +++ b/osu.Game/Beatmaps/Formats/IHasComboColours.cs @@ -1,6 +1,7 @@ // 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 osuTK.Graphics; @@ -13,9 +14,17 @@ namespace osu.Game.Beatmaps.Formats /// IReadOnlyList ComboColours { get; } + /// + /// The list of custom combo colours. + /// If non-empty, will return these colours; + /// if empty, will fall back to default combo colours. + /// + List CustomComboColours { get; } + /// /// Adds combo colours to the list. /// + [Obsolete("Use SkinConfiguration.ComboColours directly.")] // can be removed 20220215 void AddComboColours(params Color4[] colours); } } diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index b39890084f..20080308f9 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -123,7 +123,7 @@ namespace osu.Game.Beatmaps.Formats { if (!(output is IHasComboColours tHasComboColours)) return; - tHasComboColours.AddComboColours(colour); + tHasComboColours.CustomComboColours.Add(colour); } else { diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs index 30192182f3..16ac17546d 100644 --- a/osu.Game/Skinning/DefaultLegacySkin.cs +++ b/osu.Game/Skinning/DefaultLegacySkin.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using JetBrains.Annotations; using osu.Framework.IO.Stores; using osu.Game.Extensions; @@ -21,12 +22,13 @@ namespace osu.Game.Skinning : base(skin, new NamespacedResourceStore(resources.Resources, "Skins/Legacy"), resources, string.Empty) { Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255); - Configuration.AddComboColours( + Configuration.CustomComboColours = new List + { new Color4(255, 192, 0, 255), new Color4(0, 202, 0, 255), new Color4(18, 124, 255, 255), new Color4(242, 24, 57, 255) - ); + }; Configuration.LegacyVersion = 2.7m; } diff --git a/osu.Game/Skinning/SkinConfiguration.cs b/osu.Game/Skinning/SkinConfiguration.cs index 25a924c929..a18144246f 100644 --- a/osu.Game/Skinning/SkinConfiguration.cs +++ b/osu.Game/Skinning/SkinConfiguration.cs @@ -27,14 +27,14 @@ namespace osu.Game.Skinning new Color4(242, 24, 57, 255), }; - private readonly List comboColours = new List(); + public List CustomComboColours { get; set; } = new List(); public IReadOnlyList ComboColours { get { - if (comboColours.Count > 0) - return comboColours; + if (CustomComboColours.Count > 0) + return CustomComboColours; if (AllowDefaultComboColoursFallback) return DefaultComboColours; @@ -43,7 +43,7 @@ namespace osu.Game.Skinning } } - public void AddComboColours(params Color4[] colours) => comboColours.AddRange(colours); + void IHasComboColours.AddComboColours(params Color4[] colours) => CustomComboColours.AddRange(colours); public Dictionary CustomColours { get; } = new Dictionary(); diff --git a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs index cdf6a9a2b4..356d2c848c 100644 --- a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs +++ b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs @@ -116,7 +116,7 @@ namespace osu.Game.Tests.Beatmaps { if (hasColours) { - Configuration.AddComboColours(Colours); + Configuration.CustomComboColours = Colours.ToList(); Configuration.CustomColours.Add("HyperDash", HYPER_DASH_COLOUR); Configuration.CustomColours.Add("HyperDashAfterImage", HYPER_DASH_AFTER_IMAGE_COLOUR); Configuration.CustomColours.Add("HyperDashFruit", HYPER_DASH_FRUIT_COLOUR); @@ -145,7 +145,7 @@ namespace osu.Game.Tests.Beatmaps { if (hasCustomColours) { - Configuration.AddComboColours(Colours); + Configuration.CustomComboColours = Colours.ToList(); Configuration.CustomColours.Add("HyperDash", HYPER_DASH_COLOUR); Configuration.CustomColours.Add("HyperDashAfterImage", HYPER_DASH_AFTER_IMAGE_COLOUR); Configuration.CustomColours.Add("HyperDashFruit", HYPER_DASH_FRUIT_COLOUR); From 8f698a42f727472723668931713ae4671af2ace0 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 15 Aug 2021 20:35:52 +0200 Subject: [PATCH 1163/2442] Refactor `MarkChannelAsRead` test assert --- .../Chat/TestSceneChannelManager.cs | 75 +++++++++---------- 1 file changed, 35 insertions(+), 40 deletions(-) diff --git a/osu.Game.Tests/Chat/TestSceneChannelManager.cs b/osu.Game.Tests/Chat/TestSceneChannelManager.cs index fcb602cb04..5e22101e5c 100644 --- a/osu.Game.Tests/Chat/TestSceneChannelManager.cs +++ b/osu.Game.Tests/Chat/TestSceneChannelManager.cs @@ -47,19 +47,11 @@ namespace osu.Game.Tests.Chat return true; case PostMessageRequest postMessage: - var message = new Message(++currentMessageId) - { - IsAction = postMessage.Message.IsAction, - ChannelId = postMessage.Message.ChannelId, - Content = postMessage.Message.Content, - Links = postMessage.Message.Links, - Timestamp = postMessage.Message.Timestamp, - Sender = postMessage.Message.Sender - }; - - sentMessages.Add(message); - postMessage.TriggerSuccess(message); + handlePostMessageRequest(postMessage); + return true; + case MarkChannelAsReadRequest markRead: + handleMarkChannelAsReadRequest(markRead); return true; } @@ -101,41 +93,44 @@ namespace osu.Game.Tests.Chat }); AddStep("post message", () => channelManager.PostMessage("Something interesting")); - AddUntilStep("wait until the message is posted", () => channel.Messages.Count == sentMessages.Count); + AddStep("post /help command", () => channelManager.PostCommand("help", channel)); AddStep("post /me command with no action", () => channelManager.PostCommand("me", channel)); AddStep("post /join command with no channel", () => channelManager.PostCommand("join", channel)); AddStep("post /join command with non-existent channel", () => channelManager.PostCommand("join i-dont-exist", channel)); AddStep("post non-existent command", () => channelManager.PostCommand("non-existent-cmd arg", channel)); - AddStep("register mark channel as read request handler", () => - { - ((DummyAPIAccess)API).HandleRequest = req => - { - switch (req) - { - case MarkChannelAsReadRequest markRead: - var isSentMessage = sentMessages.Contains(markRead.Message); - - AddAssert("mark channel as read called with a real message", () => isSentMessage); - - if (isSentMessage) - { - markRead.TriggerSuccess(); - } - else - { - markRead.TriggerFailure(new APIException("unknown message!", null)); - } - - return true; - } - - return false; - }; - }); - AddStep("mark channel as read", () => channelManager.MarkChannelAsRead(channel)); + AddAssert("channel's last read ID is set to the latest message", () => channel.LastReadId == sentMessages.Last().Id); + } + + private void handlePostMessageRequest(PostMessageRequest request) + { + var message = new Message(++currentMessageId) + { + IsAction = request.Message.IsAction, + ChannelId = request.Message.ChannelId, + Content = request.Message.Content, + Links = request.Message.Links, + Timestamp = request.Message.Timestamp, + Sender = request.Message.Sender + }; + + sentMessages.Add(message); + request.TriggerSuccess(message); + } + + private void handleMarkChannelAsReadRequest(MarkChannelAsReadRequest request) + { + // only accept messages that were sent through the API + if (sentMessages.Contains(request.Message)) + { + request.TriggerSuccess(); + } + else + { + request.TriggerFailure(new APIException("unknown message!", null)); + } } private Channel createChannel(int id, ChannelType type) => new Channel(new User()) From 29a22bd11f4b82304a14a1501ec48a373d4c1651 Mon Sep 17 00:00:00 2001 From: emu1337 Date: Sun, 15 Aug 2021 20:48:00 +0200 Subject: [PATCH 1164/2442] added rhythm multiplier for strain sections --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 15 +++++++++++++++ .../Rulesets/Difficulty/Skills/StrainSkill.cs | 7 ++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index f0eb199e5f..b1373cd215 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -34,6 +34,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { } + private double calculateRhythmBonus(double time) + { + return 1.0; + } + + protected override double StrainValueOf(DifficultyHitObject current) { if (current.BaseObject is Spinner) @@ -66,5 +72,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills return (1 + (speedBonus - 1) * 0.75) * angleBonus * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / osuCurrent.StrainTime; } + protected override double GetTotalCurrentStrain(DifficultyHitObject current) + { + return base.GetTotalCurrentStrain(current) * calculateRhythmBonus(current.StartTime); + } + + protected override double GetPeakStrain(double time) + { + return base.GetPeakStrain(time) * calculateRhythmBonus(time); + } } } diff --git a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs index d4fcefab9b..95b0fe43fc 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs @@ -71,7 +71,12 @@ namespace osu.Game.Rulesets.Difficulty.Skills CurrentStrain *= strainDecay(current.DeltaTime); CurrentStrain += StrainValueOf(current) * SkillMultiplier; - currentSectionPeak = Math.Max(CurrentStrain, currentSectionPeak); + currentSectionPeak = Math.Max(GetTotalCurrentStrain(current), currentSectionPeak); + } + + protected virtual double GetTotalCurrentStrain(DifficultyHitObject current) + { + return CurrentStrain; } /// From df43e758ee18c95ca49e36c05e526517b38646b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 15 Aug 2021 16:13:59 +0200 Subject: [PATCH 1165/2442] Add editable beatmap skin --- osu.Game/Screens/Edit/EditorBeatmap.cs | 5 +- osu.Game/Screens/Edit/EditorBeatmapSkin.cs | 59 ++++++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 osu.Game/Screens/Edit/EditorBeatmapSkin.cs diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index 7de98e5e85..3402bf653a 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -54,7 +54,7 @@ namespace osu.Game.Screens.Edit private readonly Bindable hasTiming = new Bindable(); [CanBeNull] - public readonly ISkin BeatmapSkin; + public readonly EditorBeatmapSkin BeatmapSkin; [Resolved] private BindableBeatDivisor beatDivisor { get; set; } @@ -69,7 +69,8 @@ namespace osu.Game.Screens.Edit public EditorBeatmap(IBeatmap playableBeatmap, ISkin beatmapSkin = null) { PlayableBeatmap = playableBeatmap; - BeatmapSkin = beatmapSkin; + if (beatmapSkin is Skin skin) + BeatmapSkin = new EditorBeatmapSkin(skin); beatmapProcessor = playableBeatmap.BeatmapInfo.Ruleset?.CreateInstance().CreateBeatmapProcessor(PlayableBeatmap); diff --git a/osu.Game/Screens/Edit/EditorBeatmapSkin.cs b/osu.Game/Screens/Edit/EditorBeatmapSkin.cs new file mode 100644 index 0000000000..6745f08b80 --- /dev/null +++ b/osu.Game/Screens/Edit/EditorBeatmapSkin.cs @@ -0,0 +1,59 @@ +// 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.Linq; +using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.OpenGL.Textures; +using osu.Framework.Graphics.Textures; +using osu.Game.Audio; +using osu.Game.Skinning; +using osuTK.Graphics; + +namespace osu.Game.Screens.Edit +{ + /// + /// A beatmap skin which is being edited. + /// + public class EditorBeatmapSkin : ISkin + { + public event Action BeatmapSkinChanged; + + /// + /// The combo colours of this skin. + /// If empty, the default combo colours will be used. + /// + public BindableList ComboColours; + + private readonly Skin skin; + + public EditorBeatmapSkin(Skin skin) + { + this.skin = skin; + + ComboColours = new BindableList(); + if (skin.Configuration.ComboColours != null) + ComboColours.AddRange(skin.Configuration.ComboColours.Select(c => (Colour4)c)); + ComboColours.BindCollectionChanged((_, __) => updateColours()); + } + + private void invokeSkinChanged() => BeatmapSkinChanged?.Invoke(); + + private void updateColours() + { + skin.Configuration.CustomComboColours = ComboColours.Select(c => (Color4)c).ToList(); + invokeSkinChanged(); + } + + #region Delegated ISkin implementation + + public Drawable GetDrawableComponent(ISkinComponent component) => skin.GetDrawableComponent(component); + public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => skin.GetTexture(componentName, wrapModeS, wrapModeT); + public ISample GetSample(ISampleInfo sampleInfo) => skin.GetSample(sampleInfo); + public IBindable GetConfig(TLookup lookup) => skin.GetConfig(lookup); + + #endregion + } +} From 0d64da8c63f1e6533f62defaddc21d6b6bc727ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 15 Aug 2021 16:14:41 +0200 Subject: [PATCH 1166/2442] Add skin providing container responding to beatmap skin edits --- .../Screens/Edit/Compose/ComposeScreen.cs | 3 +- .../Edit/EditorSkinProvidingContainer.cs | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 osu.Game/Screens/Edit/EditorSkinProvidingContainer.cs diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs index 4a1f1196a9..62b3d33069 100644 --- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs +++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs @@ -15,7 +15,6 @@ using osu.Game.Extensions; using osu.Game.Rulesets; using osu.Game.Rulesets.Edit; using osu.Game.Screens.Edit.Compose.Components.Timeline; -using osu.Game.Skinning; namespace osu.Game.Screens.Edit.Compose { @@ -73,7 +72,7 @@ namespace osu.Game.Screens.Edit.Compose { Debug.Assert(ruleset != null); - return new RulesetSkinProvidingContainer(ruleset, EditorBeatmap.PlayableBeatmap, beatmap.Value.Skin).WithChild(content); + return new EditorSkinProvidingContainer(EditorBeatmap).WithChild(content); } #region Input Handling diff --git a/osu.Game/Screens/Edit/EditorSkinProvidingContainer.cs b/osu.Game/Screens/Edit/EditorSkinProvidingContainer.cs new file mode 100644 index 0000000000..27563b5a0f --- /dev/null +++ b/osu.Game/Screens/Edit/EditorSkinProvidingContainer.cs @@ -0,0 +1,40 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Skinning; + +#nullable enable + +namespace osu.Game.Screens.Edit +{ + /// + /// A that fires when users have made a change to the beatmap skin + /// of the map being edited. + /// + public class EditorSkinProvidingContainer : RulesetSkinProvidingContainer + { + private readonly EditorBeatmapSkin? beatmapSkin; + + public EditorSkinProvidingContainer(EditorBeatmap editorBeatmap) + : base(editorBeatmap.PlayableBeatmap.BeatmapInfo.Ruleset.CreateInstance(), editorBeatmap, editorBeatmap.BeatmapSkin) + { + beatmapSkin = editorBeatmap.BeatmapSkin; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + if (beatmapSkin != null) + beatmapSkin.BeatmapSkinChanged += TriggerSourceChanged; + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (beatmapSkin != null) + beatmapSkin.BeatmapSkinChanged -= TriggerSourceChanged; + } + } +} From 81280dfd257fe482e0baeab20c828849b26837a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 15 Aug 2021 16:32:26 +0200 Subject: [PATCH 1167/2442] Use editable skin structure in combo colour picker --- osu.Game/Screens/Edit/Setup/ColoursSection.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Edit/Setup/ColoursSection.cs b/osu.Game/Screens/Edit/Setup/ColoursSection.cs index d7e16645f2..05d9855a24 100644 --- a/osu.Game/Screens/Edit/Setup/ColoursSection.cs +++ b/osu.Game/Screens/Edit/Setup/ColoursSection.cs @@ -1,14 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Graphics.UserInterfaceV2; -using osu.Game.Skinning; -using osuTK.Graphics; namespace osu.Game.Screens.Edit.Setup { @@ -31,9 +27,8 @@ namespace osu.Game.Screens.Edit.Setup } }; - var colours = Beatmap.BeatmapSkin?.GetConfig>(GlobalSkinColours.ComboColours)?.Value; - if (colours != null) - comboColours.Colours.AddRange(colours.Select(c => (Colour4)c)); + if (Beatmap.BeatmapSkin != null) + comboColours.Colours.BindTo(Beatmap.BeatmapSkin.ComboColours); } } } From 6108451449c8b5a56734a28b4fb034226b01ee53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 15 Aug 2021 18:38:01 +0200 Subject: [PATCH 1168/2442] Retrieve separated skin instance from working beatmap for editing --- .../Beatmaps/Formats/LegacyBeatmapEncoderTest.cs | 2 +- osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs | 2 +- .../Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs | 2 +- osu.Game.Tests/WaveformTestBeatmap.cs | 2 +- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs | 2 +- osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 10 +++++++++- osu.Game/Screens/Edit/Editor.cs | 2 +- osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs | 2 +- osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs | 2 +- osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs | 2 +- osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs | 2 +- osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs | 2 +- 14 files changed, 22 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index 059432eeaf..855a75117d 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -169,7 +169,7 @@ namespace osu.Game.Tests.Beatmaps.Formats protected override Track GetBeatmapTrack() => throw new NotImplementedException(); - protected override ISkin GetSkin() => throw new NotImplementedException(); + protected internal override ISkin GetSkin() => throw new NotImplementedException(); public override Stream GetStream(string storagePath) => throw new NotImplementedException(); } diff --git a/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs b/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs index aed28f5f84..3bf6aaac7a 100644 --- a/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs +++ b/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs @@ -204,7 +204,7 @@ namespace osu.Game.Tests.Gameplay this.resources = resources; } - protected override ISkin GetSkin() => new TestSkin("test-sample", resources); + protected internal override ISkin GetSkin() => new TestSkin("test-sample", resources); } private class TestDrawableStoryboardSample : DrawableStoryboardSample diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs index 13e84e335d..e560c81fb2 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs @@ -111,7 +111,7 @@ namespace osu.Game.Tests.Visual.Gameplay this.beatmapSkin = beatmapSkin; } - protected override ISkin GetSkin() => beatmapSkin; + protected internal override ISkin GetSkin() => beatmapSkin; } private class TestOsuRuleset : OsuRuleset diff --git a/osu.Game.Tests/WaveformTestBeatmap.cs b/osu.Game.Tests/WaveformTestBeatmap.cs index 5477e4a0f8..9c85fa0c9c 100644 --- a/osu.Game.Tests/WaveformTestBeatmap.cs +++ b/osu.Game.Tests/WaveformTestBeatmap.cs @@ -53,7 +53,7 @@ namespace osu.Game.Tests protected override Waveform GetWaveform() => new Waveform(trackStore.GetStream(firstAudioFile)); - protected override ISkin GetSkin() => null; + protected internal override ISkin GetSkin() => null; public override Stream GetStream(string storagePath) => null; diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 0d16294c68..4a78ceb299 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -534,7 +534,7 @@ namespace osu.Game.Beatmaps protected override IBeatmap GetBeatmap() => beatmap; protected override Texture GetBackground() => null; protected override Track GetBeatmapTrack() => null; - protected override ISkin GetSkin() => null; + protected internal override ISkin GetSkin() => null; public override Stream GetStream(string storagePath) => null; } } diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index d78ffbbfb6..45112ae74c 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -128,7 +128,7 @@ namespace osu.Game.Beatmaps return storyboard; } - protected override ISkin GetSkin() + protected internal override ISkin GetSkin() { try { diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index ea7f45e53f..acfd01a3c8 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -50,7 +50,7 @@ namespace osu.Game.Beatmaps protected override Track GetBeatmapTrack() => GetVirtualTrack(); - protected override ISkin GetSkin() => null; + protected internal override ISkin GetSkin() => null; public override Stream GetStream(string storagePath) => null; diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 662d24cc83..61760e69b0 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -327,7 +327,15 @@ namespace osu.Game.Beatmaps public bool SkinLoaded => skin.IsResultAvailable; public ISkin Skin => skin.Value; - protected abstract ISkin GetSkin(); + /// + /// Creates a new skin instance for this beatmap. + /// + /// + /// This should only be called externally in scenarios where it is explicitly desired to get a new instance of a skin + /// (e.g. for editing purposes, to avoid state pollution). + /// For standard reading purposes, should always be used directly. + /// + protected internal abstract ISkin GetSkin(); private readonly RecyclableLazy skin; diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 61a3b0f5cc..e8d919311b 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -153,7 +153,7 @@ namespace osu.Game.Screens.Edit // todo: remove caching of this and consume via editorBeatmap? dependencies.Cache(beatDivisor); - AddInternal(editorBeatmap = new EditorBeatmap(playableBeatmap, loadableBeatmap.Skin)); + AddInternal(editorBeatmap = new EditorBeatmap(playableBeatmap, loadableBeatmap.GetSkin())); dependencies.CacheAs(editorBeatmap); changeHandler = new EditorChangeHandler(editorBeatmap); dependencies.CacheAs(changeHandler); diff --git a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs index 6f92db98ee..d26856365e 100644 --- a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs +++ b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs @@ -118,7 +118,7 @@ namespace osu.Game.Screens.Edit protected override Track GetBeatmapTrack() => throw new NotImplementedException(); - protected override ISkin GetSkin() => throw new NotImplementedException(); + protected internal override ISkin GetSkin() => throw new NotImplementedException(); public override Stream GetStream(string storagePath) => throw new NotImplementedException(); } diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index 4935f7fc13..64f1ee4a7a 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -217,7 +217,7 @@ namespace osu.Game.Tests.Beatmaps protected override Track GetBeatmapTrack() => throw new NotImplementedException(); - protected override ISkin GetSkin() => throw new NotImplementedException(); + protected internal override ISkin GetSkin() => throw new NotImplementedException(); public override Stream GetStream(string storagePath) => throw new NotImplementedException(); diff --git a/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs b/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs index 7ee6c519b7..bb5dd09e16 100644 --- a/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs +++ b/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs @@ -211,7 +211,7 @@ namespace osu.Game.Tests.Beatmaps this.resources = resources; } - protected override ISkin GetSkin() => new LegacyBeatmapSkin(skinBeatmapInfo, resourceStore, resources); + protected internal override ISkin GetSkin() => new LegacyBeatmapSkin(skinBeatmapInfo, resourceStore, resources); } } } diff --git a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs index 356d2c848c..27162b1d66 100644 --- a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs +++ b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs @@ -92,7 +92,7 @@ namespace osu.Game.Tests.Beatmaps HasColours = hasColours; } - protected override ISkin GetSkin() => new TestBeatmapSkin(BeatmapInfo, HasColours); + protected internal override ISkin GetSkin() => new TestBeatmapSkin(BeatmapInfo, HasColours); } protected class TestBeatmapSkin : LegacyBeatmapSkin diff --git a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs index bfce59c7de..19974701db 100644 --- a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs @@ -37,7 +37,7 @@ namespace osu.Game.Tests.Beatmaps protected override Storyboard GetStoryboard() => storyboard ?? base.GetStoryboard(); - protected override ISkin GetSkin() => null; + protected internal override ISkin GetSkin() => null; public override Stream GetStream(string storagePath) => null; From f6773522d1652a94ea1618a5482e8d668758dfd7 Mon Sep 17 00:00:00 2001 From: Daniel Kariv <38776931+danielkariv@users.noreply.github.com> Date: Mon, 16 Aug 2021 00:12:27 +0300 Subject: [PATCH 1169/2442] Correct icons in beatmap details In relation to #13968. Replacing incorrect icons in beatmap details panel to correct ones from BeatmapStatisticsIcon class. --- osu.Game/Overlays/BeatmapSet/BasicStats.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/BasicStats.cs b/osu.Game/Overlays/BeatmapSet/BasicStats.cs index 3f1034759e..21bb75b960 100644 --- a/osu.Game/Overlays/BeatmapSet/BasicStats.cs +++ b/osu.Game/Overlays/BeatmapSet/BasicStats.cs @@ -78,10 +78,10 @@ namespace osu.Game.Overlays.BeatmapSet Direction = FillDirection.Horizontal, Children = new[] { - length = new Statistic(FontAwesome.Regular.Clock, "Length") { Width = 0.25f }, - bpm = new Statistic(FontAwesome.Regular.Circle, "BPM") { Width = 0.25f }, - circleCount = new Statistic(FontAwesome.Regular.Circle, "Circle Count") { Width = 0.25f }, - sliderCount = new Statistic(FontAwesome.Regular.Circle, "Slider Count") { Width = 0.25f }, + length = new Statistic(BeatmapStatisticsIconType.Length, "Length") { Width = 0.25f }, + bpm = new Statistic(BeatmapStatisticsIconType.Bpm, "BPM") { Width = 0.25f }, + circleCount = new Statistic(BeatmapStatisticsIconType.Circles, "Circle Count") { Width = 0.25f }, + sliderCount = new Statistic(BeatmapStatisticsIconType.Sliders, "Slider Count") { Width = 0.25f }, }, }; } @@ -104,7 +104,7 @@ namespace osu.Game.Overlays.BeatmapSet set => this.value.Text = value; } - public Statistic(IconUsage icon, string name) + public Statistic(BeatmapStatisticsIconType icon, string name) { TooltipText = name; RelativeSizeAxes = Axes.X; @@ -129,11 +129,9 @@ namespace osu.Game.Overlays.BeatmapSet Rotation = 45, Colour = Color4Extensions.FromHex(@"441288"), }, - new SpriteIcon - { + new BeatmapStatisticIcon(icon){ Anchor = Anchor.CentreLeft, Origin = Anchor.Centre, - Icon = icon, Size = new Vector2(12), Colour = Color4Extensions.FromHex(@"f7dd55"), Scale = new Vector2(0.8f), From 38828b6b82976ee3041bce446e9c21b525027ae3 Mon Sep 17 00:00:00 2001 From: Daniel Kariv <38776931+danielkariv@users.noreply.github.com> Date: Mon, 16 Aug 2021 00:40:31 +0300 Subject: [PATCH 1170/2442] Updating beatmap details icons changes the sizing and add yellow circle so the UI will fit more with osu-web style. --- osu.Game/Overlays/BeatmapSet/BasicStats.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/BasicStats.cs b/osu.Game/Overlays/BeatmapSet/BasicStats.cs index 21bb75b960..757698e1aa 100644 --- a/osu.Game/Overlays/BeatmapSet/BasicStats.cs +++ b/osu.Game/Overlays/BeatmapSet/BasicStats.cs @@ -129,10 +129,20 @@ namespace osu.Game.Overlays.BeatmapSet Rotation = 45, Colour = Color4Extensions.FromHex(@"441288"), }, - new BeatmapStatisticIcon(icon){ + new SpriteIcon + { Anchor = Anchor.CentreLeft, Origin = Anchor.Centre, - Size = new Vector2(12), + Icon = FontAwesome.Regular.Circle, + Size = new Vector2(10), + Rotation = 0, + Colour = Color4Extensions.FromHex(@"f7dd55"), + }, + new BeatmapStatisticIcon(icon) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.Centre, + Size = new Vector2(10), Colour = Color4Extensions.FromHex(@"f7dd55"), Scale = new Vector2(0.8f), }, From cc3468b4abaec8f0ead7af7431c3b051667c924b Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Mon, 16 Aug 2021 06:32:33 +0800 Subject: [PATCH 1171/2442] apply suggestions - make `UserActivity.InGame` and derive that to `InSoloGame` and `InMultiplayerGame` - rename `SoloGame` to `InSoloGame` - rename `MultiplayerGame` to `InMultiplayerGame` --- osu.Desktop/DiscordRichPresence.cs | 4 +- .../Online/TestSceneNowPlayingCommand.cs | 2 +- .../Visual/Online/TestSceneUserPanel.cs | 2 +- osu.Game/Online/Chat/NowPlayingCommand.cs | 4 +- osu.Game/Rulesets/Ruleset.cs | 2 +- osu.Game/Screens/Play/Player.cs | 2 +- osu.Game/Screens/Play/RoomSubmittingPlayer.cs | 2 +- osu.Game/Users/UserActivity.cs | 44 +++++++++++-------- 8 files changed, 35 insertions(+), 27 deletions(-) diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index 832d26b0ef..dcb88efeb6 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -139,8 +139,8 @@ namespace osu.Desktop { switch (activity) { - case UserActivity.SoloGame solo: - return solo.Beatmap.ToString(); + case UserActivity.InGame game: + return game.Beatmap.ToString(); case UserActivity.Editing edit: return edit.Beatmap.ToString(); diff --git a/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs b/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs index 64e80e9f02..366fa8a4af 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs @@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestPlayActivity() { - AddStep("Set activity", () => api.Activity.Value = new UserActivity.SoloGame(new BeatmapInfo(), new RulesetInfo())); + AddStep("Set activity", () => api.Activity.Value = new UserActivity.InSoloGame(new BeatmapInfo(), new RulesetInfo())); AddStep("Run command", () => Add(new NowPlayingCommand())); diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs index c2e9945c99..a048ae2c54 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs @@ -130,7 +130,7 @@ namespace osu.Game.Tests.Visual.Online AddAssert("visit message is not visible", () => !evast.LastVisitMessage.IsPresent); } - private UserActivity soloGameStatusForRuleset(int rulesetId) => new UserActivity.SoloGame(null, rulesetStore.GetRuleset(rulesetId)); + private UserActivity soloGameStatusForRuleset(int rulesetId) => new UserActivity.InSoloGame(null, rulesetStore.GetRuleset(rulesetId)); private class TestUserListPanel : UserListPanel { diff --git a/osu.Game/Online/Chat/NowPlayingCommand.cs b/osu.Game/Online/Chat/NowPlayingCommand.cs index 7756591e03..97a2fbdd5c 100644 --- a/osu.Game/Online/Chat/NowPlayingCommand.cs +++ b/osu.Game/Online/Chat/NowPlayingCommand.cs @@ -41,9 +41,9 @@ namespace osu.Game.Online.Chat switch (api.Activity.Value) { - case UserActivity.SoloGame solo: + case UserActivity.InGame game: verb = "playing"; - beatmap = solo.Beatmap; + beatmap = game.Beatmap; break; case UserActivity.Editing edit: diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 98eb374a0d..cd1ff0f218 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -224,7 +224,7 @@ namespace osu.Game.Rulesets public abstract string ShortName { get; } /// - /// The playing verb to be shown in the . + /// The playing verb to be shown in the . /// public virtual string PlayingVerb => "Playing"; diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 09eaf1c543..f1da9fddc7 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -47,7 +47,7 @@ namespace osu.Game.Screens.Play public override bool AllowBackButton => false; // handled by HoldForMenuButton - protected override UserActivity InitialActivity => new UserActivity.SoloGame(Beatmap.Value.BeatmapInfo, Ruleset.Value); + protected override UserActivity InitialActivity => new UserActivity.InSoloGame(Beatmap.Value.BeatmapInfo, Ruleset.Value); public override float BackgroundParallaxAmount => 0.1f; diff --git a/osu.Game/Screens/Play/RoomSubmittingPlayer.cs b/osu.Game/Screens/Play/RoomSubmittingPlayer.cs index b6f5fe0205..f751fb747c 100644 --- a/osu.Game/Screens/Play/RoomSubmittingPlayer.cs +++ b/osu.Game/Screens/Play/RoomSubmittingPlayer.cs @@ -20,7 +20,7 @@ namespace osu.Game.Screens.Play protected readonly PlaylistItem PlaylistItem; - protected override UserActivity InitialActivity => new UserActivity.MultiplayerGame(Beatmap.Value.BeatmapInfo, Ruleset.Value); + protected override UserActivity InitialActivity => new UserActivity.InMultiplayerGame(Beatmap.Value.BeatmapInfo, Ruleset.Value); protected RoomSubmittingPlayer(PlaylistItem playlistItem, PlayerConfiguration configuration = null) : base(configuration) diff --git a/osu.Game/Users/UserActivity.cs b/osu.Game/Users/UserActivity.cs index f48c714a30..a6b2719956 100644 --- a/osu.Game/Users/UserActivity.cs +++ b/osu.Game/Users/UserActivity.cs @@ -25,14 +25,37 @@ namespace osu.Game.Users public override string Status => "Choosing a beatmap"; } - public class MultiplayerGame : SoloGame + public abstract class InGame : UserActivity { - public MultiplayerGame(BeatmapInfo beatmap, RulesetInfo ruleset) + public BeatmapInfo Beatmap { get; } + + public RulesetInfo Ruleset { get; } + + public InGame(BeatmapInfo info, RulesetInfo ruleset) + { + Beatmap = info; + Ruleset = ruleset; + } + } + + public class InMultiplayerGame : InGame + { + public InMultiplayerGame(BeatmapInfo beatmap, RulesetInfo ruleset) : base(beatmap, ruleset) { } - public override string Status => $@"{base.Status} with others"; + public override string Status => $@"{Ruleset.CreateInstance().PlayingVerb} with others"; + } + + public class InSoloGame : InGame + { + public InSoloGame(BeatmapInfo info, RulesetInfo ruleset) + : base(info, ruleset) + { + } + + public override string Status => Ruleset.CreateInstance().PlayingVerb; } public class Editing : UserActivity @@ -47,21 +70,6 @@ namespace osu.Game.Users public override string Status => @"Editing a beatmap"; } - public class SoloGame : UserActivity - { - public BeatmapInfo Beatmap { get; } - - public RulesetInfo Ruleset { get; } - - public SoloGame(BeatmapInfo info, RulesetInfo ruleset) - { - Beatmap = info; - Ruleset = ruleset; - } - - public override string Status => Ruleset.CreateInstance().PlayingVerb; - } - public class Spectating : UserActivity { public override string Status => @"Spectating a game"; From e744629a4167586e87e41ab1ba6da547b7f5a530 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 16 Aug 2021 01:01:56 +0200 Subject: [PATCH 1172/2442] Fix broken obsoletion message --- osu.Game/Beatmaps/Formats/IHasComboColours.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/IHasComboColours.cs b/osu.Game/Beatmaps/Formats/IHasComboColours.cs index 91d960a2fb..853a590595 100644 --- a/osu.Game/Beatmaps/Formats/IHasComboColours.cs +++ b/osu.Game/Beatmaps/Formats/IHasComboColours.cs @@ -24,7 +24,7 @@ namespace osu.Game.Beatmaps.Formats /// /// Adds combo colours to the list. /// - [Obsolete("Use SkinConfiguration.ComboColours directly.")] // can be removed 20220215 + [Obsolete("Use CustomComboColours directly.")] // can be removed 20220215 void AddComboColours(params Color4[] colours); } } From e8e387b549d1f1caa50b790bf1633ece3d0b1b7e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Aug 2021 08:02:23 +0900 Subject: [PATCH 1173/2442] Cache buffered background to fix multiplayer lounge performance Consider this a request for comment. It's the cleanest solution I can come up with without dropping either the blur, or use of `ModelBackedDrawable`. Intended to resolve https://github.com/ppy/osu/issues/14276. --- .../Components/OnlinePlayBackgroundSprite.cs | 6 +-- .../Screens/OnlinePlay/OnlinePlayScreen.cs | 48 +++++++++++++++---- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundSprite.cs b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundSprite.cs index d8dfac496d..e2ba0b03b0 100644 --- a/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundSprite.cs +++ b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundSprite.cs @@ -10,12 +10,12 @@ namespace osu.Game.Screens.OnlinePlay.Components { public class OnlinePlayBackgroundSprite : OnlinePlayComposite { - private readonly BeatmapSetCoverType beatmapSetCoverType; + protected readonly BeatmapSetCoverType BeatmapSetCoverType; private UpdateableBeatmapBackgroundSprite sprite; public OnlinePlayBackgroundSprite(BeatmapSetCoverType beatmapSetCoverType = BeatmapSetCoverType.Cover) { - this.beatmapSetCoverType = beatmapSetCoverType; + BeatmapSetCoverType = beatmapSetCoverType; } [BackgroundDependencyLoader] @@ -33,6 +33,6 @@ namespace osu.Game.Screens.OnlinePlay.Components sprite.Beatmap.Value = Playlist.FirstOrDefault()?.Beatmap.Value; } - protected virtual UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new UpdateableBeatmapBackgroundSprite(beatmapSetCoverType) { RelativeSizeAxes = Axes.Both }; + protected virtual UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new UpdateableBeatmapBackgroundSprite(BeatmapSetCoverType) { RelativeSizeAxes = Axes.Both }; } } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index c2ad0285b1..72b63ab4d4 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Logging; using osu.Framework.Screens; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics.Containers; using osu.Game.Input; @@ -104,14 +105,9 @@ namespace osu.Game.Screens.OnlinePlay RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new BufferedContainer + new BeatmapBackgroundSprite { - RelativeSizeAxes = Axes.Both, - BlurSigma = new Vector2(10), - Child = new BeatmapBackgroundSprite - { - RelativeSizeAxes = Axes.Both - } + RelativeSizeAxes = Axes.Both }, new Box { @@ -306,11 +302,45 @@ namespace osu.Game.Screens.OnlinePlay private class BeatmapBackgroundSprite : OnlinePlayBackgroundSprite { - protected override UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new BackgroundSprite { RelativeSizeAxes = Axes.Both }; + protected override UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new BlurredBackgroundSprite(BeatmapSetCoverType) { RelativeSizeAxes = Axes.Both }; - private class BackgroundSprite : UpdateableBeatmapBackgroundSprite + public class BlurredBackgroundSprite : UpdateableBeatmapBackgroundSprite { + public BlurredBackgroundSprite(BeatmapSetCoverType type) + : base(type) + { + } + protected override double LoadDelay => 200; + + protected override Drawable CreateDrawable(BeatmapInfo model) => + new BufferedLoader(base.CreateDrawable(model)); + } + + // This class is an unfortunate requirement due to `LongRunningLoad` requiring direct async loading. + // It means that if the web request fetching the beatmap background takes too long, it will suddenly appear. + internal class BufferedLoader : BufferedContainer + { + private readonly Drawable drawable; + + public BufferedLoader(Drawable drawable) + { + this.drawable = drawable; + + RelativeSizeAxes = Axes.Both; + BlurSigma = new Vector2(10); + CacheDrawnFrameBuffer = true; + } + + [BackgroundDependencyLoader] + private void load() + { + LoadComponentAsync(drawable, d => + { + Add(d); + ForceRedraw(); + }); + } } } From c56b34d2dacabd31463d6b1747421f88983e4de7 Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Mon, 16 Aug 2021 07:06:23 +0800 Subject: [PATCH 1174/2442] apply code inspection fixes --- osu.Game/Rulesets/Ruleset.cs | 2 +- osu.Game/Users/UserActivity.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index cd1ff0f218..3e7479643e 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -224,7 +224,7 @@ namespace osu.Game.Rulesets public abstract string ShortName { get; } /// - /// The playing verb to be shown in the . + /// The playing verb to be shown in the . /// public virtual string PlayingVerb => "Playing"; diff --git a/osu.Game/Users/UserActivity.cs b/osu.Game/Users/UserActivity.cs index a6b2719956..1bbe38b416 100644 --- a/osu.Game/Users/UserActivity.cs +++ b/osu.Game/Users/UserActivity.cs @@ -25,7 +25,7 @@ namespace osu.Game.Users public override string Status => "Choosing a beatmap"; } - public abstract class InGame : UserActivity + public class InGame : UserActivity { public BeatmapInfo Beatmap { get; } @@ -36,6 +36,8 @@ namespace osu.Game.Users Beatmap = info; Ruleset = ruleset; } + + public override string Status => Ruleset.CreateInstance().PlayingVerb; } public class InMultiplayerGame : InGame @@ -45,7 +47,7 @@ namespace osu.Game.Users { } - public override string Status => $@"{Ruleset.CreateInstance().PlayingVerb} with others"; + public override string Status => $@"{base.Status} with others"; } public class InSoloGame : InGame @@ -54,8 +56,6 @@ namespace osu.Game.Users : base(info, ruleset) { } - - public override string Status => Ruleset.CreateInstance().PlayingVerb; } public class Editing : UserActivity From d35886ef196c032075e68bfcc1913f6469187465 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Aug 2021 11:03:49 +0900 Subject: [PATCH 1175/2442] Reduce frame buffer render scale for blurred background --- osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 72b63ab4d4..fd265e9978 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -329,6 +329,7 @@ namespace osu.Game.Screens.OnlinePlay RelativeSizeAxes = Axes.Both; BlurSigma = new Vector2(10); + FrameBufferScale = new Vector2(0.5f); CacheDrawnFrameBuffer = true; } From f0fe79b568724b5151a8b9544438463ee56014a4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Aug 2021 11:04:21 +0900 Subject: [PATCH 1176/2442] Remove buffered container workarounds for now --- .../Lounge/Components/DrawableRoom.cs | 65 +++++++------------ 1 file changed, 25 insertions(+), 40 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 193fb0cf57..c8ecd65574 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -158,21 +158,14 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Children = new Drawable[] { // This resolves internal 1px gaps due to applying the (parenting) corner radius and masking across multiple filling background sprites. - new BufferedContainer + new Box { RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colours.Background5, - }, - new OnlinePlayBackgroundSprite - { - RelativeSizeAxes = Axes.Both - }, - } + Colour = colours.Background5, + }, + new OnlinePlayBackgroundSprite + { + RelativeSizeAxes = Axes.Both }, new Container { @@ -187,37 +180,29 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components CornerRadius = corner_radius, Children = new Drawable[] { - // This resolves internal 1px gaps due to applying the (parenting) corner radius and masking across multiple filling background sprites. - new BufferedContainer + new GridContainer { RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + ColumnDimensions = new[] { - new GridContainer - { - RelativeSizeAxes = Axes.Both, - ColumnDimensions = new[] - { - new Dimension(GridSizeMode.Relative, 0.2f) - }, - Content = new[] - { - new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colours.Background5, - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal(colours.Background5, colours.Background5.Opacity(0.3f)) - }, - } - } - }, + new Dimension(GridSizeMode.Relative, 0.2f) }, + Content = new[] + { + new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Background5, + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientHorizontal(colours.Background5, colours.Background5.Opacity(0.3f)) + }, + } + } }, new Container { From 76a8d4329fe570a10532d3cb87cd09210523abcf Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 16 Aug 2021 12:43:09 +0900 Subject: [PATCH 1177/2442] Make TestRoomManager update existing room --- osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs index 2e9c0d1d53..d7c5a0a0e4 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs @@ -38,7 +38,13 @@ namespace osu.Game.Tests.Visual.OnlinePlay public void AddOrUpdateRoom(Room room) { - Rooms.Add(room); + var existing = Rooms.FirstOrDefault(r => r.RoomID.Value != null && r.RoomID.Value == room.RoomID.Value); + + if (existing != null) + existing.CopyFrom(room); + else + Rooms.Add(room); + RoomsUpdated?.Invoke(); } From 3db0b69f9247ae4037a6c6621fa7bcb9b7078f6e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 16 Aug 2021 12:44:12 +0900 Subject: [PATCH 1178/2442] Throw not implemented exceptions --- .../TestScenePlaylistsMatchSettingsOverlay.cs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs index 79af2d3099..98882b659c 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs @@ -141,17 +141,11 @@ namespace osu.Game.Tests.Visual.Playlists public IBindableList Rooms => null; - public void AddOrUpdateRoom(Room room) - { - } + public void AddOrUpdateRoom(Room room) => throw new NotImplementedException(); - public void RemoveRoom(Room room) - { - } + public void RemoveRoom(Room room) => throw new NotImplementedException(); - public void ClearRooms() - { - } + public void ClearRooms() => throw new NotImplementedException(); public void CreateRoom(Room room, Action onSuccess = null, Action onError = null) { From 9d9974166379c78ab3208c0c3ffbaf94e985e31a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 16 Aug 2021 06:56:59 +0300 Subject: [PATCH 1179/2442] Add failing test case --- .../Visual/Gameplay/TestSceneHUDOverlay.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs index b7e92a79a0..3017428039 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs @@ -12,6 +12,7 @@ using osu.Game.Configuration; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play; +using osu.Game.Skinning; using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay @@ -142,6 +143,22 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("return value", () => config.SetValue(OsuSetting.KeyOverlay, keyCounterVisibleValue)); } + [Test] + public void TestHiddenHUDDoesntBlockSkinnableComponentsLoad() + { + HUDVisibilityMode originalConfigValue = default; + + AddStep("get original config value", () => originalConfigValue = config.Get(OsuSetting.HUDVisibilityMode)); + + AddStep("set hud to never show", () => config.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never)); + + createNew(); + AddUntilStep("wait for hud load", () => hudOverlay.IsLoaded); + AddUntilStep("skinnable components loaded", () => hudOverlay.ChildrenOfType().Single().ComponentsLoaded); + + AddStep("set original config value", () => config.SetValue(OsuSetting.HUDVisibilityMode, originalConfigValue)); + } + private void createNew(Action action = null) { AddStep("create overlay", () => From 554b09ac1b28e36416c929397ffb45fa60337caf Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 16 Aug 2021 06:57:45 +0300 Subject: [PATCH 1180/2442] Fix `SkinnableTargetsContainer` blocked from processing scheduled tasks --- osu.Game/Screens/Play/HUDOverlay.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 2cf2555b3e..13df9fefa7 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -57,8 +57,6 @@ namespace osu.Game.Screens.Play private Bindable configVisibilityMode; - private readonly Container visibilityContainer; - private readonly BindableBool replayLoaded = new BindableBool(); private static bool hasShownNotificationOnce; @@ -72,7 +70,7 @@ namespace osu.Game.Screens.Play private readonly SkinnableTargetContainer mainComponents; - private IEnumerable hideTargets => new Drawable[] { visibilityContainer, KeyCounter, topRightElements }; + private IEnumerable hideTargets => new Drawable[] { mainComponents, KeyCounter, topRightElements }; public HUDOverlay(DrawableRuleset drawableRuleset, IReadOnlyList mods) { @@ -84,13 +82,9 @@ namespace osu.Game.Screens.Play Children = new Drawable[] { CreateFailingLayer(), - visibilityContainer = new Container + mainComponents = new SkinnableTargetContainer(SkinnableTarget.MainHUDComponents) { RelativeSizeAxes = Axes.Both, - Child = mainComponents = new SkinnableTargetContainer(SkinnableTarget.MainHUDComponents) - { - RelativeSizeAxes = Axes.Both, - }, }, topRightElements = new FillFlowContainer { From 81f94424713470101867c541b8e83d4dd719e15d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 16 Aug 2021 13:04:06 +0900 Subject: [PATCH 1181/2442] Inline update/addRoom in usage sites --- .../OnlinePlay/Components/RoomManager.cs | 43 +++++-------------- 1 file changed, 11 insertions(+), 32 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs index ab92adcac7..43bf3a2ce5 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs @@ -57,11 +57,10 @@ namespace osu.Game.Screens.OnlinePlay.Components { joinedRoom.Value = room; - update(room, result); - addRoom(room); + AddOrUpdateRoom(result); + room.CopyFrom(result); // Also copy back to the source model, since this is likely to have been stored elsewhere. - RoomsUpdated?.Invoke(); - onSuccess?.Invoke(room); + onSuccess?.Invoke(result); }; req.Failure += exception => @@ -119,8 +118,14 @@ namespace osu.Game.Screens.OnlinePlay.Components try { - update(room, room); - addRoom(room); + foreach (var pi in room.Playlist) + pi.MapObjects(beatmaps, rulesets); + + var existing = rooms.FirstOrDefault(e => e.RoomID.Value == room.RoomID.Value); + if (existing == null) + rooms.Add(room); + else + existing.CopyFrom(room); } catch (Exception ex) { @@ -145,32 +150,6 @@ namespace osu.Game.Screens.OnlinePlay.Components notifyRoomsUpdated(); } - /// - /// Updates a local with a remote copy. - /// - /// The local to update. - /// The remote to update with. - private void update(Room local, Room remote) - { - foreach (var pi in remote.Playlist) - pi.MapObjects(beatmaps, rulesets); - - local.CopyFrom(remote); - } - - /// - /// Adds a to the list of available rooms. - /// - /// The to add. - private void addRoom(Room room) - { - var existing = rooms.FirstOrDefault(e => e.RoomID.Value == room.RoomID.Value); - if (existing == null) - rooms.Add(room); - else - existing.CopyFrom(room); - } - private void notifyRoomsUpdated() => Scheduler.AddOnce(() => RoomsUpdated?.Invoke()); } } From b6a2020c59ebd7667d821277fcc25493f07ff4e2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 16 Aug 2021 13:09:04 +0900 Subject: [PATCH 1182/2442] General refactorings from PR review --- .../Components/ListingPollingComponent.cs | 8 ++++---- .../Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 15 ++++++++------- .../Multiplayer/MultiplayerLoungeSubScreen.cs | 12 ++++++------ 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs index f686326d08..bc6480d05e 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs @@ -15,8 +15,8 @@ namespace osu.Game.Screens.OnlinePlay.Components /// public class ListingPollingComponent : RoomPollingComponent { - public IBindable HasPolledOnce => hasPolledOnce; - private readonly Bindable hasPolledOnce = new Bindable(); + public IBindable InitialRoomsReceived => initialRoomsReceived; + private readonly Bindable initialRoomsReceived = new Bindable(); [Resolved] private Bindable currentFilter { get; set; } @@ -30,7 +30,7 @@ namespace osu.Game.Screens.OnlinePlay.Components currentFilter.BindValueChanged(_ => { RoomManager.ClearRooms(); - hasPolledOnce.Value = false; + initialRoomsReceived.Value = false; if (IsLoaded) PollImmediately(); @@ -60,7 +60,7 @@ namespace osu.Game.Screens.OnlinePlay.Components foreach (var incoming in result) RoomManager.AddOrUpdateRoom(incoming); - hasPolledOnce.Value = true; + initialRoomsReceived.Value = true; tcs.SetResult(true); }; diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 4e3f37f814..bd2648791c 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -45,6 +45,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge AutoSizeAxes = Axes.Both }; + protected ListingPollingComponent ListingPollingComponent { get; private set; } + [Resolved] private Bindable selectedRoom { get; set; } @@ -72,7 +74,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private RoomsContainer roomsContainer; private SearchTextBox searchTextBox; private Dropdown statusDropdown; - private ListingPollingComponent listingPollingComponent; [BackgroundDependencyLoader(true)] private void load([CanBeNull] IdleTracker idleTracker) @@ -86,7 +87,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge InternalChildren = new Drawable[] { - listingPollingComponent = CreatePollingComponent(), + ListingPollingComponent = CreatePollingComponent(), loadingLayer = new LoadingLayer(true), new Container { @@ -186,7 +187,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge searchTextBox.Current.BindValueChanged(_ => updateFilterDebounced()); ruleset.BindValueChanged(_ => UpdateFilter()); - listingPollingComponent.HasPolledOnce.BindValueChanged(_ => updateLoadingLayer()); + ListingPollingComponent.InitialRoomsReceived.BindValueChanged(_ => updateLoadingLayer()); isIdle.BindValueChanged(_ => updatePollingRate(this.IsCurrentScreen()), true); @@ -337,7 +338,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private void updateLoadingLayer() { - if (operationInProgress.Value || !listingPollingComponent.HasPolledOnce.Value) + if (operationInProgress.Value || !ListingPollingComponent.InitialRoomsReceived.Value) loadingLayer.Show(); else loadingLayer.Hide(); @@ -346,11 +347,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private void updatePollingRate(bool isCurrentScreen) { if (!isCurrentScreen) - listingPollingComponent.TimeBetweenPolls.Value = 0; + ListingPollingComponent.TimeBetweenPolls.Value = 0; else - listingPollingComponent.TimeBetweenPolls.Value = isIdle.Value ? 120000 : 15000; + ListingPollingComponent.TimeBetweenPolls.Value = isIdle.Value ? 120000 : 15000; - Logger.Log($"Polling adjusted (listing: {listingPollingComponent.TimeBetweenPolls.Value})"); + Logger.Log($"Polling adjusted (listing: {ListingPollingComponent.TimeBetweenPolls.Value})"); } protected abstract OsuButton CreateNewRoomButton(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs index 77db955f0a..97fed2040d 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private MultiplayerClient client { get; set; } - private MultiplayerListingPollingComponent listingPollingComponent; + private MultiplayerListingPollingComponent multiplayerListingPollingComponent => (MultiplayerListingPollingComponent)ListingPollingComponent; private readonly IBindable isConnected = new Bindable(); @@ -34,8 +34,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer base.LoadComplete(); isConnected.BindTo(client.IsConnected); - isConnected.BindValueChanged(c => Scheduler.AddOnce(() => listingPollingComponent.AllowPolling = c.NewValue)); - listingPollingComponent.AllowPolling = isConnected.Value; + isConnected.BindValueChanged(c => Scheduler.AddOnce(() => multiplayerListingPollingComponent.AllowPolling = c.NewValue)); + multiplayerListingPollingComponent.AllowPolling = isConnected.Value; } public override void OnResuming(IScreen last) @@ -47,7 +47,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer if (last is MultiplayerMatchSubScreen match) { RoomManager.RemoveRoom(match.Room); - listingPollingComponent.PollImmediately(); + multiplayerListingPollingComponent.PollImmediately(); } } @@ -69,7 +69,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override RoomSubScreen CreateRoomSubScreen(Room room) => new MultiplayerMatchSubScreen(room); - protected override ListingPollingComponent CreatePollingComponent() => listingPollingComponent = new MultiplayerListingPollingComponent(); + protected override ListingPollingComponent CreatePollingComponent() => new MultiplayerListingPollingComponent(); protected override void OpenNewRoom(Room room) { @@ -104,7 +104,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } } - protected override Task Poll() => !AllowPolling ? Task.CompletedTask : base.Poll(); + protected override Task Poll() => AllowPolling ? base.Poll() : Task.CompletedTask; } } } From 53c3eccfb50bfc6bd3238fa84e2c371a83b4a388 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 16 Aug 2021 07:21:37 +0300 Subject: [PATCH 1183/2442] Force HUD visibility mode to "Always" during testing --- .../Multiplayer/TestSceneMultiSpectatorScreen.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index 9bb9c24c6b..18e4a6c575 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; using osu.Game.Rulesets.UI; @@ -26,6 +27,9 @@ namespace osu.Game.Tests.Visual.Multiplayer [Resolved] private OsuGameBase game { get; set; } + [Resolved] + private OsuConfigManager config { get; set; } + [Resolved] private BeatmapManager beatmapManager { get; set; } @@ -86,6 +90,11 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestSpectatorPlayerInteractiveElementsHidden() { + HUDVisibilityMode originalConfigValue = default; + + AddStep("get original config hud visibility", () => originalConfigValue = config.Get(OsuSetting.HUDVisibilityMode)); + AddStep("set config hud visibility to always", () => config.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always)); + start(new[] { PLAYER_1_ID, PLAYER_2_ID }); loadSpectateScreen(false); @@ -100,6 +109,8 @@ namespace osu.Game.Tests.Visual.Multiplayer !p.ChildrenOfType().Any() && !p.ChildrenOfType().Any() && p.ChildrenOfType().SingleOrDefault()?.ShowHandle == false)); + + AddStep("restore config hud visibility", () => config.SetValue(OsuSetting.HUDVisibilityMode, originalConfigValue)); } [Test] From f82ed64aa7afc735f7092b96810c66eb45222b9d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 16 Aug 2021 09:06:56 +0300 Subject: [PATCH 1184/2442] Fix participant panel null user test no longer functioning properly I guess the changes that involved `MultiplayerTestScene` having a test user lookup cache caused this test case to false-pass silently. Added an explicit assert which ensures the added user indeed has a null `User` value. --- .../TestSceneMultiplayerParticipantsList.cs | 4 +++- .../Multiplayer/TestMultiplayerClient.cs | 2 +- osu.Game/Tests/Visual/TestUserLookupCache.cs | 20 +++++++++++++++---- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs index a3e6c8de3b..a44ce87738 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Testing; @@ -48,7 +49,8 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddAssert("one unique panel", () => this.ChildrenOfType().Select(p => p.User).Distinct().Count() == 1); - AddStep("add non-resolvable user", () => Client.AddNullUser(-3)); + AddStep("add non-resolvable user", () => Client.AddNullUser()); + AddAssert("null user added", () => Client.Room.AsNonNull().Users.Count(u => u.User == null) == 1); AddUntilStep("two unique panels", () => this.ChildrenOfType().Select(p => p.User).Distinct().Count() == 2); } diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 67b79d7390..f2da66d666 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -61,7 +61,7 @@ namespace osu.Game.Tests.Visual.Multiplayer return roomUser; } - public void AddNullUser(int userId) => ((IMultiplayerClient)this).UserJoined(new MultiplayerRoomUser(userId)); + public void AddNullUser() => ((IMultiplayerClient)this).UserJoined(new MultiplayerRoomUser(TestUserLookupCache.NULL_USER_ID)); public void RemoveUser(User user) { diff --git a/osu.Game/Tests/Visual/TestUserLookupCache.cs b/osu.Game/Tests/Visual/TestUserLookupCache.cs index d2941b5bd5..b73e81d0dd 100644 --- a/osu.Game/Tests/Visual/TestUserLookupCache.cs +++ b/osu.Game/Tests/Visual/TestUserLookupCache.cs @@ -10,10 +10,22 @@ namespace osu.Game.Tests.Visual { public class TestUserLookupCache : UserLookupCache { - protected override Task ComputeValueAsync(int lookup, CancellationToken token = default) => Task.FromResult(new User + /// + /// A special user ID which would return a for. + /// As a simulation to what a regular would return in the case of failing to fetch the user. + /// + public const int NULL_USER_ID = -1; + + protected override Task ComputeValueAsync(int lookup, CancellationToken token = default) { - Id = lookup, - Username = $"User {lookup}" - }); + if (lookup == NULL_USER_ID) + return Task.FromResult((User)null); + + return Task.FromResult(new User + { + Id = lookup, + Username = $"User {lookup}" + }); + } } } From 67bac207cfce01649c201721cfca1d595a388ff8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 16 Aug 2021 09:37:29 +0300 Subject: [PATCH 1185/2442] Cover kicking a multiplayer room user with null `User` --- .../Multiplayer/TestSceneMultiplayerParticipantsList.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs index a44ce87738..c4ebc13245 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs @@ -53,6 +53,11 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("null user added", () => Client.Room.AsNonNull().Users.Count(u => u.User == null) == 1); AddUntilStep("two unique panels", () => this.ChildrenOfType().Select(p => p.User).Distinct().Count() == 2); + + AddStep("kick null user", () => this.ChildrenOfType().Single(p => p.User.User == null) + .ChildrenOfType().Single().TriggerClick()); + + AddAssert("null user kicked", () => Client.Room.AsNonNull().Users.Count == 1); } [Test] From 79cd06278426fbc73c7f90f044e25a2047bed488 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 16 Aug 2021 09:26:00 +0300 Subject: [PATCH 1186/2442] Let `TeamDisplay` take the full `MultiplayerRoomUser` rather than the underlying `User` --- .../Participants/ParticipantPanel.cs | 2 +- .../Multiplayer/Participants/TeamDisplay.cs | 17 +++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs index 1787480e1f..c4b4dbb777 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs @@ -83,7 +83,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants Colour = Color4Extensions.FromHex("#F7E65D"), Alpha = 0 }, - new TeamDisplay(user), + new TeamDisplay(User), new Container { RelativeSizeAxes = Axes.Both, diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/TeamDisplay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/TeamDisplay.cs index 5a7073f9de..351b9b3673 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/TeamDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/TeamDisplay.cs @@ -11,7 +11,6 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; -using osu.Game.Users; using osuTK; using osuTK.Graphics; @@ -19,16 +18,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants { internal class TeamDisplay : MultiplayerRoomComposite { - private readonly User user; + private readonly MultiplayerRoomUser user; + private Drawable box; [Resolved] private OsuColour colours { get; set; } - [Resolved] - private MultiplayerClient client { get; set; } - - public TeamDisplay(User user) + public TeamDisplay(MultiplayerRoomUser user) { this.user = user; @@ -61,7 +58,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants } }; - if (user.Id == client.LocalUser?.UserID) + if (Client.LocalUser?.Equals(user) == true) { InternalChild = new OsuClickableContainer { @@ -79,9 +76,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants private void changeTeam() { - client.SendMatchRequest(new ChangeTeamRequest + Client.SendMatchRequest(new ChangeTeamRequest { - TeamID = ((client.LocalUser?.MatchState as TeamVersusUserState)?.TeamID + 1) % 2 ?? 0, + TeamID = ((Client.LocalUser?.MatchState as TeamVersusUserState)?.TeamID + 1) % 2 ?? 0, }); } @@ -93,7 +90,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants // we don't have a way of knowing when an individual user's state has updated, so just handle on RoomUpdated for now. - var userRoomState = Room?.Users.FirstOrDefault(u => u.UserID == user.Id)?.MatchState; + var userRoomState = Room?.Users.FirstOrDefault(u => u.Equals(user))?.MatchState; const double duration = 400; From 7fe6f6dd149c97a6c0a6bae80ecdcac66691543d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 16 Aug 2021 09:26:45 +0300 Subject: [PATCH 1187/2442] Fix kick button action asserting and using `User.User.ID` rather than `User.UserID` --- .../Multiplayer/Participants/ParticipantPanel.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs index c4b4dbb777..6f8c735b6e 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; @@ -168,12 +167,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants Origin = Anchor.Centre, Alpha = 0, Margin = new MarginPadding(4), - Action = () => - { - Debug.Assert(user != null); - - Client.KickUser(user.Id); - } + Action = () => Client.KickUser(User.UserID), }, }, } From 81480ac4fc5ea2e14b7790284e8eec3528329bd5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Aug 2021 16:16:02 +0900 Subject: [PATCH 1188/2442] Use `PlayerConfiguration` to convey no-seek state --- .../Multiplayer/Spectate/MultiSpectatorPlayer.cs | 4 +--- osu.Game/Screens/Play/Player.cs | 8 ++++---- osu.Game/Screens/Play/PlayerConfiguration.cs | 5 +++++ osu.Game/Screens/Play/SpectatorPlayer.cs | 3 ++- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs index 1b1dee5ae2..feb1af770b 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate /// The score containing the player's replay. /// The clock controlling the gameplay running state. public MultiSpectatorPlayer([NotNull] Score score, [NotNull] ISpectatorPlayerClock spectatorPlayerClock) - : base(score) + : base(score, new PlayerConfiguration { AllowSeeking = false }) { this.spectatorPlayerClock = spectatorPlayerClock; } @@ -35,8 +35,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate { spectatorPlayerClock.WaitingOnFrames.BindTo(waitingOnFrames); - AllowUserSeekingState.Value = false; - AllowUserSeekingState.Disabled = true; HUDOverlay.PlayerSettingsOverlay.Expire(); HUDOverlay.HoldToQuit.Expire(); } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index dc37464a61..73bf3f9bda 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -77,9 +77,9 @@ namespace osu.Game.Screens.Play protected readonly Bindable LocalUserPlaying = new Bindable(); - protected readonly Bindable AllowUserSeekingState = new Bindable(); + private readonly Bindable allowUserSeeking = new Bindable(); - public IBindable AllowUserSeeking => AllowUserSeekingState; + public IBindable AllowUserSeeking => allowUserSeeking; public int RestartCount; @@ -275,8 +275,8 @@ namespace osu.Game.Screens.Play DrawableRuleset.HasReplayLoaded.BindValueChanged(r => { - if (!AllowUserSeekingState.Disabled) - AllowUserSeekingState.Value = r.NewValue; + if (Configuration.AllowSeeking) + allowUserSeeking.Value = r.NewValue; updateGameplayState(); }); diff --git a/osu.Game/Screens/Play/PlayerConfiguration.cs b/osu.Game/Screens/Play/PlayerConfiguration.cs index 18ee73374f..39c7a7568e 100644 --- a/osu.Game/Screens/Play/PlayerConfiguration.cs +++ b/osu.Game/Screens/Play/PlayerConfiguration.cs @@ -20,6 +20,11 @@ namespace osu.Game.Screens.Play /// public bool AllowRestart { get; set; } = true; + /// + /// Whether the player should be allowed to seek in a displayed replay. + /// + public bool AllowSeeking { get; set; } = true; + /// /// Whether the player should be allowed to skip intros/outros, advancing to the start of gameplay or the end of a storyboard. /// diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index f662a479ec..1dae28092a 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -23,7 +23,8 @@ namespace osu.Game.Screens.Play protected override bool CheckModsAllowFailure() => false; // todo: better support starting mid-way through beatmap - public SpectatorPlayer(Score score) + public SpectatorPlayer(Score score, PlayerConfiguration configuration = null) + : base(configuration) { this.score = score; } From 838bcc51b2922f6403a8e459aafe8d8ecde41a4a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Aug 2021 16:27:19 +0900 Subject: [PATCH 1189/2442] Avoid new bindable requirement --- osu.Game/Screens/Play/Player.cs | 20 ++++++++------------ osu.Game/Screens/Play/SongProgress.cs | 3 ++- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 73bf3f9bda..c43b701ebf 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -77,10 +77,6 @@ namespace osu.Game.Screens.Play protected readonly Bindable LocalUserPlaying = new Bindable(); - private readonly Bindable allowUserSeeking = new Bindable(); - - public IBindable AllowUserSeeking => allowUserSeeking; - public int RestartCount; [Resolved] @@ -273,13 +269,7 @@ namespace osu.Game.Screens.Play DrawableRuleset.FrameStableClock.IsCatchingUp.BindValueChanged(_ => updateSampleDisabledState()); - DrawableRuleset.HasReplayLoaded.BindValueChanged(r => - { - if (Configuration.AllowSeeking) - allowUserSeeking.Value = r.NewValue; - - updateGameplayState(); - }); + DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updateGameplayState()); // bind clock into components that require it DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused); @@ -592,7 +582,13 @@ namespace osu.Game.Screens.Play /// Seek to a specific time in gameplay. /// /// The destination time to seek to. - public void Seek(double time) => GameplayClockContainer.Seek(time); + public void Seek(double time) + { + if (!Configuration.AllowSeeking) + throw new InvalidOperationException($"Seeking has ben disabled by the current {nameof(Configuration)}."); + + GameplayClockContainer.Seek(time); + } private ScheduledDelegate frameStablePlaybackResetDelegate; diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index 8debe6243d..3e30cf17b3 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -119,7 +119,8 @@ namespace osu.Game.Screens.Play if (drawableRuleset != null) { - ((IBindable)AllowSeeking).BindTo(player.AllowUserSeeking); + if (player?.Configuration.AllowSeeking == true) + ((IBindable)AllowSeeking).BindTo(drawableRuleset.HasReplayLoaded); referenceClock = drawableRuleset.FrameStableClock; Objects = drawableRuleset.Objects; From ae8a1adae838648807431fec10b41826abbd9849 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Aug 2021 16:47:57 +0900 Subject: [PATCH 1190/2442] Allow seeking via `Player.Seek` even if disabled --- osu.Game/Screens/Play/Player.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index c43b701ebf..09eaf1c543 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -582,13 +582,7 @@ namespace osu.Game.Screens.Play /// Seek to a specific time in gameplay. /// /// The destination time to seek to. - public void Seek(double time) - { - if (!Configuration.AllowSeeking) - throw new InvalidOperationException($"Seeking has ben disabled by the current {nameof(Configuration)}."); - - GameplayClockContainer.Seek(time); - } + public void Seek(double time) => GameplayClockContainer.Seek(time); private ScheduledDelegate frameStablePlaybackResetDelegate; From 8d45f86bd3ded0bb0b7c901c3fb30e1b1f7fff93 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Aug 2021 16:48:40 +0900 Subject: [PATCH 1191/2442] Rename variable to better reflect its purpose --- .../OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs | 2 +- osu.Game/Screens/Play/PlayerConfiguration.cs | 4 ++-- osu.Game/Screens/Play/SongProgress.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs index feb1af770b..ececa1e497 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate /// The score containing the player's replay. /// The clock controlling the gameplay running state. public MultiSpectatorPlayer([NotNull] Score score, [NotNull] ISpectatorPlayerClock spectatorPlayerClock) - : base(score, new PlayerConfiguration { AllowSeeking = false }) + : base(score, new PlayerConfiguration { AllowUserInteraction = false }) { this.spectatorPlayerClock = spectatorPlayerClock; } diff --git a/osu.Game/Screens/Play/PlayerConfiguration.cs b/osu.Game/Screens/Play/PlayerConfiguration.cs index 39c7a7568e..3aa424e5d5 100644 --- a/osu.Game/Screens/Play/PlayerConfiguration.cs +++ b/osu.Game/Screens/Play/PlayerConfiguration.cs @@ -21,9 +21,9 @@ namespace osu.Game.Screens.Play public bool AllowRestart { get; set; } = true; /// - /// Whether the player should be allowed to seek in a displayed replay. + /// Whether the player should be able to interact with this player instance. /// - public bool AllowSeeking { get; set; } = true; + public bool AllowUserInteraction { get; set; } = true; /// /// Whether the player should be allowed to skip intros/outros, advancing to the start of gameplay or the end of a storyboard. diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index 3e30cf17b3..b27a9c5f5d 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -119,7 +119,7 @@ namespace osu.Game.Screens.Play if (drawableRuleset != null) { - if (player?.Configuration.AllowSeeking == true) + if (player?.Configuration.AllowUserInteraction == true) ((IBindable)AllowSeeking).BindTo(drawableRuleset.HasReplayLoaded); referenceClock = drawableRuleset.FrameStableClock; From fc89f2bac4a3971835beeeb104a10026dfb13782 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Aug 2021 16:56:48 +0900 Subject: [PATCH 1192/2442] Revert "Rename element in OsuSettings enum" This reverts commit c2bbe175627ce35f1d63bc7c627b35b4883de5a0. --- osu.Game/Configuration/OsuConfigManager.cs | 4 ++-- .../Overlays/Settings/Sections/Gameplay/GeneralSettings.cs | 2 +- osu.Game/Screens/Play/SongProgress.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 6c7adcc806..60a0d5a0ac 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -101,7 +101,7 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.HitLighting, true); SetDefault(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always); - SetDefault(OsuSetting.ShowDifficultyGraph, true); + SetDefault(OsuSetting.ShowProgressGraph, true); SetDefault(OsuSetting.ShowHealthDisplayWhenCantFail, true); SetDefault(OsuSetting.FadePlayfieldWhenHealthLow, true); SetDefault(OsuSetting.KeyOverlay, false); @@ -217,7 +217,7 @@ namespace osu.Game.Configuration AlwaysPlayFirstComboBreak, FloatingComments, HUDVisibilityMode, - ShowDifficultyGraph, + ShowProgressGraph, ShowHealthDisplayWhenCantFail, FadePlayfieldWhenHealthLow, MouseDisableButtons, diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index 69aa57082a..353292606f 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -46,7 +46,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay new SettingsCheckbox { LabelText = "Show difficulty graph on progress bar", - Current = config.GetBindable(OsuSetting.ShowDifficultyGraph) + Current = config.GetBindable(OsuSetting.ShowProgressGraph) }, new SettingsCheckbox { diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index 6aa7e017ce..f28622f42e 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -125,7 +125,7 @@ namespace osu.Game.Screens.Play Objects = drawableRuleset.Objects; } - config.BindWith(OsuSetting.ShowDifficultyGraph, ShowGraph); + config.BindWith(OsuSetting.ShowProgressGraph, ShowGraph); graph.FillColour = bar.FillColour = colours.BlueLighter; } From 71fab416d8902bbf6b1657de800773e0f78fa2bc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Aug 2021 16:59:59 +0900 Subject: [PATCH 1193/2442] Add a note against `OsuSetting` --- osu.Game/Configuration/OsuConfigManager.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 60a0d5a0ac..9b0d7f51da 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -201,6 +201,8 @@ namespace osu.Game.Configuration public Func LookupKeyBindings { get; set; } } + // IMPORTANT: These are used in user configuration files. + // The naming of these keys should not be changed once they are deployed in a release, unless migration logic is also added. public enum OsuSetting { Ruleset, From 1c7cbc862156659d31651b3cfb748d4490e6ac6b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Aug 2021 17:14:13 +0900 Subject: [PATCH 1194/2442] Add missing `readonly` keyword to new bindable --- osu.Game/Screens/Edit/EditorBeatmapSkin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/EditorBeatmapSkin.cs b/osu.Game/Screens/Edit/EditorBeatmapSkin.cs index 6745f08b80..429df85904 100644 --- a/osu.Game/Screens/Edit/EditorBeatmapSkin.cs +++ b/osu.Game/Screens/Edit/EditorBeatmapSkin.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.Edit /// The combo colours of this skin. /// If empty, the default combo colours will be used. /// - public BindableList ComboColours; + public readonly BindableList ComboColours; private readonly Skin skin; From db1a0ebb55448903587ce59bbdf99c5ed90744a5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 16 Aug 2021 18:46:44 +0900 Subject: [PATCH 1195/2442] Fix preview track crashes --- .../BeatmapListing/Panels/PlayButton.cs | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs index 4bbc3569fe..3aa9aa5ca5 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs @@ -139,19 +139,24 @@ namespace osu.Game.Overlays.BeatmapListing.Panels LoadComponentAsync(Preview = previewTrackManager.Get(beatmapSet), preview => { - // beatmapset may have changed. - if (Preview != preview) - return; + // Make sure that we schedule to after the next audio frame to fix crashes in single-threaded execution. + // See: https://github.com/ppy/osu-framework/issues/4692 + Schedule(() => + { + // beatmapset may have changed. + if (Preview != preview) + return; - AddInternal(preview); - loading = false; - // make sure that the update of value of Playing (and the ensuing value change callbacks) - // are marshaled back to the update thread. - preview.Stopped += () => Schedule(() => playing.Value = false); + AddInternal(preview); + loading = false; + // make sure that the update of value of Playing (and the ensuing value change callbacks) + // are marshaled back to the update thread. + preview.Stopped += () => Schedule(() => playing.Value = false); - // user may have changed their mind. - if (playing.Value) - attemptStart(); + // user may have changed their mind. + if (playing.Value) + attemptStart(); + }); }); } else From 855fff1486477eb490ed851a33a26be57b96c095 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 16 Aug 2021 12:47:56 +0300 Subject: [PATCH 1196/2442] Fix `DifficultyAdjustSettingsControl.SliderControl` not following up with the current pattern This was causing any `ValueChanged` event bind (such as the one in `SettingsItem` to invoke `SettingsChanged`) to be overwritten when `Current` is set afterwards. --- osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs index 067657159b..186514e868 100644 --- a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs +++ b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs @@ -91,7 +91,13 @@ namespace osu.Game.Rulesets.Mods { // This is required as SettingsItem relies heavily on this bindable for internal use. // The actual update flow is done via the bindable provided in the constructor. - public Bindable Current { get; set; } = new Bindable(); + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + public Bindable Current + { + get => current.Current; + set => current.Current = value; + } public SliderControl(BindableNumber currentNumber) { From 6653a78e659774213635568d5fd2bdcc7d693170 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 16 Aug 2021 12:48:49 +0300 Subject: [PATCH 1197/2442] Add test coverage --- .../TestSceneModDifficultyAdjustSettings.cs | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs index e0d76b3e4a..f8652573f4 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModDifficultyAdjustSettings.cs @@ -1,8 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -174,6 +176,60 @@ namespace osu.Game.Tests.Visual.UserInterface checkBindableAtValue("Circle Size", null); } + [Test] + public void TestModSettingChangeTracker() + { + ModSettingChangeTracker tracker = null; + Queue settingsChangedQueue = null; + + setBeatmapWithDifficultyParameters(5); + + AddStep("add mod settings change tracker", () => + { + settingsChangedQueue = new Queue(); + + tracker = new ModSettingChangeTracker(modDifficultyAdjust.Yield()) + { + SettingChanged = settingsChangedQueue.Enqueue + }; + }); + + AddAssert("no settings changed", () => settingsChangedQueue.Count == 0); + + setSliderValue("Circle Size", 3); + + settingsChangedFired(); + + setSliderValue("Circle Size", 5); + checkBindableAtValue("Circle Size", 5); + + settingsChangedFired(); + + AddStep("reset mod settings", () => modDifficultyAdjust.CircleSize.SetDefault()); + checkBindableAtValue("Circle Size", null); + + settingsChangedFired(); + + setExtendedLimits(true); + + settingsChangedFired(); + + AddStep("dispose tracker", () => + { + tracker.Dispose(); + tracker = null; + }); + + void settingsChangedFired() + { + AddAssert("setting changed event fired", () => + { + settingsChangedQueue.Dequeue(); + return settingsChangedQueue.Count == 0; + }); + } + } + private void resetToDefault(string name) { AddStep($"Reset {name} to default", () => From c191b3812529e9fdd0f47488fd22e21e6398ff9b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Aug 2021 18:40:34 +0900 Subject: [PATCH 1198/2442] Reduce transform overhead of `RestoreDefaultValueButton` --- osu.Game/Overlays/RestoreDefaultValueButton.cs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/RestoreDefaultValueButton.cs b/osu.Game/Overlays/RestoreDefaultValueButton.cs index fd3ee16fe6..62f5222012 100644 --- a/osu.Game/Overlays/RestoreDefaultValueButton.cs +++ b/osu.Game/Overlays/RestoreDefaultValueButton.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osuTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.UserInterface; @@ -38,7 +39,9 @@ namespace osu.Game.Overlays current.ValueChanged += _ => UpdateState(); current.DefaultChanged += _ => UpdateState(); current.DisabledChanged += _ => UpdateState(); - UpdateState(); + + if (IsLoaded) + UpdateState(); } } @@ -81,7 +84,10 @@ namespace osu.Game.Overlays protected override void LoadComplete() { base.LoadComplete(); - UpdateState(); + + // avoid unnecessary transforms on first display. + Alpha = currentAlpha; + Colour = currentColour; } public LocalisableString TooltipText => "revert to default"; @@ -101,14 +107,16 @@ namespace osu.Game.Overlays public void UpdateState() => Scheduler.AddOnce(updateState); + private float currentAlpha => current.IsDefault ? 0f : hovering && !current.Disabled ? 1f : 0.65f; + private ColourInfo currentColour => current.Disabled ? Color4.Gray : buttonColour; + private void updateState() { if (current == null) return; - this.FadeTo(current.IsDefault ? 0f : - hovering && !current.Disabled ? 1f : 0.65f, 200, Easing.OutQuint); - this.FadeColour(current.Disabled ? Color4.Gray : buttonColour, 200, Easing.OutQuint); + this.FadeTo(currentAlpha, 200, Easing.OutQuint); + this.FadeColour(currentColour, 200, Easing.OutQuint); } } } From 4b975ca10d34211f6555ff0e98b49c99f23981cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Aug 2021 18:43:02 +0900 Subject: [PATCH 1199/2442] Add better test coverage of `SettingsPanel` --- .../Visual/Settings/TestSceneSettingsPanel.cs | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs index 115d2fec7d..0af77b3b5a 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs @@ -4,6 +4,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; using osu.Game.Overlays; namespace osu.Game.Tests.Visual.Settings @@ -11,27 +12,39 @@ namespace osu.Game.Tests.Visual.Settings [TestFixture] public class TestSceneSettingsPanel : OsuTestScene { - private readonly SettingsPanel settings; - private readonly DialogOverlay dialogOverlay; + private SettingsPanel settings; + private DialogOverlay dialogOverlay; - public TestSceneSettingsPanel() + [SetUpSteps] + public void SetUpSteps() { - settings = new SettingsOverlay + AddStep("create settings", () => { - State = { Value = Visibility.Visible } - }; - Add(dialogOverlay = new DialogOverlay - { - Depth = -1 + settings?.Expire(); + + Add(settings = new SettingsOverlay + { + State = { Value = Visibility.Visible } + }); }); } + [Test] + public void ToggleVisibility() + { + AddWaitStep("wait some", 5); + AddToggleStep("toggle editor visibility", visible => settings.ToggleVisibility()); + } + [BackgroundDependencyLoader] private void load() { - Dependencies.Cache(dialogOverlay); + Add(dialogOverlay = new DialogOverlay + { + Depth = -1 + }); - Add(settings); + Dependencies.Cache(dialogOverlay); } } } From cecb312e7703e1ebacf023a13db78159607b9407 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Aug 2021 18:44:00 +0900 Subject: [PATCH 1200/2442] Ensure `TakeFocus` does not crash when not yet loaded --- osu.Game/Graphics/UserInterface/FocusedTextBox.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs index f77a3109c9..4a42027964 100644 --- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs +++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs @@ -22,7 +22,10 @@ namespace osu.Game.Graphics.UserInterface public void TakeFocus() { - if (allowImmediateFocus) GetContainingInputManager().ChangeFocus(this); + if (!allowImmediateFocus) + return; + + Schedule(() => GetContainingInputManager().ChangeFocus(this)); } public bool HoldFocus From e39a295c5c417ec6d71ded0f07b1029849436c89 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 16 Aug 2021 13:45:50 +0300 Subject: [PATCH 1201/2442] Hide tablet settings content when input handler is disabled --- .../Settings/Sections/Input/TabletSettings.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index c561b693d8..8303fee3ef 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -22,6 +22,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input { private readonly ITabletHandler tabletHandler; + private readonly Bindable enabled = new BindableBool(true); + private readonly Bindable areaOffset = new Bindable(); private readonly Bindable areaSize = new Bindable(); private readonly IBindable tablet = new Bindable(); @@ -74,7 +76,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input LabelText = CommonStrings.Enabled, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Current = tabletHandler.Enabled + Current = enabled, }, noTabletMessage = new FillFlowContainer { @@ -194,6 +196,9 @@ namespace osu.Game.Overlays.Settings.Sections.Input { base.LoadComplete(); + enabled.BindTo(tabletHandler.Enabled); + enabled.BindValueChanged(_ => Scheduler.AddOnce(updateVisibility)); + rotation.BindTo(tabletHandler.Rotation); areaOffset.BindTo(tabletHandler.AreaOffset); @@ -239,7 +244,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input tablet.BindTo(tabletHandler.Tablet); tablet.BindValueChanged(val => { - Scheduler.AddOnce(toggleVisibility); + Scheduler.AddOnce(updateVisibility); var tab = val.NewValue; @@ -259,8 +264,15 @@ namespace osu.Game.Overlays.Settings.Sections.Input }, true); } - private void toggleVisibility() + private void updateVisibility() { + if (!tabletHandler.Enabled.Value) + { + mainSettings.Hide(); + noTabletMessage.Hide(); + return; + } + bool tabletFound = tablet.Value != null; if (!tabletFound) From 7bebbf9f7400e068b1378e0b6b7eec0711c8d14f Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 16 Aug 2021 12:46:41 +0200 Subject: [PATCH 1202/2442] Mark format strings as verbatim. --- osu.Game/Overlays/BeatmapSet/BasicStats.cs | 4 ++-- osu.Game/Overlays/BeatmapSet/SuccessRate.cs | 2 +- osu.Game/Screens/Select/Details/UserRatings.cs | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/BasicStats.cs b/osu.Game/Overlays/BeatmapSet/BasicStats.cs index ce348bd753..ed7676aa21 100644 --- a/osu.Game/Overlays/BeatmapSet/BasicStats.cs +++ b/osu.Game/Overlays/BeatmapSet/BasicStats.cs @@ -67,8 +67,8 @@ namespace osu.Game.Overlays.BeatmapSet length.TooltipText = BeatmapsetsStrings.ShowStatsTotalLength(TimeSpan.FromMilliseconds(beatmap.Length).ToFormattedDuration()); length.Value = TimeSpan.FromMilliseconds(beatmap.Length).ToFormattedDuration(); - circleCount.Value = beatmap.OnlineInfo.CircleCount.ToLocalisableString("N0"); - sliderCount.Value = beatmap.OnlineInfo.SliderCount.ToLocalisableString("N0"); + circleCount.Value = beatmap.OnlineInfo.CircleCount.ToLocalisableString(@"N0"); + sliderCount.Value = beatmap.OnlineInfo.SliderCount.ToLocalisableString(@"N0"); } } diff --git a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs index d370e57f14..b1e9abe3aa 100644 --- a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs +++ b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs @@ -44,7 +44,7 @@ namespace osu.Game.Overlays.BeatmapSet int playCount = beatmap?.OnlineInfo?.PlayCount ?? 0; var rate = playCount != 0 ? (float)passCount / playCount : 0; - successPercent.Text = rate.ToLocalisableString("0.#%"); + successPercent.Text = rate.ToLocalisableString(@"0.#%"); successRate.Length = rate; percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic); diff --git a/osu.Game/Screens/Select/Details/UserRatings.cs b/osu.Game/Screens/Select/Details/UserRatings.cs index a45bcd0666..a7f28b932a 100644 --- a/osu.Game/Screens/Select/Details/UserRatings.cs +++ b/osu.Game/Screens/Select/Details/UserRatings.cs @@ -37,8 +37,8 @@ namespace osu.Game.Screens.Select.Details if (metrics == null) { - negativeRatings.Text = 0.ToLocalisableString("N0"); - positiveRatings.Text = 0.ToLocalisableString("N0"); + negativeRatings.Text = 0.ToLocalisableString(@"N0"); + positiveRatings.Text = 0.ToLocalisableString(@"N0"); ratingsBar.Length = 0; graph.Values = new float[rating_range]; } @@ -49,8 +49,8 @@ namespace osu.Game.Screens.Select.Details var negativeCount = ratings.Take(rating_range / 2).Sum(); var totalCount = ratings.Sum(); - negativeRatings.Text = negativeCount.ToLocalisableString("N0"); - positiveRatings.Text = (totalCount - negativeCount).ToLocalisableString("N0"); + negativeRatings.Text = negativeCount.ToLocalisableString(@"N0"); + positiveRatings.Text = (totalCount - negativeCount).ToLocalisableString(@"N0"); ratingsBar.Length = totalCount == 0 ? 0 : (float)negativeCount / totalCount; graph.Values = ratings.Take(rating_range).Select(r => (float)r); } @@ -90,14 +90,14 @@ namespace osu.Game.Screens.Select.Details { negativeRatings = new OsuSpriteText { - Text = 0.ToLocalisableString("N0"), + Text = 0.ToLocalisableString(@"N0"), Font = OsuFont.GetFont(size: 12) }, positiveRatings = new OsuSpriteText { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - Text = 0.ToLocalisableString("N0"), + Text = 0.ToLocalisableString(@"N0"), Font = OsuFont.GetFont(size: 12) }, }, From c0130da2359ad8947eebdf1a2743cbc2e31e5765 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Aug 2021 18:44:12 +0900 Subject: [PATCH 1203/2442] Avoid running initial layout transform in `LayoutSettings` --- .../Overlays/Settings/Sections/Graphics/LayoutSettings.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 91208cb78a..277e344d84 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -97,8 +97,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics Direction = FillDirection.Vertical, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - AutoSizeDuration = transition_duration, - AutoSizeEasing = Easing.OutQuint, Masking = true, Children = new[] { @@ -177,6 +175,9 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics scalingMode.BindValueChanged(mode => { scalingSettings.ClearTransforms(); + + scalingSettings.AutoSizeDuration = transition_duration; + scalingSettings.AutoSizeEasing = Easing.OutQuint; scalingSettings.AutoSizeAxes = mode.NewValue != ScalingMode.Off ? Axes.Y : Axes.None; if (mode.NewValue == ScalingMode.Off) From 92ad66c86c857b7854064c6d6e87b6b107d472ca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Aug 2021 18:56:02 +0900 Subject: [PATCH 1204/2442] Remove transform overhead from `OsuDropdown` on initial display --- osu.Game/Graphics/UserInterface/OsuDropdown.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index 61dd5fb2d9..42f628a75a 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -69,6 +69,7 @@ namespace osu.Game.Graphics.UserInterface BackgroundColour = Color4.Black.Opacity(0.5f); MaskingContainer.CornerRadius = corner_radius; + Alpha = 0; // todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring ItemsContainer.Padding = new MarginPadding(5); @@ -94,9 +95,11 @@ namespace osu.Game.Graphics.UserInterface protected override void AnimateClose() { - this.FadeOut(300, Easing.OutQuint); if (wasOpened) + { + this.FadeOut(300, Easing.OutQuint); sampleClose?.Play(); + } } // todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring From 237d3e656b72b2827e04a5228dae4064926a86e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Aug 2021 18:58:26 +0900 Subject: [PATCH 1205/2442] Remove initial transform overhead of `Nub` --- osu.Game/Graphics/UserInterface/Nub.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/Nub.cs b/osu.Game/Graphics/UserInterface/Nub.cs index 8d686e8c2f..cbf3e6b3aa 100644 --- a/osu.Game/Graphics/UserInterface/Nub.cs +++ b/osu.Game/Graphics/UserInterface/Nub.cs @@ -6,6 +6,7 @@ using osuTK; using osuTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; @@ -57,18 +58,13 @@ namespace osu.Game.Graphics.UserInterface EdgeEffect = new EdgeEffectParameters { - Colour = GlowColour, + Colour = GlowColour.Opacity(0), Type = EdgeEffectType.Glow, Radius = 10, Roundness = 8, }; } - protected override void LoadComplete() - { - FadeEdgeEffectTo(0); - } - private bool glowing; public bool Glowing From 8d051d9fa0d6c692bc740ec155d0a1bf1914cc7c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Aug 2021 19:16:48 +0900 Subject: [PATCH 1206/2442] Avoid multiple synchronous overheads in `SettingsItem` --- osu.Game/Overlays/Settings/SettingsItem.cs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index ef2027fdab..c35690151c 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -93,15 +93,13 @@ namespace osu.Game.Overlays.Settings public bool MatchingFilter { - set => this.FadeTo(value ? 1 : 0); + set => Alpha = value ? 1 : 0; } public bool FilteringActive { get; set; } public event Action SettingChanged; - private readonly RestoreDefaultValueButton restoreDefaultButton; - protected SettingsItem() { RelativeSizeAxes = Axes.X; @@ -110,7 +108,6 @@ namespace osu.Game.Overlays.Settings InternalChildren = new Drawable[] { - restoreDefaultButton = new RestoreDefaultValueButton(), FlowContent = new FillFlowContainer { RelativeSizeAxes = Axes.X, @@ -122,7 +119,11 @@ namespace osu.Game.Overlays.Settings }, }, }; + } + [BackgroundDependencyLoader] + private void load() + { // all bindable logic is in constructor intentionally to support "CreateSettingsControls" being used in a context it is // never loaded, but requires bindable storage. if (controlWithCurrent == null) @@ -130,14 +131,15 @@ namespace osu.Game.Overlays.Settings controlWithCurrent.Current.ValueChanged += _ => SettingChanged?.Invoke(); controlWithCurrent.Current.DisabledChanged += _ => updateDisabled(); - } - - protected override void LoadComplete() - { - base.LoadComplete(); + // intentionally done before LoadComplete to avoid overhead. if (ShowsDefaultIndicator) - restoreDefaultButton.Current = controlWithCurrent.Current; + { + AddInternal(new RestoreDefaultValueButton + { + Current = controlWithCurrent.Current, + }); + } } private void updateDisabled() From b541550ea97516b877363c2d0509848450b02a40 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Aug 2021 19:17:36 +0900 Subject: [PATCH 1207/2442] Avoid initial synchronous dropdown population overhead in `AudioDevicesSettings` --- .../Sections/Audio/AudioDevicesSettings.cs | 46 +++++++++---------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs index d64f176468..2354475498 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs @@ -20,17 +20,26 @@ namespace osu.Game.Overlays.Settings.Sections.Audio private SettingsDropdown dropdown; - protected override void Dispose(bool isDisposing) + [BackgroundDependencyLoader] + private void load() { - base.Dispose(isDisposing); - - if (audio != null) + Children = new Drawable[] { - audio.OnNewDevice -= onDeviceChanged; - audio.OnLostDevice -= onDeviceChanged; - } + dropdown = new AudioDeviceSettingsDropdown + { + Keywords = new[] { "speaker", "headphone", "output" } + } + }; + + updateItems(); + + audio.OnNewDevice += onDeviceChanged; + audio.OnLostDevice += onDeviceChanged; + dropdown.Current = audio.AudioDevice; } + private void onDeviceChanged(string name) => updateItems(); + private void updateItems() { var deviceItems = new List { string.Empty }; @@ -49,26 +58,15 @@ namespace osu.Game.Overlays.Settings.Sections.Audio dropdown.Items = deviceItems.Distinct().ToList(); } - private void onDeviceChanged(string name) => updateItems(); - - protected override void LoadComplete() + protected override void Dispose(bool isDisposing) { - base.LoadComplete(); + base.Dispose(isDisposing); - Children = new Drawable[] + if (audio != null) { - dropdown = new AudioDeviceSettingsDropdown - { - Keywords = new[] { "speaker", "headphone", "output" } - } - }; - - updateItems(); - - dropdown.Current = audio.AudioDevice; - - audio.OnNewDevice += onDeviceChanged; - audio.OnLostDevice += onDeviceChanged; + audio.OnNewDevice -= onDeviceChanged; + audio.OnLostDevice -= onDeviceChanged; + } } private class AudioDeviceSettingsDropdown : SettingsDropdown From c6bd8520a7464f43e4ef558fd61d0015c1e1f171 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Aug 2021 19:18:39 +0900 Subject: [PATCH 1208/2442] Add basic asynchronous loading pattern to `SettingsPanel` --- osu.Game/Overlays/SettingsPanel.cs | 93 ++++++++++++++++++++---------- 1 file changed, 64 insertions(+), 29 deletions(-) diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index f1c41c4b50..c191f61e60 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using osuTK; using osuTK.Graphics; @@ -13,6 +14,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; +using osu.Framework.Threading; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; @@ -58,6 +60,8 @@ namespace osu.Game.Overlays private readonly bool showSidebar; + private LoadingLayer loading; + protected SettingsPanel(bool showSidebar) { this.showSidebar = showSidebar; @@ -86,45 +90,69 @@ namespace osu.Game.Overlays Colour = OsuColour.Gray(0.05f), Alpha = 1, }, - SectionsContainer = new SettingsSectionsContainer + loading = new LoadingLayer { - Masking = true, - RelativeSizeAxes = Axes.Both, - ExpandableHeader = CreateHeader(), - FixedHeader = searchTextBox = new SeekLimitedSearchTextBox - { - RelativeSizeAxes = Axes.X, - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Width = 0.95f, - Margin = new MarginPadding - { - Top = 20, - Bottom = 20 - }, - }, - Footer = CreateFooter() - }, + State = { Value = Visibility.Visible } + } } }; + SectionsContainer = new SettingsSectionsContainer + { + Masking = true, + RelativeSizeAxes = Axes.Both, + ExpandableHeader = CreateHeader(), + FixedHeader = searchTextBox = new SeekLimitedSearchTextBox + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Width = 0.95f, + Margin = new MarginPadding + { + Top = 20, + Bottom = 20 + }, + }, + Footer = CreateFooter() + }; + if (showSidebar) { AddInternal(Sidebar = new Sidebar { Width = sidebar_width }); - - SectionsContainer.SelectedSection.ValueChanged += section => - { - selectedSidebarButton.Selected = false; - selectedSidebarButton = Sidebar.Children.Single(b => b.Section == section.NewValue); - selectedSidebarButton.Selected = true; - }; } - searchTextBox.Current.ValueChanged += term => SectionsContainer.SearchContainer.SearchTerm = term.NewValue; - CreateSections()?.ForEach(AddSection); } + private void ensureContentLoaded() + { + if (SectionsContainer.LoadState > LoadState.NotLoaded) + return; + + Debug.Assert(SectionsContainer != null); + + LoadComponentAsync(SectionsContainer, d => + { + ContentContainer.Add(d); + d.FadeInFromZero(500); + loading.Hide(); + + if (Sidebar != null) + { + SectionsContainer.SelectedSection.ValueChanged += section => + { + selectedSidebarButton.Selected = false; + selectedSidebarButton = Sidebar.Children.Single(b => b.Section == section.NewValue); + selectedSidebarButton.Selected = true; + }; + } + + searchTextBox.Current.BindValueChanged(term => SectionsContainer.SearchContainer.SearchTerm = term.NewValue, true); + searchTextBox.TakeFocus(); + }); + } + protected void AddSection(SettingsSection section) { SectionsContainer.Add(section); @@ -136,6 +164,10 @@ namespace osu.Game.Overlays Section = section, Action = () => { + // may not be loaded yet. + if (SectionsContainer == null) + return; + SectionsContainer.ScrollTo(section); Sidebar.State = ExpandedState.Contracted; }, @@ -159,7 +191,8 @@ namespace osu.Game.Overlays { base.PopIn(); - ContentContainer.MoveToX(ExpandedPosition, TRANSITION_LENGTH, Easing.OutQuint); + ContentContainer.MoveToX(ExpandedPosition, TRANSITION_LENGTH, Easing.OutQuint) + .OnComplete(_ => ensureContentLoaded()); Sidebar?.MoveToX(0, TRANSITION_LENGTH, Easing.OutQuint); this.FadeTo(1, TRANSITION_LENGTH, Easing.OutQuint); @@ -187,7 +220,7 @@ namespace osu.Game.Overlays protected override void OnFocus(FocusEvent e) { - searchTextBox.TakeFocus(); + searchTextBox?.TakeFocus(); base.OnFocus(e); } @@ -209,6 +242,8 @@ namespace osu.Game.Overlays { public SearchContainer SearchContainer; + public new ScheduledDelegate Schedule(Action action) => Scheduler.AddDelayed(action, TransformDelay); + protected override FlowContainer CreateScrollContentContainer() => SearchContainer = new SearchContainer { From 230c4eb2475a16c136d001598570d8b4d0c25450 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Aug 2021 19:47:27 +0900 Subject: [PATCH 1209/2442] Fade in sidebar buttons after the load has completed --- osu.Game/Overlays/Settings/SidebarButton.cs | 3 + osu.Game/Overlays/SettingsPanel.cs | 68 +++++++++++++-------- 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/osu.Game/Overlays/Settings/SidebarButton.cs b/osu.Game/Overlays/Settings/SidebarButton.cs index 30a53b351d..cf6a313a1f 100644 --- a/osu.Game/Overlays/Settings/SidebarButton.cs +++ b/osu.Game/Overlays/Settings/SidebarButton.cs @@ -22,6 +22,9 @@ namespace osu.Game.Overlays.Settings private readonly Box selectionIndicator; private readonly Container text; + // always consider as part of flow, even when not visible (for the sake of the initial animation). + public override bool IsPresent => true; + private SettingsSection section; public SettingsSection Section diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index c191f61e60..388c809be8 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -135,54 +135,70 @@ namespace osu.Game.Overlays LoadComponentAsync(SectionsContainer, d => { ContentContainer.Add(d); - d.FadeInFromZero(500); + d.FadeInFromZero(750, Easing.OutQuint); loading.Hide(); - if (Sidebar != null) - { - SectionsContainer.SelectedSection.ValueChanged += section => - { - selectedSidebarButton.Selected = false; - selectedSidebarButton = Sidebar.Children.Single(b => b.Section == section.NewValue); - selectedSidebarButton.Selected = true; - }; - } - searchTextBox.Current.BindValueChanged(term => SectionsContainer.SearchContainer.SearchTerm = term.NewValue, true); searchTextBox.TakeFocus(); + + if (Sidebar == null) + return; + + LoadComponentsAsync(createSidebarButtons(), buttons => + { + float delay = 0; + + foreach (var button in buttons) + { + Sidebar.Add(button); + + button.FadeOut() + .Delay(delay) + .FadeInFromZero(1000, Easing.OutQuint); + + delay += 30; + } + + SectionsContainer.SelectedSection.BindValueChanged(section => + { + if (selectedSidebarButton != null) + selectedSidebarButton.Selected = false; + + selectedSidebarButton = Sidebar.Children.Single(b => b.Section == section.NewValue); + selectedSidebarButton.Selected = true; + }, true); + }); }); } - protected void AddSection(SettingsSection section) + private IEnumerable createSidebarButtons() { - SectionsContainer.Add(section); - - if (Sidebar != null) + foreach (var section in SectionsContainer) { - var button = new SidebarButton + yield return new SidebarButton { Section = section, Action = () => { - // may not be loaded yet. - if (SectionsContainer == null) + if (!SectionsContainer.IsLoaded) return; SectionsContainer.ScrollTo(section); Sidebar.State = ExpandedState.Contracted; }, }; - - Sidebar.Add(button); - - if (selectedSidebarButton == null) - { - selectedSidebarButton = Sidebar.Children.First(); - selectedSidebarButton.Selected = true; - } } } + protected void AddSection(SettingsSection section) + { + if (IsLoaded) + // just to keep things simple. can be accommodated for if we ever need it. + throw new InvalidOperationException("All sections must be added before the panel is loaded."); + + SectionsContainer.Add(section); + } + protected virtual Drawable CreateHeader() => new Container(); protected virtual Drawable CreateFooter() => new Container(); From e485728109869c1e3704056e1c2b9c98708c43d1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Aug 2021 19:54:45 +0900 Subject: [PATCH 1210/2442] Add keywords to make finding audio offset adjustments easier in settings --- osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs index 7f2e377c83..eaa557bb37 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.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.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Localisation; @@ -13,6 +15,8 @@ namespace osu.Game.Overlays.Settings.Sections.Audio { protected override LocalisableString Header => "Offset Adjustment"; + public override IEnumerable FilterTerms => base.FilterTerms.Concat(new[] { "universal", "uo", "timing" }); + [BackgroundDependencyLoader] private void load(OsuConfigManager config) { From de61cb8e6a9a2fb22263098f6ec779360437adce Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Aug 2021 19:51:13 +0900 Subject: [PATCH 1211/2442] Adjust delay slightly --- osu.Game/Overlays/SettingsPanel.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index 388c809be8..6593b6cb1e 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using osuTK; using osuTK.Graphics; @@ -130,8 +129,6 @@ namespace osu.Game.Overlays if (SectionsContainer.LoadState > LoadState.NotLoaded) return; - Debug.Assert(SectionsContainer != null); - LoadComponentAsync(SectionsContainer, d => { ContentContainer.Add(d); @@ -207,8 +204,13 @@ namespace osu.Game.Overlays { base.PopIn(); - ContentContainer.MoveToX(ExpandedPosition, TRANSITION_LENGTH, Easing.OutQuint) - .OnComplete(_ => ensureContentLoaded()); + ContentContainer.MoveToX(ExpandedPosition, TRANSITION_LENGTH, Easing.OutQuint); + + // delay load enough to ensure it doesn't overlap with the initial animation. + // this is done as there is still a brief stutter during load completion which is more visible if the transition is in progress. + // the eventual goal would be to remove the need for this by splitting up load into smaller work pieces, or fixing the remaining + // load complete overheads. + Scheduler.AddDelayed(ensureContentLoaded, TRANSITION_LENGTH / 3); Sidebar?.MoveToX(0, TRANSITION_LENGTH, Easing.OutQuint); this.FadeTo(1, TRANSITION_LENGTH, Easing.OutQuint); From 1f942d15f825f6b5db0378e092fdc00c508f86b3 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 16 Aug 2021 13:38:57 +0200 Subject: [PATCH 1212/2442] Localise scoreboard --- .../BeatmapSet/Scores/NoScoresPlaceholder.cs | 7 +++--- .../Overlays/BeatmapSet/Scores/ScoreTable.cs | 23 ++++++++++--------- .../Scores/TopScoreStatisticsSection.cs | 22 ++++++++++-------- .../BeatmapSet/Scores/TopScoreUserSection.cs | 3 ++- .../Leaderboards/BeatmapLeaderboardScope.cs | 8 ++++--- 5 files changed, 35 insertions(+), 28 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/NoScoresPlaceholder.cs b/osu.Game/Overlays/BeatmapSet/Scores/NoScoresPlaceholder.cs index 391ba93a4b..82ca3e030f 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/NoScoresPlaceholder.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/NoScoresPlaceholder.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Screens.Select.Leaderboards; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapSet.Scores { @@ -30,15 +31,15 @@ namespace osu.Game.Overlays.BeatmapSet.Scores switch (scope) { default: - text.Text = @"No scores have been set yet. Maybe you can be the first!"; + text.Text = BeatmapsetsStrings.ShowScoreboardNoScoresGlobal; break; case BeatmapLeaderboardScope.Friend: - text.Text = @"None of your friends have set a score on this map yet."; + text.Text = BeatmapsetsStrings.ShowScoreboardNoScoresFriend; break; case BeatmapLeaderboardScope.Country: - text.Text = @"No one from your country has set a score on this map yet."; + text.Text = BeatmapsetsStrings.ShowScoreboardNoScoresCountry; break; } } diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index fee0e62315..a154016824 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -20,6 +20,7 @@ using osuTK; using osuTK.Graphics; using osu.Framework.Localisation; using osu.Framework.Extensions.LocalisationExtensions; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapSet.Scores { @@ -93,13 +94,13 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { var columns = new List { - new TableColumn("rank", Anchor.CentreRight, new Dimension(GridSizeMode.AutoSize)), + new TableColumn(BeatmapsetsStrings.ShowScoreboardHeadersRank, Anchor.CentreRight, new Dimension(GridSizeMode.AutoSize)), new TableColumn("", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 70)), // grade - new TableColumn("score", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("accuracy", Anchor.CentreLeft, new Dimension(GridSizeMode.Absolute, minSize: 60, maxSize: 70)), + new TableColumn(BeatmapsetsStrings.ShowScoreboardHeadersScore, Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize)), + new TableColumn(BeatmapsetsStrings.ShowScoreboardHeadersAccuracy, Anchor.CentreLeft, new Dimension(GridSizeMode.Absolute, minSize: 60, maxSize: 70)), new TableColumn("", Anchor.CentreLeft, new Dimension(GridSizeMode.Absolute, 25)), // flag - new TableColumn("player", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 125)), - new TableColumn("max combo", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 70, maxSize: 120)) + new TableColumn(BeatmapsetsStrings.ShowScoreboardHeadersPlayer, Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 125)), + new TableColumn(BeatmapsetsStrings.ShowScoreboardHeadersCombo, Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 70, maxSize: 120)) }; // All statistics across all scores, unordered. @@ -124,9 +125,9 @@ namespace osu.Game.Overlays.BeatmapSet.Scores } if (showPerformancePoints) - columns.Add(new TableColumn("pp", Anchor.CentreLeft, new Dimension(GridSizeMode.Absolute, 30))); + columns.Add(new TableColumn(BeatmapsetsStrings.ShowScoreboardHeaderspp, Anchor.CentreLeft, new Dimension(GridSizeMode.Absolute, 30))); - columns.Add(new TableColumn("mods", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize))); + columns.Add(new TableColumn(BeatmapsetsStrings.ShowScoreboardHeadersMods, Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize))); return columns.ToArray(); } @@ -140,7 +141,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { new OsuSpriteText { - Text = $"#{index + 1}", + Text = (index + 1).ToLocalisableString(@"\##"), Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold) }, new UpdateableRank(score.Rank) @@ -168,7 +169,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores username, new OsuSpriteText { - Text = $@"{score.MaxCombo:N0}x", + Text = score.MaxCombo.ToLocalisableString(@"0\x"), Font = OsuFont.GetFont(size: text_size), Colour = score.MaxCombo == score.Beatmap?.MaxCombo ? highAccuracyColour : Color4.White } @@ -183,7 +184,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores content.Add(new OsuSpriteText { - Text = stat.MaxCount == null ? $"{stat.Count}" : $"{stat.Count}/{stat.MaxCount}", + Text = stat.MaxCount == null ? stat.Count.ToLocalisableString(@"N0") : (LocalisableString)$"{stat.Count}/{stat.MaxCount}", Font = OsuFont.GetFont(size: text_size), Colour = stat.Count == 0 ? Color4.Gray : Color4.White }); @@ -193,7 +194,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { content.Add(new OsuSpriteText { - Text = $@"{score.PP:N0}", + Text = score.PP.ToLocalisableString(@"N0"), Font = OsuFont.GetFont(size: text_size) }); } diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs index 3d5f3f595c..582528b675 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -13,6 +14,7 @@ using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; using osu.Game.Scoring; @@ -61,9 +63,9 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Spacing = new Vector2(margin, 0), Children = new Drawable[] { - totalScoreColumn = new TextColumn("total score", largeFont, top_columns_min_width), - accuracyColumn = new TextColumn("accuracy", largeFont, top_columns_min_width), - maxComboColumn = new TextColumn("max combo", largeFont, top_columns_min_width) + totalScoreColumn = new TextColumn(BeatmapsetsStrings.ShowScoreboardHeadersScoreTotal, largeFont, top_columns_min_width), + accuracyColumn = new TextColumn(BeatmapsetsStrings.ShowScoreboardHeadersAccuracy, largeFont, top_columns_min_width), + maxComboColumn = new TextColumn(BeatmapsetsStrings.ShowScoreboardHeadersCombo, largeFont, top_columns_min_width) } }, new FillFlowContainer @@ -81,7 +83,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Direction = FillDirection.Horizontal, Spacing = new Vector2(margin, 0), }, - ppColumn = new TextColumn("pp", smallFont, bottom_columns_min_width), + ppColumn = new TextColumn(BeatmapsetsStrings.ShowScoreboardHeaderspp, smallFont, bottom_columns_min_width), modsColumn = new ModsInfoColumn(), } }, @@ -111,10 +113,10 @@ namespace osu.Game.Overlays.BeatmapSet.Scores score = value; accuracyColumn.Text = value.DisplayAccuracy; - maxComboColumn.Text = $@"{value.MaxCombo:N0}x"; + maxComboColumn.Text = value.MaxCombo.ToLocalisableString(@"0\x"); ppColumn.Alpha = value.Beatmap?.Status.GrantsPerformancePoints() == true ? 1 : 0; - ppColumn.Text = $@"{value.PP:N0}"; + ppColumn.Text = value.PP.ToLocalisableString(@"N0"); statisticsColumns.ChildrenEnumerable = value.GetStatisticsForDisplay().Select(createStatisticsColumn); modsColumn.Mods = value.Mods; @@ -126,7 +128,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private TextColumn createStatisticsColumn(HitResultDisplayStatistic stat) => new TextColumn(stat.DisplayName, smallFont, bottom_columns_min_width) { - Text = stat.MaxCount == null ? $"{stat.Count}" : $"{stat.Count}/{stat.MaxCount}" + Text = stat.MaxCount == null ? stat.Count.ToLocalisableString(@"N0") : (LocalisableString)$"{stat.Count}/{stat.MaxCount}" }; private class InfoColumn : CompositeDrawable @@ -134,7 +136,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private readonly Box separator; private readonly OsuSpriteText text; - public InfoColumn(string title, Drawable content, float? minWidth = null) + public InfoColumn(LocalisableString title, Drawable content, float? minWidth = null) { AutoSizeAxes = Axes.Both; Margin = new MarginPadding { Vertical = 5 }; @@ -194,12 +196,12 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { private readonly SpriteText text; - public TextColumn(string title, FontUsage font, float? minWidth = null) + public TextColumn(LocalisableString title, FontUsage font, float? minWidth = null) : this(title, new OsuSpriteText { Font = font }, minWidth) { } - private TextColumn(string title, SpriteText text, float? minWidth = null) + private TextColumn(LocalisableString title, SpriteText text, float? minWidth = null) : base(title, text, minWidth) { this.text = text; diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs index 736366fb5c..c934020059 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -126,7 +127,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores public int? ScorePosition { - set => rankText.Text = value == null ? "-" : $"#{value}"; + set => rankText.Text = value == null ? (LocalisableString)"-" : value.ToLocalisableString(@"\##"); } /// diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboardScope.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboardScope.cs index dc4c2ba4e2..5bcb4c27a7 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboardScope.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboardScope.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System.ComponentModel; +using osu.Framework.Localisation; +using osu.Game.Resources.Localisation.Web; namespace osu.Game.Screens.Select.Leaderboards { @@ -10,13 +12,13 @@ namespace osu.Game.Screens.Select.Leaderboards [Description("Local Ranking")] Local, - [Description("Country Ranking")] + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowScoreboardCountry))] Country, - [Description("Global Ranking")] + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowScoreboardGlobal))] Global, - [Description("Friend Ranking")] + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowScoreboardFriend))] Friend, } } From 3325b0cc95d314e700a3e4d928fac9cf6b61c229 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 16 Aug 2021 14:50:08 +0200 Subject: [PATCH 1213/2442] Fix merge conflicts. --- osu.Game/Overlays/BeatmapSet/BasicStats.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/BasicStats.cs b/osu.Game/Overlays/BeatmapSet/BasicStats.cs index dfba93cdbd..80dc185bb5 100644 --- a/osu.Game/Overlays/BeatmapSet/BasicStats.cs +++ b/osu.Game/Overlays/BeatmapSet/BasicStats.cs @@ -107,7 +107,7 @@ namespace osu.Game.Overlays.BeatmapSet set => this.value.Text = value; } - public Statistic(BeatmapStatisticsIconType icon, string name) + public Statistic(BeatmapStatisticsIconType icon, LocalisableString name) { TooltipText = name; RelativeSizeAxes = Axes.X; From 568d027013a67ddcc3ff58525001301cf353da8a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Aug 2021 22:07:41 +0900 Subject: [PATCH 1214/2442] Simplify weird conditionals --- .../Settings/Sections/Input/TabletSettings.cs | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index 8303fee3ef..b8b86d9069 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -266,24 +266,16 @@ namespace osu.Game.Overlays.Settings.Sections.Input private void updateVisibility() { - if (!tabletHandler.Enabled.Value) - { - mainSettings.Hide(); - noTabletMessage.Hide(); - return; - } - - bool tabletFound = tablet.Value != null; - - if (!tabletFound) - { - mainSettings.Hide(); - noTabletMessage.Show(); - return; - } - - mainSettings.Show(); + mainSettings.Hide(); noTabletMessage.Hide(); + + if (!tabletHandler.Enabled.Value) + return; + + if (tablet.Value != null) + mainSettings.Show(); + else + noTabletMessage.Show(); } private void applyAspectRatio(BindableNumber sizeChanged) From 677f5aff9e39a0a2de16a0c25071f787db4a70e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Aug 2021 23:30:50 +0900 Subject: [PATCH 1215/2442] Fix test failures --- osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index fa2c9ecdea..8632bfe681 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -32,6 +32,7 @@ namespace osu.Game.Tests.Visual.Settings [SetUpSteps] public void SetUpSteps() { + AddUntilStep("wait for load", () => panel.ChildrenOfType().Any()); AddStep("Scroll to top", () => panel.ChildrenOfType().First().ScrollToTop()); AddWaitStep("wait for scroll", 5); } From c3a86769323e11c637cd738393928be1e1499b47 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Aug 2021 23:38:13 +0900 Subject: [PATCH 1216/2442] Simplify size specifications --- osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index c51f927f9f..508c8399b6 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -144,8 +144,7 @@ namespace osu.Game.Overlays.Changelog { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Width = heart_size, - Height = heart_size, + Size = new Vector2(heart_size), Margin = new MarginPadding { Top = 70 }, Masking = true, EdgeEffect = new EdgeEffectParameters @@ -157,8 +156,7 @@ namespace osu.Game.Overlays.Changelog }, Child = new Sprite { - Width = heart_size, - Height = heart_size, + Size = new Vector2(heart_size), Texture = textures.Get(@"Online/supporter-heart"), }, }, From df6e4664e0aaa47788c1064be78126fe57fa1d75 Mon Sep 17 00:00:00 2001 From: emu1337 Date: Mon, 16 Aug 2021 16:42:07 +0200 Subject: [PATCH 1217/2442] changed history length in speed --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index b1373cd215..5b8ec5103a 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -29,6 +29,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private const double max_speed_bonus = 45; // ~330BPM private const double speed_balancing_factor = 40; + protected override int HistoryLength => 32; + public Speed(Mod[] mods) : base(mods) { From 4bf22db4ff9ff21123d9a0cc35a6aa2ed099b65d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Aug 2021 00:23:30 +0900 Subject: [PATCH 1218/2442] Attempt to reduce skin lookup overhead where file access is not required --- osu.Game/Database/MutableDatabaseBackedStore.cs | 5 +++++ osu.Game/Skinning/SkinManager.cs | 7 ++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/MutableDatabaseBackedStore.cs b/osu.Game/Database/MutableDatabaseBackedStore.cs index c9d0c4bc41..b0feb7bb78 100644 --- a/osu.Game/Database/MutableDatabaseBackedStore.cs +++ b/osu.Game/Database/MutableDatabaseBackedStore.cs @@ -36,6 +36,11 @@ namespace osu.Game.Database /// public IQueryable ConsumableItems => AddIncludesForConsumption(ContextFactory.Get().Set()); + /// + /// Access barebones items with no includes. + /// + public IQueryable Items => ContextFactory.Get().Set(); + /// /// Add a to the database. /// diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index ea55fd28c2..fca1670419 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -105,12 +105,12 @@ namespace osu.Game.Skinning /// Returns a list of all usable s that have been loaded by the user. /// /// A newly allocated list of available . - public List GetAllUserSkins() => ModelStore.ConsumableItems.Where(s => !s.DeletePending).ToList(); + public List GetAllUserSkins() => ModelStore.Items.Where(s => !s.DeletePending).ToList(); public void SelectRandomSkin() { // choose from only user skins, removing the current selection to ensure a new one is chosen. - var randomChoices = GetAllUsableSkins().Where(s => s.ID != CurrentSkinInfo.Value.ID).ToArray(); + var randomChoices = ModelStore.Items.Where(s => !s.DeletePending && s.ID != CurrentSkinInfo.Value.ID).ToArray(); if (randomChoices.Length == 0) { @@ -118,7 +118,8 @@ namespace osu.Game.Skinning return; } - CurrentSkinInfo.Value = randomChoices.ElementAt(RNG.Next(0, randomChoices.Length)); + var chosen = randomChoices.ElementAt(RNG.Next(0, randomChoices.Length)); + CurrentSkinInfo.Value = ModelStore.ConsumableItems.Single(i => i.ID == chosen.ID); } protected override SkinInfo CreateModel(ArchiveReader archive) => new SkinInfo { Name = archive.Name }; From 19cdd5c3234ad368f81d2c3df8f66abf63fb51df Mon Sep 17 00:00:00 2001 From: Xexxar Date: Mon, 16 Aug 2021 15:25:35 +0000 Subject: [PATCH 1219/2442] recoded and added rhythm complexity calculator (untested) --- .../Difficulty/Skills/Speed.cs | 82 ++++++++++++++++++- 1 file changed, 79 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 5b8ec5103a..73704735d0 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -20,6 +20,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private const double pi_over_4 = Math.PI / 4; private const double pi_over_2 = Math.PI / 2; + private const double rhythmMultiplier = 1.0; protected override double SkillMultiplier => 1400; protected override double StrainDecayBase => 0.3; protected override int ReducedSectionCount => 5; @@ -31,16 +32,91 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override int HistoryLength => 32; + private const int HistoryTimeMax = 4; // 4 seconds of calculatingRhythmBonus max. + public Speed(Mod[] mods) : base(mods) { } - private double calculateRhythmBonus(double time) + private bool isRatioEqual(double ratio, double a, double b) { - return 1.0; + return a + 15 > ratio * b && a - 15 < ratio * b; } + /// + /// Calculates a rhythm multiplier for the difficulty of the tap associated with historic data of the current . + /// + private double calculateRhythmBonus(double startTime) + { + // {doubles, triplets, quads, quints, 6-tuplets, 7 Tuplets, greater} + int previousIslandSize = -1; + double[] islandTimes = {0, 0, 0, 0, 0, 0, 0}; + int islandSize = 0; + + bool firstDeltaSwitch = false; + + for (int i = Previous.Count - 1; i < 0; i--) + { + double currDelta = ((OsuDifficultyHitObject)Previous[i - 1]).StrainTime; + double prevDelta = ((OsuDifficultyHitObject)Previous[i]).StrainTime; + double effectiveRatio = Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta); + + double currHistoricalDecay = Math.Max(0, (HistoryTimeMax - (startTime - Previous[i - 1].StartTime))) / HistoryTimeMax; + + // if (historyTime > HistoryTimeMax) + // break; // not sure if this even does what I want.. + + if (firstDeltaSwitch) + { + if (isRatioEqual(1.0, prevDelta, currDelta)) + { + islandSize++; // island is still progressing, count size. + } + + else + { + if (islandSize > 6) + islandSize = 6; + + if (Previous[i - 1].BaseObject is Slider) // bpm change is into slider, this is easy acc window + effectiveRatio *= 0.5; + + if (Previous[i].BaseObject is Slider) // bpm change was from a slider, this is easier typically than circle -> circle + effectiveRatio *= 0.75; + + if (previousIslandSize == islandSize) // repeated island size (ex: triplet -> triplet) + effectiveRatio *= 0.5; + + islandTimes[islandSize] = islandTimes[islandSize] + effectiveRatio * currHistoricalDecay; + + previousIslandSize = islandSize; // log the last island size. + + if (prevDelta * 1.25 < currDelta) // we're slowing down, stop counting + firstDeltaSwitch = false; // if we're speeding up, this stays true and we keep counting island size. + + islandSize = 0; + } + } + else if (prevDelta > 1.25 * currDelta) // we want to be speeding up. + { + // Begin counting island until we change speed again. + firstDeltaSwitch = true; + islandSize = 0; + } + } + + double rhythmComplexitySum = 0.0; + + for (int i = 0; i < islandTimes.Length; i++) + { + rhythmComplexitySum += islandTimes[i]; // sum the total amount of rhythm variance + } + +Console.WriteLine(Math.Sqrt(4 + rhythmComplexitySum * rhythmMultiplier) / 2); + + return Math.Min(1.5, Math.Sqrt(4 + rhythmComplexitySum * rhythmMultiplier) / 2); + } protected override double StrainValueOf(DifficultyHitObject current) { @@ -81,7 +157,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override double GetPeakStrain(double time) { - return base.GetPeakStrain(time) * calculateRhythmBonus(time); + return base.GetPeakStrain(time);// * calculateRhythmBonus(current.StartTime); } } } From 7d46b3f9c5fe9062afa5528d025908173c5ca812 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Mon, 16 Aug 2021 16:06:50 +0000 Subject: [PATCH 1220/2442] initial testing and debugging --- .../Difficulty/Skills/Speed.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 73704735d0..433150a025 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -20,7 +20,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private const double pi_over_4 = Math.PI / 4; private const double pi_over_2 = Math.PI / 2; - private const double rhythmMultiplier = 1.0; + private const double rhythmMultiplier = 1.5; + protected override double SkillMultiplier => 1400; protected override double StrainDecayBase => 0.3; protected override int ReducedSectionCount => 5; @@ -32,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override int HistoryLength => 32; - private const int HistoryTimeMax = 4; // 4 seconds of calculatingRhythmBonus max. + private const int HistoryTimeMax = 3000; // 4 seconds of calculatingRhythmBonus max. public Speed(Mod[] mods) : base(mods) @@ -56,16 +57,16 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills bool firstDeltaSwitch = false; - for (int i = Previous.Count - 1; i < 0; i--) + for (int i = Previous.Count - 1; i > 0; i--) { double currDelta = ((OsuDifficultyHitObject)Previous[i - 1]).StrainTime; double prevDelta = ((OsuDifficultyHitObject)Previous[i]).StrainTime; double effectiveRatio = Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta); - double currHistoricalDecay = Math.Max(0, (HistoryTimeMax - (startTime - Previous[i - 1].StartTime))) / HistoryTimeMax; + if (effectiveRatio > 0.5) + effectiveRatio = 0.5 + (effectiveRatio - 0.5) * 5; // extra buff for 1/3 -> 1/4 etc transitions. - // if (historyTime > HistoryTimeMax) - // break; // not sure if this even does what I want.. + double currHistoricalDecay = Math.Max(0, (HistoryTimeMax - (startTime - Previous[i - 1].StartTime))) / HistoryTimeMax; if (firstDeltaSwitch) { @@ -113,9 +114,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills rhythmComplexitySum += islandTimes[i]; // sum the total amount of rhythm variance } -Console.WriteLine(Math.Sqrt(4 + rhythmComplexitySum * rhythmMultiplier) / 2); +// Console.WriteLine(Math.Sqrt(4 + rhythmComplexitySum * rhythmMultiplier) / 2); - return Math.Min(1.5, Math.Sqrt(4 + rhythmComplexitySum * rhythmMultiplier) / 2); + return Math.Sqrt(4 + rhythmComplexitySum * rhythmMultiplier) / 2; } protected override double StrainValueOf(DifficultyHitObject current) From 78f9f4a23081ce994c702215ff94e6d805151b60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20Sch=C3=BCrz?= Date: Mon, 16 Aug 2021 20:36:46 +0200 Subject: [PATCH 1221/2442] Move time clamp to `Seek`/`transformSeekTo` methods --- osu.Game/Screens/Edit/EditorClock.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs index 52c95088f3..ba83261731 100644 --- a/osu.Game/Screens/Edit/EditorClock.cs +++ b/osu.Game/Screens/Edit/EditorClock.cs @@ -118,7 +118,6 @@ namespace osu.Game.Screens.Edit if (!snapped || ControlPointInfo.TimingPoints.Count == 0) { - seekTime = Math.Clamp(seekTime, 0, TrackLength); SeekSmoothlyTo(seekTime); return; } @@ -151,8 +150,6 @@ namespace osu.Game.Screens.Edit if (seekTime < timingPoint.Time && timingPoint != ControlPointInfo.TimingPoints.First()) seekTime = timingPoint.Time; - // Ensure the sought point is within the boundaries - seekTime = Math.Clamp(seekTime, 0, TrackLength); SeekSmoothlyTo(seekTime); } @@ -191,6 +188,9 @@ namespace osu.Game.Screens.Edit seekingOrStopped.Value = IsSeeking = true; ClearTransforms(); + + // Ensure the sought point is within the boundaries + position = Math.Clamp(position, 0, TrackLength); return underlyingClock.Seek(position); } @@ -289,7 +289,7 @@ namespace osu.Game.Screens.Edit } private void transformSeekTo(double seek, double duration = 0, Easing easing = Easing.None) - => this.TransformTo(this.PopulateTransform(new TransformSeek(), seek, duration, easing)); + => this.TransformTo(this.PopulateTransform(new TransformSeek(), Math.Clamp(seek, 0, TrackLength), duration, easing)); private double currentTime { From 314c342841e23954c8fc3c80aa4a6930143a5fb6 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Mon, 16 Aug 2021 22:13:01 +0300 Subject: [PATCH 1222/2442] Avoid drawing segments of cursor trail near current cursor position --- osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 7a95111c91..43e0a57f7c 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -172,7 +172,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor float interval = partSize.X / 2.5f * IntervalMultiplier; - for (float d = interval; d < distance; d += interval) + for (float d = interval; d < distance - interval; d += interval) { lastPosition = pos1 + direction * d; addPart(lastPosition.Value); From ef367c6547f7dc27ad7fc9c2470f1403438a1cde Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Mon, 16 Aug 2021 22:52:19 +0300 Subject: [PATCH 1223/2442] Move implementation to be legacy only --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs | 1 + osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs index 587ff4b573..75d847d54d 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs @@ -62,6 +62,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy protected override bool InterpolateMovements => !disjointTrail; protected override float IntervalMultiplier => 1 / Math.Max(cursorSize.Value, 1); + protected override bool AvoidDrawingNearCursor => !disjointTrail; protected override void Update() { diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 43e0a57f7c..62cab4d6d7 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -138,6 +138,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor protected virtual bool InterpolateMovements => true; protected virtual float IntervalMultiplier => 1.0f; + protected virtual bool AvoidDrawingNearCursor => false; private Vector2? lastPosition; private readonly InputResampler resampler = new InputResampler(); @@ -171,8 +172,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor Vector2 direction = diff / distance; float interval = partSize.X / 2.5f * IntervalMultiplier; + float stopAt = distance - (AvoidDrawingNearCursor ? interval : 0); - for (float d = interval; d < distance - interval; d += interval) + for (float d = interval; d < stopAt; d += interval) { lastPosition = pos1 + direction * d; addPart(lastPosition.Value); From 176b3e75335476273ceb305a60c2b4a0fc9fa3f4 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Mon, 16 Aug 2021 22:14:29 +0000 Subject: [PATCH 1224/2442] changed decay system to allow for customizing the currentStrain --- .../Difficulty/Skills/Movement.cs | 2 +- .../Difficulty/Skills/Strain.cs | 4 +- .../Difficulty/Skills/Aim.cs | 2 +- .../Difficulty/Skills/OsuStrainSkill.cs | 4 +- .../Difficulty/Skills/Speed.cs | 11 +--- .../Difficulty/Skills/Colour.cs | 2 +- .../Difficulty/Skills/Rhythm.cs | 2 +- .../Difficulty/Skills/Stamina.cs | 2 +- .../Difficulty/Skills/StrainDecaySkill.cs | 64 +++++++++++++++++++ .../Rulesets/Difficulty/Skills/StrainSkill.cs | 44 +++---------- 10 files changed, 83 insertions(+), 54 deletions(-) create mode 100644 osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index 4372ed938c..cfb3fe40be 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -9,7 +9,7 @@ using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Catch.Difficulty.Skills { - public class Movement : StrainSkill + public class Movement : StrainDecaySkill { private const float absolute_player_positioning_error = 16f; private const float normalized_hitobject_radius = 41.0f; diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs index 2ba2ee6b4a..01d930d585 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Mania.Difficulty.Skills { - public class Strain : StrainSkill + public class Strain : StrainDecaySkill { private const double individual_decay_base = 0.125; private const double overall_decay_base = 0.30; @@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills return individualStrain + overallStrain - CurrentStrain; } - protected override double GetPeakStrain(double offset) + protected override double CalculateInitialStrain(double offset) => applyDecay(individualStrain, offset - Previous[0].StartTime, individual_decay_base) + applyDecay(overallStrain, offset - Previous[0].StartTime, overall_decay_base); diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index 16a18cbcb9..f08c19af76 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// /// Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances. /// - public class Aim : OsuStrainSkill + public class Aim : OsuStrainDecaySkill { private const double angle_bonus_begin = Math.PI / 3; private const double timing_threshold = 107; diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index e47edc37cc..c1bee9b202 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -10,7 +10,7 @@ using osu.Framework.Utils; namespace osu.Game.Rulesets.Osu.Difficulty.Skills { - public abstract class OsuStrainSkill : StrainSkill + public abstract class OsuStrainDecaySkill : StrainDecaySkill { /// /// The number of sections with the highest strains, which the peak strain reductions will apply to. @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// protected virtual double DifficultyMultiplier => 1.06; - protected OsuStrainSkill(Mod[] mods) + protected OsuStrainDecaySkill(Mod[] mods) : base(mods) { } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 433150a025..bb40ff657a 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// /// Represents the skill required to press keys with regards to keeping up with the speed at which objects need to be hit. /// - public class Speed : OsuStrainSkill + public class Speed : OsuStrainDecaySkill { private const double single_spacing_threshold = 125; @@ -151,14 +151,5 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills return (1 + (speedBonus - 1) * 0.75) * angleBonus * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / osuCurrent.StrainTime; } - protected override double GetTotalCurrentStrain(DifficultyHitObject current) - { - return base.GetTotalCurrentStrain(current) * calculateRhythmBonus(current.StartTime); - } - - protected override double GetPeakStrain(double time) - { - return base.GetPeakStrain(time);// * calculateRhythmBonus(current.StartTime); - } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs index 769d021362..0c17ca66b9 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills /// /// Calculates the colour coefficient of taiko difficulty. /// - public class Colour : StrainSkill + public class Colour : StrainDecaySkill { protected override double SkillMultiplier => 1; protected override double StrainDecayBase => 0.4; diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Rhythm.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Rhythm.cs index a32f6ebe0d..973e55f4b4 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Rhythm.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Rhythm.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills /// /// Calculates the rhythm coefficient of taiko difficulty. /// - public class Rhythm : StrainSkill + public class Rhythm : StrainDecaySkill { protected override double SkillMultiplier => 10; protected override double StrainDecayBase => 0; diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs index 4cceadb23f..54cf233d69 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills /// /// The reference play style chosen uses two hands, with full alternating (the hand changes after every hit). /// - public class Stamina : StrainSkill + public class Stamina : StrainDecaySkill { protected override double SkillMultiplier => 1; protected override double StrainDecayBase => 0.4; diff --git a/osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs b/osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs new file mode 100644 index 0000000000..dab1081abb --- /dev/null +++ b/osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs @@ -0,0 +1,64 @@ +// 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 osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Difficulty.Skills +{ + /// + /// Used to processes strain values of s, keep track of strain levels caused by the processed objects + /// and to calculate a final difficulty value representing the difficulty of hitting all the processed objects. + /// + public abstract class StrainDecaySkill : StrainSkill + { + /// + /// Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other. + /// + protected abstract double SkillMultiplier { get; } + + /// + /// Determines how quickly strain decays for the given skill. + /// For example a value of 0.15 indicates that strain decays to 15% of its original value in one second. + /// + protected abstract double StrainDecayBase { get; } + + /// + /// The current strain level. + /// + protected double CurrentStrain { get; private set; } = 1; + + protected StrainDecaySkill(Mod[] mods) + : base(mods) + { + } + + /// + /// Retrieves the peak strain at a point in time. + /// + /// The time to retrieve the peak strain at. + /// The peak strain. + protected override double CalculateInitialStrain(double time) => CurrentStrain * strainDecay(time - Previous[0].StartTime); + + /// + /// Returns the strain value of . This value is calculated with or without respect to previous objects. + /// + protected override double StrainValueAt(DifficultyHitObject current) + { + CurrentStrain *= strainDecay(current.DeltaTime); + CurrentStrain += StrainValueOf(current) * SkillMultiplier; + + return CurrentStrain; + } + + /// + /// Calculates the strain value of a . This value is affected by previously processed objects. + /// + protected abstract double StrainValueOf(DifficultyHitObject current); + + private double strainDecay(double ms) => Math.Pow(StrainDecayBase, ms / 1000); + } +} diff --git a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs index 95b0fe43fc..e8ae452506 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs @@ -15,27 +15,11 @@ namespace osu.Game.Rulesets.Difficulty.Skills /// public abstract class StrainSkill : Skill { - /// - /// Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other. - /// - protected abstract double SkillMultiplier { get; } - - /// - /// Determines how quickly strain decays for the given skill. - /// For example a value of 0.15 indicates that strain decays to 15% of its original value in one second. - /// - protected abstract double StrainDecayBase { get; } - /// /// The weight by which each strain value decays. /// protected virtual double DecayWeight => 0.9; - /// - /// The current strain level. - /// - protected double CurrentStrain { get; private set; } = 1; - /// /// The length of each strain section. /// @@ -52,6 +36,11 @@ namespace osu.Game.Rulesets.Difficulty.Skills { } + /// + /// Returns the strain value of . This value is calculated with or without respect to previous objects. + /// + protected abstract double StrainValueAt(DifficultyHitObject current); + /// /// Process a and update current strain values accordingly. /// @@ -68,15 +57,7 @@ namespace osu.Game.Rulesets.Difficulty.Skills currentSectionEnd += SectionLength; } - CurrentStrain *= strainDecay(current.DeltaTime); - CurrentStrain += StrainValueOf(current) * SkillMultiplier; - - currentSectionPeak = Math.Max(GetTotalCurrentStrain(current), currentSectionPeak); - } - - protected virtual double GetTotalCurrentStrain(DifficultyHitObject current) - { - return CurrentStrain; + currentSectionPeak = Math.Max(StrainValueAt(current), currentSectionPeak); } /// @@ -93,9 +74,9 @@ namespace osu.Game.Rulesets.Difficulty.Skills /// The beginning of the new section in milliseconds. private void startNewSectionFrom(double time) { - // The maximum strain of the new section is not zero by default, strain decays as usual regardless of section boundaries. + // The maximum strain of the new section is not zero by default // This means we need to capture the strain level at the beginning of the new section, and use that as the initial peak level. - currentSectionPeak = GetPeakStrain(time); + currentSectionPeak = CalculateInitialStrain(time); } /// @@ -103,7 +84,7 @@ namespace osu.Game.Rulesets.Difficulty.Skills /// /// The time to retrieve the peak strain at. /// The peak strain. - protected virtual double GetPeakStrain(double time) => CurrentStrain * strainDecay(time - Previous[0].StartTime); + protected abstract double CalculateInitialStrain(double time); /// /// Returns a live enumerable of the peak strains for each section of the beatmap, @@ -129,12 +110,5 @@ namespace osu.Game.Rulesets.Difficulty.Skills return difficulty; } - - /// - /// Calculates the strain value of a . This value is affected by previously processed objects. - /// - protected abstract double StrainValueOf(DifficultyHitObject current); - - private double strainDecay(double ms) => Math.Pow(StrainDecayBase, ms / 1000); } } From 5561e4852e838c226257d09088e3f51e42d6f2d2 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Mon, 16 Aug 2021 22:23:40 +0000 Subject: [PATCH 1225/2442] removed stuff --- .../Difficulty/Skills/Speed.cs | 85 ------------------- 1 file changed, 85 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index bb40ff657a..78d1438923 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -20,8 +20,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private const double pi_over_4 = Math.PI / 4; private const double pi_over_2 = Math.PI / 2; - private const double rhythmMultiplier = 1.5; - protected override double SkillMultiplier => 1400; protected override double StrainDecayBase => 0.3; protected override int ReducedSectionCount => 5; @@ -31,94 +29,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private const double max_speed_bonus = 45; // ~330BPM private const double speed_balancing_factor = 40; - protected override int HistoryLength => 32; - - private const int HistoryTimeMax = 3000; // 4 seconds of calculatingRhythmBonus max. - public Speed(Mod[] mods) : base(mods) { } - private bool isRatioEqual(double ratio, double a, double b) - { - return a + 15 > ratio * b && a - 15 < ratio * b; - } - - /// - /// Calculates a rhythm multiplier for the difficulty of the tap associated with historic data of the current . - /// - private double calculateRhythmBonus(double startTime) - { - // {doubles, triplets, quads, quints, 6-tuplets, 7 Tuplets, greater} - int previousIslandSize = -1; - double[] islandTimes = {0, 0, 0, 0, 0, 0, 0}; - int islandSize = 0; - - bool firstDeltaSwitch = false; - - for (int i = Previous.Count - 1; i > 0; i--) - { - double currDelta = ((OsuDifficultyHitObject)Previous[i - 1]).StrainTime; - double prevDelta = ((OsuDifficultyHitObject)Previous[i]).StrainTime; - double effectiveRatio = Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta); - - if (effectiveRatio > 0.5) - effectiveRatio = 0.5 + (effectiveRatio - 0.5) * 5; // extra buff for 1/3 -> 1/4 etc transitions. - - double currHistoricalDecay = Math.Max(0, (HistoryTimeMax - (startTime - Previous[i - 1].StartTime))) / HistoryTimeMax; - - if (firstDeltaSwitch) - { - if (isRatioEqual(1.0, prevDelta, currDelta)) - { - islandSize++; // island is still progressing, count size. - } - - else - { - if (islandSize > 6) - islandSize = 6; - - if (Previous[i - 1].BaseObject is Slider) // bpm change is into slider, this is easy acc window - effectiveRatio *= 0.5; - - if (Previous[i].BaseObject is Slider) // bpm change was from a slider, this is easier typically than circle -> circle - effectiveRatio *= 0.75; - - if (previousIslandSize == islandSize) // repeated island size (ex: triplet -> triplet) - effectiveRatio *= 0.5; - - islandTimes[islandSize] = islandTimes[islandSize] + effectiveRatio * currHistoricalDecay; - - previousIslandSize = islandSize; // log the last island size. - - if (prevDelta * 1.25 < currDelta) // we're slowing down, stop counting - firstDeltaSwitch = false; // if we're speeding up, this stays true and we keep counting island size. - - islandSize = 0; - } - } - else if (prevDelta > 1.25 * currDelta) // we want to be speeding up. - { - // Begin counting island until we change speed again. - firstDeltaSwitch = true; - islandSize = 0; - } - } - - double rhythmComplexitySum = 0.0; - - for (int i = 0; i < islandTimes.Length; i++) - { - rhythmComplexitySum += islandTimes[i]; // sum the total amount of rhythm variance - } - -// Console.WriteLine(Math.Sqrt(4 + rhythmComplexitySum * rhythmMultiplier) / 2); - - return Math.Sqrt(4 + rhythmComplexitySum * rhythmMultiplier) / 2; - } - protected override double StrainValueOf(DifficultyHitObject current) { if (current.BaseObject is Spinner) From 61045bd0876afac630c9611de1398c0c65677141 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Mon, 16 Aug 2021 22:36:14 +0000 Subject: [PATCH 1226/2442] adjusted code comments --- osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs | 8 -------- osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs b/osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs index dab1081abb..dbac132faf 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs @@ -36,16 +36,8 @@ namespace osu.Game.Rulesets.Difficulty.Skills { } - /// - /// Retrieves the peak strain at a point in time. - /// - /// The time to retrieve the peak strain at. - /// The peak strain. protected override double CalculateInitialStrain(double time) => CurrentStrain * strainDecay(time - Previous[0].StartTime); - /// - /// Returns the strain value of . This value is calculated with or without respect to previous objects. - /// protected override double StrainValueAt(DifficultyHitObject current) { CurrentStrain *= strainDecay(current.DeltaTime); diff --git a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs index e8ae452506..0880f1b08e 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Difficulty.Skills } /// - /// Returns the strain value of . This value is calculated with or without respect to previous objects. + /// Returns the strain value at . This value is calculated with or without respect to previous objects. /// protected abstract double StrainValueAt(DifficultyHitObject current); From 9b21016eed1aadc232c0023b23624d61775659c3 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Mon, 16 Aug 2021 22:46:56 +0000 Subject: [PATCH 1227/2442] accidently renamed osuStrainSkill, fixed --- osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs | 2 +- osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs | 4 ++-- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index f08c19af76..16a18cbcb9 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// /// Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances. /// - public class Aim : OsuStrainDecaySkill + public class Aim : OsuStrainSkill { private const double angle_bonus_begin = Math.PI / 3; private const double timing_threshold = 107; diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index c1bee9b202..7bcd867a9c 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -10,7 +10,7 @@ using osu.Framework.Utils; namespace osu.Game.Rulesets.Osu.Difficulty.Skills { - public abstract class OsuStrainDecaySkill : StrainDecaySkill + public abstract class OsuStrainSkill : StrainDecaySkill { /// /// The number of sections with the highest strains, which the peak strain reductions will apply to. @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// protected virtual double DifficultyMultiplier => 1.06; - protected OsuStrainDecaySkill(Mod[] mods) + protected OsuStrainSkill(Mod[] mods) : base(mods) { } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 78d1438923..f0eb199e5f 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// /// Represents the skill required to press keys with regards to keeping up with the speed at which objects need to be hit. /// - public class Speed : OsuStrainDecaySkill + public class Speed : OsuStrainSkill { private const double single_spacing_threshold = 125; From 97d5b80834568d76ca423cf876aaf9955681a72f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 17 Aug 2021 09:18:21 +0900 Subject: [PATCH 1228/2442] Fix joining with incorrect password --- .../Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs index 2d94b2328d..776307e20e 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs @@ -19,7 +19,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private MultiplayerClient multiplayerClient { get; set; } public override void CreateRoom(Room room, Action onSuccess = null, Action onError = null) - => base.CreateRoom(room, r => joinMultiplayerRoom(r, r.Password.Value, onSuccess, onError), onError); + => base.CreateRoom(room, r => joinMultiplayerRoom(r, room.Password.Value, onSuccess, onError), onError); public override void JoinRoom(Room room, string password = null, Action onSuccess = null, Action onError = null) { From 352949069a4a3130c8db14ea8412ac11c93d57aa Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 17 Aug 2021 09:36:43 +0900 Subject: [PATCH 1229/2442] Move filter to LoungeSubScreen --- .../TestSceneLoungeRoomsContainer.cs | 10 ++++----- .../Multiplayer/TestSceneMultiplayer.cs | 22 +++++++++++-------- .../Components/ListingPollingComponent.cs | 10 +++++---- .../Lounge/Components/RoomsContainer.cs | 9 ++++---- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 15 ++++++------- .../Screens/OnlinePlay/OnlinePlayScreen.cs | 4 ---- ...stRequestHandlingMultiplayerRoomManager.cs | 5 ----- .../IOnlinePlayTestSceneDependencies.cs | 6 ----- .../Visual/OnlinePlay/OnlinePlayTestScene.cs | 2 -- .../OnlinePlayTestSceneDependencies.cs | 4 ---- 10 files changed, 35 insertions(+), 52 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs index f3d961a646..7e822f898e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs @@ -115,11 +115,11 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("4 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 4); - AddStep("filter one room", () => container.Filter(new FilterCriteria { SearchString = "1" })); + AddStep("filter one room", () => container.Filter.Value = new FilterCriteria { SearchString = "1" }); AddUntilStep("1 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 1); - AddStep("remove filter", () => container.Filter(null)); + AddStep("remove filter", () => container.Filter.Value = null); AddUntilStep("4 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 4); } @@ -131,13 +131,13 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("add rooms", () => RoomManager.AddRooms(3, new CatchRuleset().RulesetInfo)); // Todo: What even is this case...? - AddStep("set empty filter criteria", () => container.Filter(null)); + AddStep("set empty filter criteria", () => container.Filter.Value = null); AddUntilStep("5 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 5); - AddStep("filter osu! rooms", () => container.Filter(new FilterCriteria { Ruleset = new OsuRuleset().RulesetInfo })); + AddStep("filter osu! rooms", () => container.Filter.Value = new FilterCriteria { Ruleset = new OsuRuleset().RulesetInfo }); AddUntilStep("2 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 2); - AddStep("filter catch rooms", () => container.Filter(new FilterCriteria { Ruleset = new CatchRuleset().RulesetInfo })); + AddStep("filter catch rooms", () => container.Filter.Value = new FilterCriteria { Ruleset = new CatchRuleset().RulesetInfo }); AddUntilStep("3 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 3); } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 08b3fb98a8..cf2f2f259e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -132,11 +132,9 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestExitMidJoin() { - Room room = null; - AddStep("create room", () => { - room = new Room + multiplayerScreen.RoomManager.AddRoom(new Room { Name = { Value = "Test Room" }, Playlist = @@ -147,14 +145,16 @@ namespace osu.Game.Tests.Visual.Multiplayer Ruleset = { Value = new OsuRuleset().RulesetInfo }, } } - }; + }); }); - AddStep("refresh rooms", () => multiplayerScreen.RoomManager.Filter.Value = new FilterCriteria()); + AddStep("refresh rooms", () => this.ChildrenOfType().Single().UpdateFilter()); + AddUntilStep("wait for room", () => this.ChildrenOfType().Any()); + AddStep("select room", () => InputManager.Key(Key.Down)); - AddStep("join room and immediately exit", () => + AddStep("join room and immediately exit select", () => { - multiplayerScreen.ChildrenOfType().Single().Open(room); + InputManager.Key(Key.Enter); Schedule(() => Stack.CurrentScreen.Exit()); }); } @@ -178,7 +178,9 @@ namespace osu.Game.Tests.Visual.Multiplayer }); }); - AddStep("refresh rooms", () => multiplayerScreen.RoomManager.Filter.Value = new FilterCriteria()); + AddStep("refresh rooms", () => this.ChildrenOfType().Single().UpdateFilter()); + AddUntilStep("wait for room", () => this.ChildrenOfType().Any()); + AddStep("select room", () => InputManager.Key(Key.Down)); AddStep("join room", () => InputManager.Key(Key.Enter)); @@ -226,7 +228,9 @@ namespace osu.Game.Tests.Visual.Multiplayer }); }); - AddStep("refresh rooms", () => multiplayerScreen.RoomManager.Filter.Value = new FilterCriteria()); + AddStep("refresh rooms", () => this.ChildrenOfType().Single().UpdateFilter()); + AddUntilStep("wait for room", () => this.ChildrenOfType().Any()); + AddStep("select room", () => InputManager.Key(Key.Down)); AddStep("join room", () => InputManager.Key(Key.Enter)); diff --git a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs index bc6480d05e..1387b5a671 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs @@ -18,8 +18,7 @@ namespace osu.Game.Screens.OnlinePlay.Components public IBindable InitialRoomsReceived => initialRoomsReceived; private readonly Bindable initialRoomsReceived = new Bindable(); - [Resolved] - private Bindable currentFilter { get; set; } + public readonly Bindable Filter = new Bindable(); [Resolved] private Bindable selectedRoom { get; set; } @@ -27,7 +26,7 @@ namespace osu.Game.Screens.OnlinePlay.Components [BackgroundDependencyLoader] private void load() { - currentFilter.BindValueChanged(_ => + Filter.BindValueChanged(_ => { RoomManager.ClearRooms(); initialRoomsReceived.Value = false; @@ -44,10 +43,13 @@ namespace osu.Game.Screens.OnlinePlay.Components if (!API.IsLoggedIn) return base.Poll(); + if (Filter.Value == null) + return base.Poll(); + var tcs = new TaskCompletionSource(); pollReq?.Cancel(); - pollReq = new GetRoomsRequest(currentFilter.Value.Status, currentFilter.Value.Category); + pollReq = new GetRoomsRequest(Filter.Value.Status, Filter.Value.Category); pollReq.Success += result => { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index 46d9850fde..e243477a8c 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -30,8 +30,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components public IReadOnlyList Rooms => roomFlow.FlowingChildren.Cast().ToArray(); - [Resolved(CanBeNull = true)] - private Bindable filter { get; set; } + public readonly Bindable Filter = new Bindable(); [Resolved] private Bindable selectedRoom { get; set; } @@ -74,7 +73,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components rooms.BindTo(roomManager.Rooms); - filter?.BindValueChanged(criteria => Filter(criteria.NewValue)); + Filter?.BindValueChanged(criteria => applyFilterCriteria(criteria.NewValue), true); selectedRoom.BindValueChanged(selection => { @@ -85,7 +84,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private void updateSelection() => roomFlow.Children.ForEach(r => r.State = r.Room == selectedRoom.Value ? SelectionState.Selected : SelectionState.NotSelected); - public void Filter(FilterCriteria criteria) + private void applyFilterCriteria(FilterCriteria criteria) { roomFlow.Children.ForEach(r => { @@ -126,7 +125,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components roomFlow.Add(new DrawableRoom(room)); } - Filter(filter?.Value); + applyFilterCriteria(Filter?.Value); updateSelection(); } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index bd2648791c..3e8d07d002 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -56,9 +56,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge [Resolved(CanBeNull = true)] private OngoingOperationTracker ongoingOperationTracker { get; set; } - [Resolved(CanBeNull = true)] - private Bindable filter { get; set; } - [Resolved] private IBindable ruleset { get; set; } @@ -68,6 +65,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge [CanBeNull] private LeasedBindable selectionLease; + private readonly Bindable filter = new Bindable(new FilterCriteria()); private readonly IBindable operationInProgress = new Bindable(); private readonly IBindable isIdle = new BindableBool(); private LoadingLayer loadingLayer; @@ -81,13 +79,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge if (idleTracker != null) isIdle.BindTo(idleTracker.IsIdle); - filter ??= new Bindable(new FilterCriteria()); - OsuScrollContainer scrollContainer; InternalChildren = new Drawable[] { - ListingPollingComponent = CreatePollingComponent(), + ListingPollingComponent = CreatePollingComponent().With(c => c.Filter.BindTarget = filter), loadingLayer = new LoadingLayer(true), new Container { @@ -161,7 +157,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { RelativeSizeAxes = Axes.Both, ScrollbarOverlapsContent = false, - Child = roomsContainer = new RoomsContainer() + Child = roomsContainer = new RoomsContainer + { + Filter = { BindTarget = filter } + } }, } }, @@ -202,7 +201,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge #region Filtering - protected void UpdateFilter() => Scheduler.AddOnce(updateFilter); + public void UpdateFilter() => Scheduler.AddOnce(updateFilter); private ScheduledDelegate scheduledFilterUpdate; diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 183b7e51f9..e5962db608 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -20,7 +20,6 @@ using osu.Game.Overlays; using osu.Game.Screens.Menu; using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Lounge; -using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Users; using osuTK; using osuTK.Graphics; @@ -49,9 +48,6 @@ namespace osu.Game.Screens.OnlinePlay [Cached] private readonly Bindable selectedRoom = new Bindable(); - [Cached] - private readonly Bindable currentFilter = new Bindable(new FilterCriteria()); - [Cached] private readonly OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker(); diff --git a/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs b/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs index 2e56c8a094..31b59be61e 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs @@ -5,14 +5,12 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.Rooms; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.OnlinePlay.Components; -using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Multiplayer; namespace osu.Game.Tests.Visual.Multiplayer @@ -32,9 +30,6 @@ namespace osu.Game.Tests.Visual.Multiplayer [Resolved] private OsuGameBase game { get; set; } - [Cached] - public readonly Bindable Filter = new Bindable(new FilterCriteria()); - public new readonly List Rooms = new List(); private int currentRoomId; diff --git a/osu.Game/Tests/Visual/OnlinePlay/IOnlinePlayTestSceneDependencies.cs b/osu.Game/Tests/Visual/OnlinePlay/IOnlinePlayTestSceneDependencies.cs index 6e1e831d9b..71acefb158 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/IOnlinePlayTestSceneDependencies.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/IOnlinePlayTestSceneDependencies.cs @@ -4,7 +4,6 @@ using osu.Framework.Bindables; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay; -using osu.Game.Screens.OnlinePlay.Lounge.Components; namespace osu.Game.Tests.Visual.OnlinePlay { @@ -23,11 +22,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay /// IRoomManager RoomManager { get; } - /// - /// The cached . - /// - Bindable Filter { get; } - /// /// The cached . /// diff --git a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs index 997c910dd4..8716646074 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay; -using osu.Game.Screens.OnlinePlay.Lounge.Components; namespace osu.Game.Tests.Visual.OnlinePlay { @@ -20,7 +19,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay { public Bindable SelectedRoom => OnlinePlayDependencies?.SelectedRoom; public IRoomManager RoomManager => OnlinePlayDependencies?.RoomManager; - public Bindable Filter => OnlinePlayDependencies?.Filter; public OngoingOperationTracker OngoingOperationTracker => OnlinePlayDependencies?.OngoingOperationTracker; public OnlinePlayBeatmapAvailabilityTracker AvailabilityTracker => OnlinePlayDependencies?.AvailabilityTracker; diff --git a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs index 05ba509a73..e39711b7ce 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics; using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Screens.OnlinePlay; -using osu.Game.Screens.OnlinePlay.Lounge.Components; namespace osu.Game.Tests.Visual.OnlinePlay { @@ -20,7 +19,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay { public Bindable SelectedRoom { get; } public IRoomManager RoomManager { get; } - public Bindable Filter { get; } public OngoingOperationTracker OngoingOperationTracker { get; } public OnlinePlayBeatmapAvailabilityTracker AvailabilityTracker { get; } @@ -36,7 +34,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay { SelectedRoom = new Bindable(); RoomManager = CreateRoomManager(); - Filter = new Bindable(new FilterCriteria()); OngoingOperationTracker = new OngoingOperationTracker(); AvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker(); @@ -44,7 +41,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay CacheAs(SelectedRoom); CacheAs(RoomManager); - CacheAs(Filter); CacheAs(OngoingOperationTracker); CacheAs(AvailabilityTracker); CacheAs(new OverlayColourProvider(OverlayColourScheme.Plum)); From c4a42c4db007af794104c65c8c768506eb4f5b12 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 17 Aug 2021 09:36:59 +0900 Subject: [PATCH 1230/2442] Fix BasicTestRoomManager overriding rooms --- .../Tests/Visual/OnlinePlay/BasicTestRoomManager.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs index d7c5a0a0e4..55fbb9f1a6 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs @@ -28,6 +28,8 @@ namespace osu.Game.Tests.Visual.OnlinePlay IBindableList IRoomManager.Rooms => Rooms; + private int currentRoomId; + public void CreateRoom(Room room, Action onSuccess = null, Action onError = null) { room.RoomID.Value ??= Rooms.Select(r => r.RoomID.Value).Where(id => id != null).Select(id => id.Value).DefaultIfEmpty().Max() + 1; @@ -76,9 +78,9 @@ namespace osu.Game.Tests.Visual.OnlinePlay { var room = new Room { - RoomID = { Value = i }, - Position = { Value = i }, - Name = { Value = $"Room {i}" }, + RoomID = { Value = currentRoomId }, + Position = { Value = currentRoomId }, + Name = { Value = $"Room {currentRoomId}" }, Host = { Value = new User { Username = "Host" } }, EndDate = { Value = DateTimeOffset.Now + TimeSpan.FromSeconds(10) }, Category = { Value = i % 2 == 0 ? RoomCategory.Spotlight : RoomCategory.Normal }, @@ -101,6 +103,8 @@ namespace osu.Game.Tests.Visual.OnlinePlay } CreateRoom(room); + + currentRoomId++; } } } From 1e282432c90397d976164da3f9e03b99876199da Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 17 Aug 2021 09:40:25 +0900 Subject: [PATCH 1231/2442] Fix password in a better way --- osu.Game/Screens/OnlinePlay/Components/RoomManager.cs | 3 ++- .../Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs index 43bf3a2ce5..3b6c1c8be0 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs @@ -60,7 +60,8 @@ namespace osu.Game.Screens.OnlinePlay.Components AddOrUpdateRoom(result); room.CopyFrom(result); // Also copy back to the source model, since this is likely to have been stored elsewhere. - onSuccess?.Invoke(result); + // The server may not contain all properties (such as password), so invoke success with the given room. + onSuccess?.Invoke(room); }; req.Failure += exception => diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs index 776307e20e..2d94b2328d 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs @@ -19,7 +19,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private MultiplayerClient multiplayerClient { get; set; } public override void CreateRoom(Room room, Action onSuccess = null, Action onError = null) - => base.CreateRoom(room, r => joinMultiplayerRoom(r, room.Password.Value, onSuccess, onError), onError); + => base.CreateRoom(room, r => joinMultiplayerRoom(r, r.Password.Value, onSuccess, onError), onError); public override void JoinRoom(Room room, string password = null, Action onSuccess = null, Action onError = null) { From b672d4b9366d0a695b8085b649c91c037b59878f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 17 Aug 2021 09:42:17 +0900 Subject: [PATCH 1232/2442] Refactor RequestHandlingMultiplayerRoomManager to avoid confusion --- .../Multiplayer/TestSceneMultiplayer.cs | 6 +++--- .../Multiplayer/TestMultiplayerClient.cs | 4 ++-- ...stRequestHandlingMultiplayerRoomManager.cs | 20 +++++++------------ 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index cf2f2f259e..8b96f5dc80 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -134,7 +134,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("create room", () => { - multiplayerScreen.RoomManager.AddRoom(new Room + multiplayerScreen.RoomManager.AddServerSideRoom(new Room { Name = { Value = "Test Room" }, Playlist = @@ -164,7 +164,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("create room", () => { - multiplayerScreen.RoomManager.AddRoom(new Room + multiplayerScreen.RoomManager.AddServerSideRoom(new Room { Name = { Value = "Test Room" }, Playlist = @@ -213,7 +213,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("create room", () => { - multiplayerScreen.RoomManager.AddRoom(new Room + multiplayerScreen.RoomManager.AddServerSideRoom(new Room { Name = { Value = "Test Room" }, Password = { Value = "password" }, diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index f2da66d666..2c0ca0b872 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -127,7 +127,7 @@ namespace osu.Game.Tests.Visual.Multiplayer protected override Task JoinRoom(long roomId, string? password = null) { - var apiRoom = roomManager.Rooms.Single(r => r.RoomID.Value == roomId); + var apiRoom = roomManager.ServerSideRooms.Single(r => r.RoomID.Value == roomId); if (password != apiRoom.Password.Value) throw new InvalidOperationException("Invalid password."); @@ -260,7 +260,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Debug.Assert(Room != null); - var apiRoom = roomManager.Rooms.Single(r => r.RoomID.Value == Room.RoomID); + var apiRoom = roomManager.ServerSideRooms.Single(r => r.RoomID.Value == Room.RoomID); var set = apiRoom.Playlist.FirstOrDefault(p => p.BeatmapID == beatmapId)?.Beatmap.Value.BeatmapSet ?? beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == beatmapId)?.BeatmapSet; diff --git a/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs b/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs index 31b59be61e..41102ae7b5 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs @@ -30,7 +30,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [Resolved] private OsuGameBase game { get; set; } - public new readonly List Rooms = new List(); + public readonly List ServerSideRooms = new List(); private int currentRoomId; private int currentPlaylistItemId; @@ -55,7 +55,7 @@ namespace osu.Game.Tests.Visual.Multiplayer apiRoom.HasPassword.Value = !string.IsNullOrEmpty(createRoomRequest.Room.Password.Value); apiRoom.Password.Value = createRoomRequest.Room.Password.Value; - AddRoom(apiRoom); + AddServerSideRoom(apiRoom); var responseRoom = new APICreatedRoom(); responseRoom.CopyFrom(createResponseRoom(apiRoom, false)); @@ -65,7 +65,7 @@ namespace osu.Game.Tests.Visual.Multiplayer case JoinRoomRequest joinRoomRequest: { - var room = Rooms.Single(r => r.RoomID.Value == joinRoomRequest.Room.RoomID.Value); + var room = ServerSideRooms.Single(r => r.RoomID.Value == joinRoomRequest.Room.RoomID.Value); if (joinRoomRequest.Password != room.Password.Value) { @@ -84,14 +84,14 @@ namespace osu.Game.Tests.Visual.Multiplayer case GetRoomsRequest getRoomsRequest: var roomsWithoutParticipants = new List(); - foreach (var r in Rooms) + foreach (var r in ServerSideRooms) roomsWithoutParticipants.Add(createResponseRoom(r, false)); getRoomsRequest.TriggerSuccess(roomsWithoutParticipants); return true; case GetRoomRequest getRoomRequest: - getRoomRequest.TriggerSuccess(createResponseRoom(Rooms.Single(r => r.RoomID.Value == getRoomRequest.RoomId), true)); + getRoomRequest.TriggerSuccess(createResponseRoom(ServerSideRooms.Single(r => r.RoomID.Value == getRoomRequest.RoomId), true)); return true; case GetBeatmapSetRequest getBeatmapSetRequest: @@ -127,17 +127,15 @@ namespace osu.Game.Tests.Visual.Multiplayer }; } - public void AddRoom(Room room) + public void AddServerSideRoom(Room room) { room.RoomID.Value ??= currentRoomId++; for (int i = 0; i < room.Playlist.Count; i++) room.Playlist[i].ID = currentPlaylistItemId++; - Rooms.Add(room); + ServerSideRooms.Add(room); } - public new void RemoveRoom(Room room) => base.RemoveRoom(room); - private Room createResponseRoom(Room room, bool withParticipants) { var responseRoom = new Room(); @@ -147,9 +145,5 @@ namespace osu.Game.Tests.Visual.Multiplayer responseRoom.RecentParticipants.Clear(); return responseRoom; } - - public new void ClearRooms() => base.ClearRooms(); - - public new void Schedule(Action action) => base.Schedule(action); } } From 5214731dc1be8fdfe638143386319377ad839f54 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 17 Aug 2021 09:45:10 +0900 Subject: [PATCH 1233/2442] Refactor test a bit --- .../Visual/Multiplayer/TestSceneMultiplayer.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 8b96f5dc80..e618b28f40 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -44,6 +44,8 @@ namespace osu.Game.Tests.Visual.Multiplayer private TestMultiplayer multiplayerScreen; private TestMultiplayerClient client; + private TestRequestHandlingMultiplayerRoomManager roomManager => multiplayerScreen.RoomManager; + [Cached(typeof(UserLookupCache))] private UserLookupCache lookupCache = new TestUserLookupCache(); @@ -68,7 +70,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("load dependencies", () => { - client = new TestMultiplayerClient(multiplayerScreen.RoomManager); + client = new TestMultiplayerClient(roomManager); // The screen gets suspended so it stops receiving updates. Child = client; @@ -134,7 +136,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("create room", () => { - multiplayerScreen.RoomManager.AddServerSideRoom(new Room + roomManager.AddServerSideRoom(new Room { Name = { Value = "Test Room" }, Playlist = @@ -164,7 +166,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("create room", () => { - multiplayerScreen.RoomManager.AddServerSideRoom(new Room + roomManager.AddServerSideRoom(new Room { Name = { Value = "Test Room" }, Playlist = @@ -213,7 +215,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("create room", () => { - multiplayerScreen.RoomManager.AddServerSideRoom(new Room + roomManager.AddServerSideRoom(new Room { Name = { Value = "Test Room" }, Password = { Value = "password" }, From f2340c6dac64d6c7dda95e84699586e35d81b964 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 17 Aug 2021 09:48:33 +0900 Subject: [PATCH 1234/2442] Privatise mutable list --- .../Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs b/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs index 41102ae7b5..c3a944f93c 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs @@ -30,7 +30,8 @@ namespace osu.Game.Tests.Visual.Multiplayer [Resolved] private OsuGameBase game { get; set; } - public readonly List ServerSideRooms = new List(); + public IReadOnlyList ServerSideRooms => serverSideRooms; + private readonly List serverSideRooms = new List(); private int currentRoomId; private int currentPlaylistItemId; @@ -133,7 +134,7 @@ namespace osu.Game.Tests.Visual.Multiplayer for (int i = 0; i < room.Playlist.Count; i++) room.Playlist[i].ID = currentPlaylistItemId++; - ServerSideRooms.Add(room); + serverSideRooms.Add(room); } private Room createResponseRoom(Room room, bool withParticipants) From 060ba0692d27a42507bfcc1fedae13e89f0c8408 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 17 Aug 2021 04:27:04 +0300 Subject: [PATCH 1235/2442] Add hash code support for `Mod` --- osu.Game/Rulesets/Mods/Mod.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index f2fd02c652..a99ddc6924 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -197,6 +197,18 @@ namespace osu.Game.Rulesets.Mods ModUtils.GetSettingUnderlyingValue(pair.Item2.GetValue(other)))); } + public override int GetHashCode() + { + var hashCode = new HashCode(); + + hashCode.Add(GetType()); + + foreach (var (_, prop) in this.GetSettingsSourceProperties()) + hashCode.Add(ModUtils.GetSettingUnderlyingValue(prop.GetValue(this))); + + return hashCode.ToHashCode(); + } + /// /// Reset all custom settings for this mod back to their defaults. /// From 0291cd5ae2eb269cf10512ce588ac5c762379ebd Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 17 Aug 2021 04:27:43 +0300 Subject: [PATCH 1236/2442] Consider mod equality in difficulty cache lookup rather than acronym --- osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index 6ed623d0c0..8bc043a2e9 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -297,7 +297,7 @@ namespace osu.Game.Beatmaps public bool Equals(DifficultyCacheLookup other) => Beatmap.ID == other.Beatmap.ID && Ruleset.ID == other.Ruleset.ID - && OrderedMods.Select(m => m.Acronym).SequenceEqual(other.OrderedMods.Select(m => m.Acronym)); + && OrderedMods.SequenceEqual(other.OrderedMods); public override int GetHashCode() { @@ -307,7 +307,7 @@ namespace osu.Game.Beatmaps hashCode.Add(Ruleset.ID); foreach (var mod in OrderedMods) - hashCode.Add(mod.Acronym); + hashCode.Add(mod); return hashCode.ToHashCode(); } From 32ba5255558f6057831f87123135aa64955baab8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 17 Aug 2021 04:28:34 +0300 Subject: [PATCH 1237/2442] Track changes to mod settings in beatmap difficulty cache with 100ms debouncing --- osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 23 +++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index 8bc043a2e9..5025e4ad4b 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -14,6 +14,7 @@ using osu.Framework.Lists; using osu.Framework.Logging; using osu.Framework.Threading; using osu.Framework.Utils; +using osu.Game.Configuration; using osu.Game.Database; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; @@ -56,12 +57,28 @@ namespace osu.Game.Beatmaps [Resolved] private Bindable> currentMods { get; set; } + private ModSettingChangeTracker modSettingChangeTracker; + private ScheduledDelegate debouncedModSettingsChange; + protected override void LoadComplete() { base.LoadComplete(); currentRuleset.BindValueChanged(_ => updateTrackedBindables()); - currentMods.BindValueChanged(_ => updateTrackedBindables(), true); + + currentMods.BindValueChanged(mods => + { + modSettingChangeTracker?.Dispose(); + + updateTrackedBindables(); + + modSettingChangeTracker = new ModSettingChangeTracker(mods.NewValue); + modSettingChangeTracker.SettingChanged += _ => + { + debouncedModSettingsChange?.Cancel(); + debouncedModSettingsChange = Scheduler.AddDelayed(updateTrackedBindables, 100); + }; + }, true); } /// @@ -84,7 +101,7 @@ namespace osu.Game.Beatmaps /// Retrieves a bindable containing the star difficulty of a with a given and combination. /// /// - /// The bindable will not update to follow the currently-selected ruleset and mods. + /// The bindable will not update to follow the currently-selected ruleset and mods or its settings. /// /// The to get the difficulty of. /// The to get the difficulty with. If null, the 's ruleset is used. @@ -275,6 +292,8 @@ namespace osu.Game.Beatmaps { base.Dispose(isDisposing); + modSettingChangeTracker?.Dispose(); + cancelTrackedBindableUpdate(); updateScheduler?.Dispose(); } From b419ea716b307eb54fca5892024e5066b81920f0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 17 Aug 2021 04:29:15 +0300 Subject: [PATCH 1238/2442] Refactor beatmap info wedge to not fully refresh on star difficulty change Makes it look awkward when changing difficulty via mod settings for example. Now the changes should instead only affect the displayed components which consume it directly. --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 104 ++++++++++---------- 1 file changed, 54 insertions(+), 50 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 5b4e077100..678e7d5d73 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -21,6 +21,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; using osu.Framework.Logging; using osu.Game.Configuration; @@ -38,6 +39,8 @@ namespace osu.Game.Screens.Select public const float BORDER_THICKNESS = 2.5f; private const float shear_width = 36.75f; + private const float transition_duration = 250; + private static readonly Vector2 wedged_container_shear = new Vector2(shear_width / SongSelect.WEDGE_HEIGHT, 0); [Resolved] @@ -46,11 +49,6 @@ namespace osu.Game.Screens.Select [Resolved] private IBindable> mods { get; set; } - [Resolved] - private BeatmapDifficultyCache difficultyCache { get; set; } - - private IBindable beatmapDifficulty; - protected Container DisplayedContent { get; private set; } protected WedgeInfoText Info { get; private set; } @@ -81,20 +79,18 @@ namespace osu.Game.Screens.Select { this.MoveToX(0, 800, Easing.OutQuint); this.RotateTo(0, 800, Easing.OutQuint); - this.FadeIn(250); + this.FadeIn(transition_duration); } protected override void PopOut() { this.MoveToX(-100, 800, Easing.In); this.RotateTo(10, 800, Easing.In); - this.FadeOut(500, Easing.In); + this.FadeOut(transition_duration * 2, Easing.In); } private WorkingBeatmap beatmap; - private CancellationTokenSource cancellationSource; - public WorkingBeatmap Beatmap { get => beatmap; @@ -103,12 +99,6 @@ namespace osu.Game.Screens.Select if (beatmap == value) return; beatmap = value; - cancellationSource?.Cancel(); - cancellationSource = new CancellationTokenSource(); - - beatmapDifficulty?.UnbindAll(); - beatmapDifficulty = difficultyCache.GetBindableDifficulty(beatmap.BeatmapInfo, cancellationSource.Token); - beatmapDifficulty.BindValueChanged(_ => updateDisplay()); updateDisplay(); } @@ -128,7 +118,7 @@ namespace osu.Game.Screens.Select { State.Value = beatmap == null ? Visibility.Hidden : Visibility.Visible; - DisplayedContent?.FadeOut(250); + DisplayedContent?.FadeOut(transition_duration); DisplayedContent?.Expire(); DisplayedContent = null; } @@ -147,7 +137,7 @@ namespace osu.Game.Screens.Select Children = new Drawable[] { new BeatmapInfoWedgeBackground(beatmap), - Info = new WedgeInfoText(beatmap, ruleset.Value, mods.Value, beatmapDifficulty.Value ?? new StarDifficulty()), + Info = new WedgeInfoText(beatmap, ruleset.Value, mods.Value), } }, loaded => { @@ -160,12 +150,6 @@ namespace osu.Game.Screens.Select } } - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - cancellationSource?.Cancel(); - } - public class WedgeInfoText : Container { public OsuSpriteText VersionLabel { get; private set; } @@ -174,6 +158,9 @@ namespace osu.Game.Screens.Select public BeatmapSetOnlineStatusPill StatusPill { get; private set; } public FillFlowContainer MapperContainer { get; private set; } + private DifficultyColourBar difficultyColourBar; + private StarRatingDisplay starRatingDisplay; + private ILocalisedBindableString titleBinding; private ILocalisedBindableString artistBinding; private FillFlowContainer infoLabelContainer; @@ -182,20 +169,21 @@ namespace osu.Game.Screens.Select private readonly WorkingBeatmap beatmap; private readonly RulesetInfo ruleset; private readonly IReadOnlyList mods; - private readonly StarDifficulty starDifficulty; private ModSettingChangeTracker settingChangeTracker; - public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset, IReadOnlyList mods, StarDifficulty difficulty) + public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset, IReadOnlyList mods) { this.beatmap = beatmap; ruleset = userRuleset ?? beatmap.BeatmapInfo.Ruleset; this.mods = mods; - starDifficulty = difficulty; } + private CancellationTokenSource cancellationSource; + private IBindable starDifficulty; + [BackgroundDependencyLoader] - private void load(LocalisationManager localisation) + private void load(LocalisationManager localisation, BeatmapDifficultyCache difficultyCache) { var beatmapInfo = beatmap.BeatmapInfo; var metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); @@ -207,7 +195,7 @@ namespace osu.Game.Screens.Select Children = new Drawable[] { - new DifficultyColourBar(starDifficulty) + difficultyColourBar = new DifficultyColourBar { RelativeSizeAxes = Axes.Y, Width = 20, @@ -241,14 +229,15 @@ namespace osu.Game.Screens.Select Padding = new MarginPadding { Top = 14, Right = shear_width / 2 }, AutoSizeAxes = Axes.Both, Shear = wedged_container_shear, - Children = new[] + Children = new Drawable[] { - createStarRatingDisplay(starDifficulty).With(display => + starRatingDisplay = new StarRatingDisplay(default) { - display.Anchor = Anchor.TopRight; - display.Origin = Anchor.TopRight; - display.Shear = -wedged_container_shear; - }), + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Shear = -wedged_container_shear, + Alpha = 0f, + }, StatusPill = new BeatmapSetOnlineStatusPill { Anchor = Anchor.TopRight, @@ -304,6 +293,15 @@ namespace osu.Game.Screens.Select titleBinding.BindValueChanged(_ => setMetadata(metadata.Source)); artistBinding.BindValueChanged(_ => setMetadata(metadata.Source), true); + starDifficulty = difficultyCache.GetBindableDifficulty(beatmapInfo, (cancellationSource = new CancellationTokenSource()).Token); + starDifficulty.BindValueChanged(s => + { + difficultyColourBar.Current.Value = s.NewValue ?? default; + + starRatingDisplay.FadeIn(transition_duration); + starRatingDisplay.Current.Value = s.NewValue ?? default; + }); + // no difficulty means it can't have a status to show if (beatmapInfo.Version == null) StatusPill.Hide(); @@ -311,13 +309,6 @@ namespace osu.Game.Screens.Select addInfoLabels(); } - private static Drawable createStarRatingDisplay(StarDifficulty difficulty) => difficulty.Stars > 0 - ? new StarRatingDisplay(difficulty) - { - Margin = new MarginPadding { Bottom = 5 } - } - : Empty(); - private void setMetadata(string source) { ArtistLabel.Text = artistBinding.Value; @@ -429,6 +420,7 @@ namespace osu.Game.Screens.Select { base.Dispose(isDisposing); settingChangeTracker?.Dispose(); + cancellationSource?.Cancel(); } public class InfoLabel : Container, IHasTooltip @@ -490,41 +482,53 @@ namespace osu.Game.Screens.Select } } - private class DifficultyColourBar : Container + public class DifficultyColourBar : Container, IHasCurrentValue { - private readonly StarDifficulty difficulty; + private readonly BindableWithCurrent current = new BindableWithCurrent(); - public DifficultyColourBar(StarDifficulty difficulty) + public Bindable Current { - this.difficulty = difficulty; + get => current.Current; + set => current.Current = value; } + [Resolved] + private OsuColour colours { get; set; } + [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load() { const float full_opacity_ratio = 0.7f; - var difficultyColour = colours.ForStarDifficulty(difficulty.Stars); - Children = new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, - Colour = difficultyColour, Width = full_opacity_ratio, }, new Box { RelativeSizeAxes = Axes.Both, RelativePositionAxes = Axes.Both, - Colour = difficultyColour, Alpha = 0.5f, X = full_opacity_ratio, Width = 1 - full_opacity_ratio, } }; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Current.BindValueChanged(c => + { + this.FadeColour(colours.ForStarDifficulty(c.NewValue.Stars), transition_duration, Easing.OutQuint); + }, true); + + FinishTransforms(true); + } } } } From a329216ff350d2f84e66d48e8a4fdbf03e4d9dd0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 17 Aug 2021 04:28:07 +0300 Subject: [PATCH 1239/2442] Convert beatmap difficulty test into test scene and extend coverage --- .../Beatmaps/BeatmapDifficultyCacheTest.cs | 56 ------- .../TestSceneBeatmapDifficultyCache.cs | 158 ++++++++++++++++++ 2 files changed, 158 insertions(+), 56 deletions(-) delete mode 100644 osu.Game.Tests/Beatmaps/BeatmapDifficultyCacheTest.cs create mode 100644 osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs diff --git a/osu.Game.Tests/Beatmaps/BeatmapDifficultyCacheTest.cs b/osu.Game.Tests/Beatmaps/BeatmapDifficultyCacheTest.cs deleted file mode 100644 index d407c0663f..0000000000 --- a/osu.Game.Tests/Beatmaps/BeatmapDifficultyCacheTest.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using NUnit.Framework; -using osu.Game.Beatmaps; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Mods; - -namespace osu.Game.Tests.Beatmaps -{ - [TestFixture] - public class BeatmapDifficultyCacheTest - { - [Test] - public void TestKeyEqualsWithDifferentModInstances() - { - var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() }); - var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() }); - - Assert.That(key1, Is.EqualTo(key2)); - } - - [Test] - public void TestKeyEqualsWithDifferentModOrder() - { - var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() }); - var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHidden(), new OsuModHardRock() }); - - Assert.That(key1, Is.EqualTo(key2)); - } - - [TestCase(1.3, DifficultyRating.Easy)] - [TestCase(1.993, DifficultyRating.Easy)] - [TestCase(1.998, DifficultyRating.Normal)] - [TestCase(2.4, DifficultyRating.Normal)] - [TestCase(2.693, DifficultyRating.Normal)] - [TestCase(2.698, DifficultyRating.Hard)] - [TestCase(3.5, DifficultyRating.Hard)] - [TestCase(3.993, DifficultyRating.Hard)] - [TestCase(3.997, DifficultyRating.Insane)] - [TestCase(5.0, DifficultyRating.Insane)] - [TestCase(5.292, DifficultyRating.Insane)] - [TestCase(5.297, DifficultyRating.Expert)] - [TestCase(6.2, DifficultyRating.Expert)] - [TestCase(6.493, DifficultyRating.Expert)] - [TestCase(6.498, DifficultyRating.ExpertPlus)] - [TestCase(8.3, DifficultyRating.ExpertPlus)] - public void TestDifficultyRatingMapping(double starRating, DifficultyRating expectedBracket) - { - var actualBracket = BeatmapDifficultyCache.GetDifficultyRating(starRating); - - Assert.AreEqual(expectedBracket, actualBracket); - } - } -} diff --git a/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs b/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs new file mode 100644 index 0000000000..b6ba5b748a --- /dev/null +++ b/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs @@ -0,0 +1,158 @@ +// 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 System.Threading; +using System.Threading.Tasks; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Tests.Beatmaps.IO; +using osu.Game.Tests.Visual; + +namespace osu.Game.Tests.Beatmaps +{ + [HeadlessTest] + public class TestSceneBeatmapDifficultyCache : OsuTestScene + { + public const double BASE_STARS = 5.55; + + private BeatmapSetInfo importedSet; + + [Resolved] + private BeatmapManager beatmaps { get; set; } + + private TestBeatmapDifficultyCache difficultyCache; + + private IBindable starDifficultyBindable; + private Queue> starDifficultyChangesQueue; + + [BackgroundDependencyLoader] + private void load(OsuGameBase osu) + { + importedSet = ImportBeatmapTest.LoadQuickOszIntoOsu(osu).Result; + } + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("setup difficulty cache", () => + { + SelectedMods.Value = Array.Empty(); + + Child = difficultyCache = new TestBeatmapDifficultyCache(); + + starDifficultyChangesQueue = new Queue>(); + starDifficultyBindable = difficultyCache.GetBindableDifficulty(importedSet.Beatmaps.First()); + starDifficultyBindable.BindValueChanged(starDifficultyChangesQueue.Enqueue); + }); + + AddAssert($"star difficulty -> {BASE_STARS}", () => + starDifficultyChangesQueue.Dequeue().NewValue?.Stars == BASE_STARS && + starDifficultyChangesQueue.Count == 0); + } + + [Test] + public void TestStarDifficultyChangesOnModSettings() + { + OsuModDoubleTime dt = null; + + AddStep("change selected mod to DT", () => SelectedMods.Value = new[] { dt = new OsuModDoubleTime { SpeedChange = { Value = 1.5 } } }); + AddAssert($"star difficulty -> {BASE_STARS + 1.5}", () => + starDifficultyChangesQueue.Dequeue().NewValue?.Stars == BASE_STARS + 1.5 && + starDifficultyChangesQueue.Count == 0); + + AddStep("change DT speed to 1.25", () => dt.SpeedChange.Value = 1.25); + AddAssert($"star difficulty -> {BASE_STARS + 1.25}", () => + starDifficultyChangesQueue.Dequeue().NewValue?.Stars == BASE_STARS + 1.25 && + starDifficultyChangesQueue.Count == 0); + + AddStep("change selected mod to NC", () => SelectedMods.Value = new[] { new OsuModNightcore { SpeedChange = { Value = 1.75 } } }); + AddAssert($"star difficulty -> {BASE_STARS + 1.75}", () => + starDifficultyChangesQueue.Dequeue().NewValue?.Stars == BASE_STARS + 1.75 && + starDifficultyChangesQueue.Count == 0); + } + + [Test] + public void TestKeyEqualsWithDifferentModInstances() + { + var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() }); + var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() }); + + Assert.That(key1, Is.EqualTo(key2)); + Assert.That(key1.GetHashCode(), Is.EqualTo(key2.GetHashCode())); + } + + [Test] + public void TestKeyEqualsWithDifferentModOrder() + { + var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() }); + var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHidden(), new OsuModHardRock() }); + + Assert.That(key1, Is.EqualTo(key2)); + Assert.That(key1.GetHashCode(), Is.EqualTo(key2.GetHashCode())); + } + + [Test] + public void TestKeyDoesntEqualWithDifferentModSettings() + { + var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.1 } } }); + var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.9 } } }); + + Assert.That(key1, Is.Not.EqualTo(key2)); + Assert.That(key1.GetHashCode(), Is.Not.EqualTo(key2.GetHashCode())); + } + + [Test] + public void TestKeyEqualWithMatchingModSettings() + { + var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } }); + var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } }); + + Assert.That(key1, Is.EqualTo(key2)); + Assert.That(key1.GetHashCode(), Is.EqualTo(key2.GetHashCode())); + } + + [TestCase(1.3, DifficultyRating.Easy)] + [TestCase(1.993, DifficultyRating.Easy)] + [TestCase(1.998, DifficultyRating.Normal)] + [TestCase(2.4, DifficultyRating.Normal)] + [TestCase(2.693, DifficultyRating.Normal)] + [TestCase(2.698, DifficultyRating.Hard)] + [TestCase(3.5, DifficultyRating.Hard)] + [TestCase(3.993, DifficultyRating.Hard)] + [TestCase(3.997, DifficultyRating.Insane)] + [TestCase(5.0, DifficultyRating.Insane)] + [TestCase(5.292, DifficultyRating.Insane)] + [TestCase(5.297, DifficultyRating.Expert)] + [TestCase(6.2, DifficultyRating.Expert)] + [TestCase(6.493, DifficultyRating.Expert)] + [TestCase(6.498, DifficultyRating.ExpertPlus)] + [TestCase(8.3, DifficultyRating.ExpertPlus)] + public void TestDifficultyRatingMapping(double starRating, DifficultyRating expectedBracket) + { + var actualBracket = BeatmapDifficultyCache.GetDifficultyRating(starRating); + + Assert.AreEqual(expectedBracket, actualBracket); + } + + private class TestBeatmapDifficultyCache : BeatmapDifficultyCache + { + protected override Task ComputeValueAsync(DifficultyCacheLookup lookup, CancellationToken token = default) + { + var rateAdjust = lookup.OrderedMods.OfType().SingleOrDefault(); + if (rateAdjust != null) + return Task.FromResult(new StarDifficulty(BASE_STARS + rateAdjust.SpeedChange.Value, 0)); + + return Task.FromResult(new StarDifficulty(BASE_STARS, 0)); + } + } + } +} From eb6c6830bcd977430a710efaf21e65f38fa2c5f9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 17 Aug 2021 05:45:03 +0300 Subject: [PATCH 1240/2442] Add visual test slider for changing star difficulty in beatmap wedge --- .../Visual/SongSelect/TestSceneBeatmapInfoWedge.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index a416fd4daf..2b38c4f936 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs @@ -8,6 +8,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; @@ -65,6 +66,12 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("show", () => { infoWedge.Show(); }); + AddSliderStep("change star difficulty", 0, 11.9, 5.55, v => + { + foreach (var hasCurrentValue in infoWedge.Info.ChildrenOfType>()) + hasCurrentValue.Current.Value = new StarDifficulty(v, 0); + }); + foreach (var rulesetInfo in rulesets.AvailableRulesets) { var instance = rulesetInfo.CreateInstance(); From adb4fd5a2bcdf08ebc6bfe47bc45ba7c41e2c8c1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Aug 2021 12:31:33 +0900 Subject: [PATCH 1241/2442] Load only sections content asynchronously, showing the header initially --- osu.Game/Overlays/SettingsPanel.cs | 150 ++++++++++++++++------------- 1 file changed, 82 insertions(+), 68 deletions(-) diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index 6593b6cb1e..fea797287e 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using osuTK; using osuTK.Graphics; using osu.Framework.Allocation; @@ -61,6 +62,10 @@ namespace osu.Game.Overlays private LoadingLayer loading; + private readonly List loadableSections = new List(); + + private Task sectionsLoadingTask; + protected SettingsPanel(bool showSidebar) { this.showSidebar = showSidebar; @@ -96,7 +101,7 @@ namespace osu.Game.Overlays } }; - SectionsContainer = new SettingsSectionsContainer + Add(SectionsContainer = new SettingsSectionsContainer { Masking = true, RelativeSizeAxes = Axes.Both, @@ -113,8 +118,8 @@ namespace osu.Game.Overlays Bottom = 20 }, }, - Footer = CreateFooter() - }; + Footer = CreateFooter().With(f => f.Alpha = 0) + }); if (showSidebar) { @@ -124,76 +129,13 @@ namespace osu.Game.Overlays CreateSections()?.ForEach(AddSection); } - private void ensureContentLoaded() - { - if (SectionsContainer.LoadState > LoadState.NotLoaded) - return; - - LoadComponentAsync(SectionsContainer, d => - { - ContentContainer.Add(d); - d.FadeInFromZero(750, Easing.OutQuint); - loading.Hide(); - - searchTextBox.Current.BindValueChanged(term => SectionsContainer.SearchContainer.SearchTerm = term.NewValue, true); - searchTextBox.TakeFocus(); - - if (Sidebar == null) - return; - - LoadComponentsAsync(createSidebarButtons(), buttons => - { - float delay = 0; - - foreach (var button in buttons) - { - Sidebar.Add(button); - - button.FadeOut() - .Delay(delay) - .FadeInFromZero(1000, Easing.OutQuint); - - delay += 30; - } - - SectionsContainer.SelectedSection.BindValueChanged(section => - { - if (selectedSidebarButton != null) - selectedSidebarButton.Selected = false; - - selectedSidebarButton = Sidebar.Children.Single(b => b.Section == section.NewValue); - selectedSidebarButton.Selected = true; - }, true); - }); - }); - } - - private IEnumerable createSidebarButtons() - { - foreach (var section in SectionsContainer) - { - yield return new SidebarButton - { - Section = section, - Action = () => - { - if (!SectionsContainer.IsLoaded) - return; - - SectionsContainer.ScrollTo(section); - Sidebar.State = ExpandedState.Contracted; - }, - }; - } - } - protected void AddSection(SettingsSection section) { if (IsLoaded) // just to keep things simple. can be accommodated for if we ever need it. throw new InvalidOperationException("All sections must be added before the panel is loaded."); - SectionsContainer.Add(section); + loadableSections.Add(section); } protected virtual Drawable CreateHeader() => new Container(); @@ -210,7 +152,7 @@ namespace osu.Game.Overlays // this is done as there is still a brief stutter during load completion which is more visible if the transition is in progress. // the eventual goal would be to remove the need for this by splitting up load into smaller work pieces, or fixing the remaining // load complete overheads. - Scheduler.AddDelayed(ensureContentLoaded, TRANSITION_LENGTH / 3); + Scheduler.AddDelayed(loadSections, TRANSITION_LENGTH / 3); Sidebar?.MoveToX(0, TRANSITION_LENGTH, Easing.OutQuint); this.FadeTo(1, TRANSITION_LENGTH, Easing.OutQuint); @@ -250,6 +192,78 @@ namespace osu.Game.Overlays Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 }; } + private const double fade_in_duration = 1000; + + private void loadSections() + { + if (sectionsLoadingTask != null) + return; + + sectionsLoadingTask = LoadComponentsAsync(loadableSections, sections => + { + SectionsContainer.AddRange(sections); + SectionsContainer.Footer.FadeInFromZero(fade_in_duration, Easing.OutQuint); + SectionsContainer.SearchContainer.FadeInFromZero(fade_in_duration, Easing.OutQuint); + + loading.Hide(); + + searchTextBox.Current.BindValueChanged(term => SectionsContainer.SearchContainer.SearchTerm = term.NewValue, true); + searchTextBox.TakeFocus(); + + loadSidebarButtons(); + }); + } + + private void loadSidebarButtons() + { + if (Sidebar == null) + return; + + LoadComponentsAsync(createSidebarButtons(), buttons => + { + float delay = 0; + + foreach (var button in buttons) + { + Sidebar.Add(button); + + button.FadeOut() + .Delay(delay) + .FadeInFromZero(fade_in_duration, Easing.OutQuint); + + delay += 40; + } + + SectionsContainer.SelectedSection.BindValueChanged(section => + { + if (selectedSidebarButton != null) + selectedSidebarButton.Selected = false; + + selectedSidebarButton = Sidebar.Children.Single(b => b.Section == section.NewValue); + selectedSidebarButton.Selected = true; + }, true); + }); + } + + private IEnumerable createSidebarButtons() + { + foreach (var section in SectionsContainer) + { + yield return new SidebarButton + { + Section = section, + Action = () => + { + if (!SectionsContainer.IsLoaded) + return; + + SectionsContainer.ScrollTo(section); + Sidebar.State = ExpandedState.Contracted; + }, + }; + } + } + private class NonMaskedContent : Container { // masking breaks the pan-out transform with nested sub-settings panels. From 212842c5373bda3b369e1ea9d9d7535637634684 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Aug 2021 12:38:44 +0900 Subject: [PATCH 1242/2442] Fix initial `LayoutSettings` animation in a more reliable way --- .../Sections/Graphics/LayoutSettings.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 277e344d84..d29f5fef39 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -175,16 +175,14 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics scalingMode.BindValueChanged(mode => { scalingSettings.ClearTransforms(); - scalingSettings.AutoSizeDuration = transition_duration; scalingSettings.AutoSizeEasing = Easing.OutQuint; - scalingSettings.AutoSizeAxes = mode.NewValue != ScalingMode.Off ? Axes.Y : Axes.None; - if (mode.NewValue == ScalingMode.Off) - scalingSettings.ResizeHeightTo(0, transition_duration, Easing.OutQuint); + updateScalingModeVisibility(); + }); - scalingSettings.ForEach(s => s.TransferValueOnCommit = mode.NewValue == ScalingMode.Everything); - }, true); + // initial update bypasses transforms + updateScalingModeVisibility(); void updateResolutionDropdown() { @@ -193,6 +191,15 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics else resolutionDropdown.Hide(); } + + void updateScalingModeVisibility() + { + if (scalingMode.Value == ScalingMode.Off) + scalingSettings.ResizeHeightTo(0, transition_duration, Easing.OutQuint); + + scalingSettings.AutoSizeAxes = scalingMode.Value != ScalingMode.Off ? Axes.Y : Axes.None; + scalingSettings.ForEach(s => s.TransferValueOnCommit = scalingMode.Value == ScalingMode.Everything); + } } private void bindPreviewEvent(Bindable bindable) From bc86bafc0898b2d6d288ef81b2f3a10f4eb1331d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Aug 2021 12:48:30 +0900 Subject: [PATCH 1243/2442] Fix `RestoreDefaultValueButton`'s colour weirdness --- osu.Game/Graphics/UserInterface/OsuButton.cs | 1 + osu.Game/Overlays/RestoreDefaultValueButton.cs | 11 ++++------- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs index cd9ca9f87f..82a3e73b84 100644 --- a/osu.Game/Graphics/UserInterface/OsuButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuButton.cs @@ -36,6 +36,7 @@ namespace osu.Game.Graphics.UserInterface public Color4 BackgroundColour { + get => backgroundColour ?? Color4.White; set { backgroundColour = value; diff --git a/osu.Game/Overlays/RestoreDefaultValueButton.cs b/osu.Game/Overlays/RestoreDefaultValueButton.cs index 62f5222012..87a294cc10 100644 --- a/osu.Game/Overlays/RestoreDefaultValueButton.cs +++ b/osu.Game/Overlays/RestoreDefaultValueButton.cs @@ -45,8 +45,6 @@ namespace osu.Game.Overlays } } - private Color4 buttonColour; - private bool hovering; public RestoreDefaultValueButton() @@ -61,12 +59,11 @@ namespace osu.Game.Overlays private void load(OsuColour colour) { BackgroundColour = colour.Yellow; - buttonColour = colour.Yellow; Content.Width = 0.33f; Content.CornerRadius = 3; Content.EdgeEffect = new EdgeEffectParameters { - Colour = buttonColour.Opacity(0.1f), + Colour = BackgroundColour.Opacity(0.1f), Type = EdgeEffectType.Glow, Radius = 2, }; @@ -87,7 +84,7 @@ namespace osu.Game.Overlays // avoid unnecessary transforms on first display. Alpha = currentAlpha; - Colour = currentColour; + Background.Colour = currentColour; } public LocalisableString TooltipText => "revert to default"; @@ -108,7 +105,7 @@ namespace osu.Game.Overlays public void UpdateState() => Scheduler.AddOnce(updateState); private float currentAlpha => current.IsDefault ? 0f : hovering && !current.Disabled ? 1f : 0.65f; - private ColourInfo currentColour => current.Disabled ? Color4.Gray : buttonColour; + private ColourInfo currentColour => current.Disabled ? Color4.Gray : BackgroundColour; private void updateState() { @@ -116,7 +113,7 @@ namespace osu.Game.Overlays return; this.FadeTo(currentAlpha, 200, Easing.OutQuint); - this.FadeColour(currentColour, 200, Easing.OutQuint); + Background.FadeColour(currentColour, 200, Easing.OutQuint); } } } From 1e2c0031d7473302f2f39bee34f4e4d2d4088048 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 17 Aug 2021 13:34:44 +0900 Subject: [PATCH 1244/2442] Remove unused usings --- osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs b/osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs index dbac132faf..73bab31e82 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs @@ -2,8 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; -using System.Linq; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Mods; From 081524b6c862af923b6f1084e3182f0abd85f59f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 17 Aug 2021 13:44:21 +0900 Subject: [PATCH 1245/2442] Privatise setters --- osu.Game/Screens/Play/PlayerLoader.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index a6592e4d24..969527a758 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -46,14 +46,14 @@ namespace osu.Game.Screens.Play protected override bool PlayResumeSound => false; - protected BeatmapMetadataDisplay MetadataInfo; + protected BeatmapMetadataDisplay MetadataInfo { get; private set; } /// /// A fill flow containing the player settings groups, exposed for the ability to hide it from inheritors of the player loader. /// - protected FillFlowContainer PlayerSettings; + protected FillFlowContainer PlayerSettings { get; private set; } - protected VisualSettings VisualSettings; + protected VisualSettings VisualSettings { get; private set; } protected Task LoadTask { get; private set; } From 3a7b9bf096d1b3c259c8db8c7c44f9c99dc44ff5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 17 Aug 2021 08:56:49 +0300 Subject: [PATCH 1246/2442] Fix `MatchSettingsOverlay` not resetting focus on hide properly --- .../Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs index 2676453a7e..62a968b508 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs @@ -41,11 +41,13 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components protected override void PopIn() { + base.PopIn(); Settings.MoveToY(0, TRANSITION_DURATION, Easing.OutQuint); } protected override void PopOut() { + base.PopOut(); Settings.MoveToY(-1, TRANSITION_DURATION, Easing.InSine); } From 82eddeffefe1a33035cd44a7a7435c650c316d00 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Aug 2021 16:13:45 +0900 Subject: [PATCH 1247/2442] Add `LocalUserPlayInfo` interface to convey common information about player status --- osu.Desktop/Windows/GameplayWinKeyBlocker.cs | 8 ++++---- osu.Game/Input/ConfineMouseTracker.cs | 5 +++-- osu.Game/OsuGame.cs | 4 +++- osu.Game/Performance/HighPerformanceSession.cs | 5 +++-- osu.Game/Screens/Play/ILocalUserPlayInfo.cs | 17 +++++++++++++++++ osu.Game/Screens/Play/Player.cs | 4 +++- 6 files changed, 33 insertions(+), 10 deletions(-) create mode 100644 osu.Game/Screens/Play/ILocalUserPlayInfo.cs diff --git a/osu.Desktop/Windows/GameplayWinKeyBlocker.cs b/osu.Desktop/Windows/GameplayWinKeyBlocker.cs index efc3f21149..dbfd170ea1 100644 --- a/osu.Desktop/Windows/GameplayWinKeyBlocker.cs +++ b/osu.Desktop/Windows/GameplayWinKeyBlocker.cs @@ -5,23 +5,23 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Platform; -using osu.Game; using osu.Game.Configuration; +using osu.Game.Screens.Play; namespace osu.Desktop.Windows { public class GameplayWinKeyBlocker : Component { private Bindable disableWinKey; - private Bindable localUserPlaying; + private IBindable localUserPlaying; [Resolved] private GameHost host { get; set; } [BackgroundDependencyLoader] - private void load(OsuGame game, OsuConfigManager config) + private void load(ILocalUserPlayInfo localUserInfo, OsuConfigManager config) { - localUserPlaying = game.LocalUserPlaying.GetBoundCopy(); + localUserPlaying = localUserInfo.IsPlaying.GetBoundCopy(); localUserPlaying.BindValueChanged(_ => updateBlocking()); disableWinKey = config.GetBindable(OsuSetting.GameplayDisableWinKey); diff --git a/osu.Game/Input/ConfineMouseTracker.cs b/osu.Game/Input/ConfineMouseTracker.cs index 75d9c8debb..d2bf953dbc 100644 --- a/osu.Game/Input/ConfineMouseTracker.cs +++ b/osu.Game/Input/ConfineMouseTracker.cs @@ -7,6 +7,7 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Input; using osu.Game.Configuration; +using osu.Game.Screens.Play; namespace osu.Game.Input { @@ -24,14 +25,14 @@ namespace osu.Game.Input private IBindable localUserPlaying; [BackgroundDependencyLoader] - private void load(OsuGame game, FrameworkConfigManager frameworkConfigManager, OsuConfigManager osuConfigManager) + private void load(ILocalUserPlayInfo localUserInfo, FrameworkConfigManager frameworkConfigManager, OsuConfigManager osuConfigManager) { frameworkConfineMode = frameworkConfigManager.GetBindable(FrameworkSetting.ConfineMouseMode); frameworkWindowMode = frameworkConfigManager.GetBindable(FrameworkSetting.WindowMode); frameworkWindowMode.BindValueChanged(_ => updateConfineMode()); osuConfineMode = osuConfigManager.GetBindable(OsuSetting.ConfineMouseMode); - localUserPlaying = game.LocalUserPlaying.GetBoundCopy(); + localUserPlaying = localUserInfo.IsPlaying.GetBoundCopy(); osuConfineMode.ValueChanged += _ => updateConfineMode(); localUserPlaying.BindValueChanged(_ => updateConfineMode(), true); diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index fb682e0909..0db63df69b 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -62,7 +62,7 @@ namespace osu.Game /// The full osu! experience. Builds on top of to add menus and binding logic /// for initial components that are generally retrieved via DI. /// - public class OsuGame : OsuGameBase, IKeyBindingHandler + public class OsuGame : OsuGameBase, IKeyBindingHandler, ILocalUserPlayInfo { /// /// The amount of global offset to apply when a left/right anchored overlay is displayed (ie. settings or notifications). @@ -1085,5 +1085,7 @@ namespace osu.Game if (newScreen == null) Exit(); } + + IBindable ILocalUserPlayInfo.IsPlaying => LocalUserPlaying; } } diff --git a/osu.Game/Performance/HighPerformanceSession.cs b/osu.Game/Performance/HighPerformanceSession.cs index 661c1046f1..3ef0e0bf93 100644 --- a/osu.Game/Performance/HighPerformanceSession.cs +++ b/osu.Game/Performance/HighPerformanceSession.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Game.Screens.Play; namespace osu.Game.Performance { @@ -12,9 +13,9 @@ namespace osu.Game.Performance private readonly IBindable localUserPlaying = new Bindable(); [BackgroundDependencyLoader] - private void load(OsuGame game) + private void load(ILocalUserPlayInfo localUserInfo) { - localUserPlaying.BindTo(game.LocalUserPlaying); + localUserPlaying.BindTo(localUserInfo.IsPlaying); } protected override void LoadComplete() diff --git a/osu.Game/Screens/Play/ILocalUserPlayInfo.cs b/osu.Game/Screens/Play/ILocalUserPlayInfo.cs new file mode 100644 index 0000000000..64c3f97305 --- /dev/null +++ b/osu.Game/Screens/Play/ILocalUserPlayInfo.cs @@ -0,0 +1,17 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; + +namespace osu.Game.Screens.Play +{ + [Cached] + public interface ILocalUserPlayInfo + { + /// + /// Whether the local user is currently playing. + /// + public IBindable IsPlaying { get; } + } +} diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 09eaf1c543..5461c6ac6c 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -38,7 +38,7 @@ namespace osu.Game.Screens.Play { [Cached] [Cached(typeof(ISamplePlaybackDisabler))] - public abstract class Player : ScreenWithBeatmapBackground, ISamplePlaybackDisabler + public abstract class Player : ScreenWithBeatmapBackground, ISamplePlaybackDisabler, ILocalUserPlayInfo { /// /// The delay upon completion of the beatmap before displaying the results screen. @@ -1052,5 +1052,7 @@ namespace osu.Game.Screens.Play #endregion IBindable ISamplePlaybackDisabler.SamplePlaybackDisabled => samplePlaybackDisabled; + + IBindable ILocalUserPlayInfo.IsPlaying => LocalUserPlaying; } } From e3b29df29930e4e5f79072b4b762fad0f27fd31c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Aug 2021 14:39:08 +0900 Subject: [PATCH 1248/2442] Add test scene for `MultiplayerPlayer` --- .../Multiplayer/TestSceneMultiplayerPlayer.cs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs new file mode 100644 index 0000000000..80da7a7e5e --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs @@ -0,0 +1,38 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Testing; +using osu.Game.Rulesets.Osu; +using osu.Game.Screens.OnlinePlay.Multiplayer; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneMultiplayerPlayer : MultiplayerTestScene + { + private MultiplayerPlayer player; + + [SetUpSteps] + public override void SetUpSteps() + { + base.SetUpSteps(); + + AddStep("set beatmap", () => + { + Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); + }); + + AddStep("initialise gameplay", () => + { + Stack.Push(player = new MultiplayerPlayer(Client.CurrentMatchPlayingItem.Value, Client.Room?.Users.ToArray())); + }); + } + + [Test] + public void TestGameplay() + { + AddUntilStep("wait for gameplay start", () => player.LocalUserPlaying.Value); + } + } +} From 0d283aa6a348bef4702a7d1d3bf8010195c70098 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Aug 2021 14:39:22 +0900 Subject: [PATCH 1249/2442] Expose `LocalUserPlaying` from `Player` --- .../Visual/Gameplay/TestSceneOverlayActivation.cs | 2 -- osu.Game/Screens/Play/Player.cs | 6 ++++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneOverlayActivation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneOverlayActivation.cs index 4fa4c00981..b308f3d7d8 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneOverlayActivation.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneOverlayActivation.cs @@ -3,7 +3,6 @@ using System.Linq; using NUnit.Framework; -using osu.Framework.Bindables; using osu.Game.Overlays; using osu.Game.Rulesets; @@ -66,7 +65,6 @@ namespace osu.Game.Tests.Visual.Gameplay protected class OverlayTestPlayer : TestPlayer { public new OverlayActivation OverlayActivationMode => base.OverlayActivationMode.Value; - public new Bindable LocalUserPlaying => base.LocalUserPlaying; } } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 5461c6ac6c..59c7abb299 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -75,7 +75,9 @@ namespace osu.Game.Screens.Play private readonly Bindable storyboardReplacesBackground = new Bindable(); - protected readonly Bindable LocalUserPlaying = new Bindable(); + public IBindable LocalUserPlaying => localUserPlaying; + + private readonly Bindable localUserPlaying = new Bindable(); public int RestartCount; @@ -442,7 +444,7 @@ namespace osu.Game.Screens.Play { bool inGameplay = !DrawableRuleset.HasReplayLoaded.Value && !DrawableRuleset.IsPaused.Value && !breakTracker.IsBreakTime.Value; OverlayActivationMode.Value = inGameplay ? OverlayActivation.Disabled : OverlayActivation.UserTriggered; - LocalUserPlaying.Value = inGameplay; + localUserPlaying.Value = inGameplay; } private void updateSampleDisabledState() From 35b9f84c00efa0aacc6c01b0ecc00c129ac27a1d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Aug 2021 14:39:51 +0900 Subject: [PATCH 1250/2442] Expose `StandAloneChatDisplay.Textbox` --- osu.Game/Online/Chat/StandAloneChatDisplay.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs index 8b0caddbc6..160c620214 100644 --- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs +++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs @@ -22,7 +22,7 @@ namespace osu.Game.Online.Chat { public readonly Bindable Channel = new Bindable(); - private readonly FocusedTextBox textbox; + protected readonly FocusedTextBox Textbox; protected ChannelManager ChannelManager; @@ -54,7 +54,7 @@ namespace osu.Game.Online.Chat if (postingTextbox) { - AddInternal(textbox = new FocusedTextBox + AddInternal(Textbox = new FocusedTextBox { RelativeSizeAxes = Axes.X, Height = textbox_height, @@ -65,7 +65,7 @@ namespace osu.Game.Online.Chat Origin = Anchor.BottomLeft, }); - textbox.OnCommit += postMessage; + Textbox.OnCommit += postMessage; } Channel.BindValueChanged(channelChanged); @@ -82,7 +82,7 @@ namespace osu.Game.Online.Chat private void postMessage(TextBox sender, bool newtext) { - var text = textbox.Text.Trim(); + var text = Textbox.Text.Trim(); if (string.IsNullOrWhiteSpace(text)) return; @@ -92,7 +92,7 @@ namespace osu.Game.Online.Chat else ChannelManager?.PostMessage(text, target: Channel.Value); - textbox.Text = string.Empty; + Textbox.Text = string.Empty; } protected virtual ChatLine CreateMessage(Message message) => new StandAloneMessage(message); From b82f92d7b8913d039cae235a94488047e293aec4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Aug 2021 14:57:38 +0900 Subject: [PATCH 1251/2442] Adjust background colours of textbox in chat display --- osu.Game/Online/Chat/StandAloneChatDisplay.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs index 160c620214..9d23a089df 100644 --- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs +++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs @@ -30,6 +30,8 @@ namespace osu.Game.Online.Chat private readonly bool postingTextbox; + protected readonly Box Background; + private const float textbox_height = 30; /// @@ -44,7 +46,7 @@ namespace osu.Game.Online.Chat InternalChildren = new Drawable[] { - new Box + Background = new Box { Colour = Color4.Black, Alpha = 0.8f, @@ -54,7 +56,7 @@ namespace osu.Game.Online.Chat if (postingTextbox) { - AddInternal(Textbox = new FocusedTextBox + AddInternal(Textbox = new ChatTextBox { RelativeSizeAxes = Axes.X, Height = textbox_height, @@ -110,6 +112,17 @@ namespace osu.Game.Online.Chat AddInternal(drawableChannel); } + public class ChatTextBox : FocusedTextBox + { + protected override void LoadComplete() + { + base.LoadComplete(); + + BackgroundUnfocused = new Color4(10, 10, 10, 10); + BackgroundFocused = new Color4(10, 10, 10, 255); + } + } + public class StandAloneDrawableChannel : DrawableChannel { public Func CreateChatLineAction; From 30eee363dcac3226fa679853f4ed40d060643334 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Aug 2021 14:57:48 +0900 Subject: [PATCH 1252/2442] Add chat display during multiplayer gameplay --- .../Multiplayer/GameplayChatDisplay.cs | 62 +++++++++++++++++++ .../Multiplayer/MultiplayerPlayer.cs | 10 ++- 2 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs new file mode 100644 index 0000000000..5b33fa1844 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs @@ -0,0 +1,62 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Screens.OnlinePlay.Match.Components; +using osu.Game.Screens.Play; + +namespace osu.Game.Screens.OnlinePlay.Multiplayer +{ + public class GameplayChatDisplay : MatchChatDisplay + { + [Resolved] + private ILocalUserPlayInfo localUserInfo { get; set; } + + private IBindable localUserPlaying = new Bindable(); + + public Bindable Expanded = new Bindable(); + + private const float height = 100; + + public GameplayChatDisplay() + { + RelativeSizeAxes = Axes.X; + + Background.Alpha = 0.2f; + } + + private void expandedChanged(ValueChangedEvent expanded) + { + if (expanded.NewValue) + { + this.FadeIn(300, Easing.OutQuint); + this.ResizeHeightTo(height, 500, Easing.OutQuint); + } + else + { + this.FadeOut(300, Easing.OutQuint); + this.ResizeHeightTo(0, 500, Easing.OutQuint); + } + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + localUserPlaying = localUserInfo.IsPlaying.GetBoundCopy(); + localUserPlaying.BindValueChanged(playing => + { + // for now let's never hold focus. this avoid misdirected gameplay keys entering chat. + // note that this is done within this callback as it triggers an un-focus as well. + Textbox.HoldFocus = false; + + // only hold focus (after sending a message) during breaks + Textbox.ReleaseFocusOnCommit = playing.NewValue; + }, true); + + Expanded.BindValueChanged(expandedChanged, true); + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index ca1a3710ab..24657943d7 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -68,6 +68,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { AutoSizeAxes = Axes.Both, Direction = FillDirection.Vertical, + Spacing = new Vector2(5) }); // todo: this should be implemented via a custom HUD implementation, and correctly masked to the main content area. @@ -78,7 +79,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer ((IBindable)leaderboard.Expanded).BindTo(HUDOverlay.ShowHud); - leaderboardFlow.Add(l); + leaderboardFlow.Insert(0, l); if (leaderboard.TeamScores.Count >= 2) { @@ -87,10 +88,15 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer Team1Score = { BindTarget = leaderboard.TeamScores.First().Value }, Team2Score = { BindTarget = leaderboard.TeamScores.Last().Value }, Expanded = { BindTarget = HUDOverlay.ShowHud }, - }, leaderboardFlow.Add); + }, scoreDisplay => leaderboardFlow.Insert(1, scoreDisplay)); } }); + LoadComponentAsync(new GameplayChatDisplay + { + Expanded = { BindTarget = HUDOverlay.ShowHud }, + }, chat => leaderboardFlow.Insert(2, chat)); + HUDOverlay.Add(loadingDisplay = new LoadingLayer(true) { Depth = float.MaxValue }); } From 124f149cb5f861de220d98c5da7f0a2a19b7a9d1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Aug 2021 15:05:36 +0900 Subject: [PATCH 1253/2442] Add key binding to focus chat input --- .../Input/Bindings/GlobalActionContainer.cs | 4 ++++ .../Multiplayer/GameplayChatDisplay.cs | 20 ++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index d3cc90ef99..66edb25d64 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -90,6 +90,7 @@ namespace osu.Game.Input.Bindings new KeyBinding(InputKey.Left, GlobalAction.SeekReplayBackward), new KeyBinding(InputKey.Right, GlobalAction.SeekReplayForward), new KeyBinding(InputKey.Control, GlobalAction.HoldForHUD), + new KeyBinding(InputKey.Tab, GlobalAction.FocusChatInput), }; public IEnumerable SongSelectKeyBindings => new[] @@ -280,5 +281,8 @@ namespace osu.Game.Input.Bindings [Description("Seek replay backward")] SeekReplayBackward, + + [Description("Focus chat")] + FocusChatInput } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs index 5b33fa1844..40da8d0a84 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs @@ -4,12 +4,14 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Input.Bindings; +using osu.Game.Input.Bindings; using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.Play; namespace osu.Game.Screens.OnlinePlay.Multiplayer { - public class GameplayChatDisplay : MatchChatDisplay + public class GameplayChatDisplay : MatchChatDisplay, IKeyBindingHandler { [Resolved] private ILocalUserPlayInfo localUserInfo { get; set; } @@ -58,5 +60,21 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer Expanded.BindValueChanged(expandedChanged, true); } + + public bool OnPressed(GlobalAction action) + { + switch (action) + { + case GlobalAction.FocusChatInput: + Textbox.TakeFocus(); + return true; + } + + return false; + } + + public void OnReleased(GlobalAction action) + { + } } } From 6a2d82c81aa6cd9ff1f6dae57b561ff1132c5367 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Aug 2021 15:11:45 +0900 Subject: [PATCH 1254/2442] Add test coverage --- .../TestSceneGameplayChatDisplay.cs | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs new file mode 100644 index 0000000000..795963a1ba --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs @@ -0,0 +1,85 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using Moq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Testing; +using osu.Game.Screens.OnlinePlay.Multiplayer; +using osu.Game.Screens.Play; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneGameplayChatDisplay : MultiplayerTestScene + { + private GameplayChatDisplay chatDisplay; + + [Cached(typeof(ILocalUserPlayInfo))] + private ILocalUserPlayInfo localUserInfo; + + private readonly Bindable localUserPlaying = new Bindable(); + + private TextBox textBox => chatDisplay.ChildrenOfType().First(); + + public TestSceneGameplayChatDisplay() + { + var mockLocalUserInfo = new Mock(); + mockLocalUserInfo.SetupGet(i => i.IsPlaying).Returns(localUserPlaying); + + localUserInfo = mockLocalUserInfo.Object; + } + + [SetUpSteps] + public override void SetUpSteps() + { + base.SetUpSteps(); + + AddStep("load chat display", () => Child = chatDisplay = new GameplayChatDisplay + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Width = 0.5f, + }); + + AddStep("expand", () => chatDisplay.Expanded.Value = true); + } + + [Test] + public void TestFocusDroppedWhenPlaying() + { + assertChatFocused(false); + + AddStep("focus chat", () => + { + InputManager.MoveMouseTo(textBox); + InputManager.Click(MouseButton.Left); + }); + + setLocalUserPlaying(true); + assertChatFocused(false); + + // should still stay non-focused even after entering a new break section. + setLocalUserPlaying(false); + assertChatFocused(false); + } + + [Test] + public void TestFocusOnTabKey() + { + assertChatFocused(false); + AddStep("press tab", () => InputManager.Key(Key.Tab)); + assertChatFocused(true); + } + + private void assertChatFocused(bool isFocused) => + AddAssert($"chat {(isFocused ? "focused" : "not focused")}", () => textBox.HasFocus == isFocused); + + private void setLocalUserPlaying(bool playing) => + AddStep($"local user {(playing ? "playing" : "not playing")}", () => localUserPlaying.Value = playing); + } +} From 6ee6a468941525b3b6d98369c4f3fdcdd30c58d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Aug 2021 16:22:12 +0900 Subject: [PATCH 1255/2442] Remove unnecessary `public` prefix in interface specification --- osu.Game/Screens/Play/ILocalUserPlayInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/ILocalUserPlayInfo.cs b/osu.Game/Screens/Play/ILocalUserPlayInfo.cs index 64c3f97305..9a2259b12f 100644 --- a/osu.Game/Screens/Play/ILocalUserPlayInfo.cs +++ b/osu.Game/Screens/Play/ILocalUserPlayInfo.cs @@ -12,6 +12,6 @@ namespace osu.Game.Screens.Play /// /// Whether the local user is currently playing. /// - public IBindable IsPlaying { get; } + IBindable IsPlaying { get; } } } From 72dd18732d44efb3c24e732fa59be8cae0f52e9f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Aug 2021 16:37:18 +0900 Subject: [PATCH 1256/2442] Fix regressed tests --- osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index 8632bfe681..57ba051214 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Settings [SetUpSteps] public void SetUpSteps() { - AddUntilStep("wait for load", () => panel.ChildrenOfType().Any()); + AddUntilStep("wait for load", () => panel.ChildrenOfType().Any()); AddStep("Scroll to top", () => panel.ChildrenOfType().First().ScrollToTop()); AddWaitStep("wait for scroll", 5); } From 8a1651e8308f1ee865d22cc81f9bc87192e962a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Aug 2021 17:04:32 +0900 Subject: [PATCH 1257/2442] Reorganise methods in `PollingComponent` --- osu.Game/Online/PollingComponent.cs | 73 ++++++++++++++--------------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/osu.Game/Online/PollingComponent.cs b/osu.Game/Online/PollingComponent.cs index 806c0047e7..243be8da44 100644 --- a/osu.Game/Online/PollingComponent.cs +++ b/osu.Game/Online/PollingComponent.cs @@ -47,39 +47,13 @@ namespace osu.Game.Online pollIfNecessary(); } - private bool pollIfNecessary() + /// + /// Immediately performs a . + /// + public void PollImmediately() { - // we must be loaded so we have access to clock. - if (!IsLoaded) return false; - - // there's already a poll process running. - if (pollingActive) return false; - - // don't try polling if the time between polls hasn't been set. - if (TimeBetweenPolls.Value == 0) return false; - - if (!lastTimePolled.HasValue) - { - doPoll(); - return true; - } - - if (Time.Current - lastTimePolled.Value > TimeBetweenPolls.Value) - { - doPoll(); - return true; - } - - // not enough time has passed since the last poll. we do want to schedule a poll to happen, though. + lastTimePolled = Time.Current - TimeBetweenPolls.Value; scheduleNextPoll(); - return false; - } - - private void doPoll() - { - scheduledPoll = null; - pollingActive = true; - Poll().ContinueWith(_ => pollComplete()); } /// @@ -90,13 +64,11 @@ namespace osu.Game.Online return Task.CompletedTask; } - /// - /// Immediately performs a . - /// - public void PollImmediately() + private void doPoll() { - lastTimePolled = Time.Current - TimeBetweenPolls.Value; - scheduleNextPoll(); + scheduledPoll = null; + pollingActive = true; + Poll().ContinueWith(_ => pollComplete()); } /// @@ -111,6 +83,33 @@ namespace osu.Game.Online pollIfNecessary(); } + private void pollIfNecessary() + { + // we must be loaded so we have access to clock. + if (!IsLoaded) return; + + // there's already a poll process running. + if (pollingActive) return; + + // don't try polling if the time between polls hasn't been set. + if (TimeBetweenPolls.Value == 0) return; + + if (!lastTimePolled.HasValue) + { + doPoll(); + return; + } + + if (Time.Current - lastTimePolled.Value > TimeBetweenPolls.Value) + { + doPoll(); + return; + } + + // not enough time has passed since the last poll. we do want to schedule a poll to happen, though. + scheduleNextPoll(); + } + private void scheduleNextPoll() { scheduledPoll?.Cancel(); From 4b198d14eb4dd473cf042c940d0955bbdc69411a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 17 Aug 2021 17:05:20 +0900 Subject: [PATCH 1258/2442] Initial refactor of RoomSubScreen --- .../Multiplayer/TestSceneMultiplayer.cs | 13 + .../TestSceneMultiplayerMatchSubScreen.cs | 34 +- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 126 +++++-- .../Multiplayer/MultiplayerMatchSubScreen.cs | 349 +++++++++--------- .../Playlists/PlaylistsRoomSubScreen.cs | 8 + 5 files changed, 302 insertions(+), 228 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index e618b28f40..035bdbea1c 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -83,6 +83,19 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("load multiplayer", () => LoadScreen(multiplayerScreen)); AddUntilStep("wait for multiplayer to load", () => multiplayerScreen.IsLoaded); AddUntilStep("wait for lounge to load", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); + + createRoom(() => new Room + { + Name = { Value = "Test Room" }, + Playlist = + { + new PlaylistItem + { + Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + } + } + }); } [Test] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index ea10fc1b8b..21364fe154 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -59,23 +59,6 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for load", () => screen.IsCurrentScreen()); } - [Test] - public void TestSettingValidity() - { - AddAssert("create button not enabled", () => !this.ChildrenOfType().Single().Enabled.Value); - - AddStep("set playlist", () => - { - SelectedRoom.Value.Playlist.Add(new PlaylistItem - { - Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, - Ruleset = { Value = new OsuRuleset().RulesetInfo }, - }); - }); - - AddAssert("create button enabled", () => this.ChildrenOfType().Single().Enabled.Value); - } - [Test] public void TestCreatedRoom() { @@ -97,6 +80,23 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for join", () => Client.Room != null); } + [Test] + public void TestSettingValidity() + { + AddAssert("create button not enabled", () => !this.ChildrenOfType().Single().Enabled.Value); + + AddStep("set playlist", () => + { + SelectedRoom.Value.Playlist.Add(new PlaylistItem + { + Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + }); + }); + + AddAssert("create button enabled", () => this.ChildrenOfType().Single().Enabled.Value); + } + [Test] public void TestStartMatchWhileSpectating() { diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 243d2abf74..8de8e5e897 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -19,6 +19,7 @@ using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Overlays.Mods; using osu.Game.Rulesets.Mods; +using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Match.Components; namespace osu.Game.Screens.OnlinePlay.Match @@ -31,8 +32,6 @@ namespace osu.Game.Screens.OnlinePlay.Match public override bool DisallowExternalBeatmapRulesetChanges => true; - private readonly ModSelectOverlay userModsSelectOverlay; - /// /// A container that provides controls for selection of user mods. /// This will be shown/hidden automatically when applicable. @@ -58,49 +57,106 @@ namespace osu.Game.Screens.OnlinePlay.Match private IBindable> managerUpdated; [Cached] - protected OnlinePlayBeatmapAvailabilityTracker BeatmapAvailabilityTracker { get; } + protected OnlinePlayBeatmapAvailabilityTracker BeatmapAvailabilityTracker { get; private set; } protected IBindable BeatmapAvailability => BeatmapAvailabilityTracker.Availability; - protected RoomSubScreen() + private readonly Room room; + + private ModSelectOverlay userModsSelectOverlay; + + protected RoomSubScreen(Room room) { + this.room = room; + Padding = new MarginPadding { Top = Header.HEIGHT }; - AddRangeInternal(new Drawable[] + BeatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"3e3a44") // This is super temporary. - }, - BeatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker - { - SelectedItem = { BindTarget = SelectedItem } - }, - new Container - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Depth = float.MinValue, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }, - Child = userModsSelectOverlay = new UserModSelectOverlay - { - SelectedMods = { BindTarget = UserMods }, - IsValidMod = _ => false - } - }, - }); + SelectedItem = { BindTarget = SelectedItem } + }; } - protected override void ClearInternal(bool disposeChildren = true) => - throw new InvalidOperationException($"{nameof(RoomSubScreen)}'s children should not be cleared as it will remove required components"); - [BackgroundDependencyLoader] private void load(AudioManager audio) { sampleStart = audio.Samples.Get(@"SongSelect/confirm-selection"); + + InternalChildren = new Drawable[] + { + BeatmapAvailabilityTracker, + new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + // Padded main content (drawable room + main content) + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = 60 }, + // Main content + Child = new GridContainer + { + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new Drawable[] + { + CreateDrawableRoom(room), + }, + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Children = new[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 10, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex(@"3e3a44") // This is super temporary. + }, + }, + CreateMainContent(), + new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Child = userModsSelectOverlay = new UserModSelectOverlay + { + SelectedMods = { BindTarget = UserMods }, + IsValidMod = _ => false + } + }, + } + } + } + } + } + } + }, + // Footer + new[] + { + CreateFooter() + } + } + } + }; } protected override void LoadComplete() @@ -257,6 +313,12 @@ namespace osu.Game.Screens.OnlinePlay.Match track.Looping = false; } + protected abstract DrawableRoom CreateDrawableRoom(Room room); + + protected abstract Drawable CreateMainContent(); + + protected abstract Drawable CreateFooter(); + private class UserModSelectOverlay : LocalPlayerModSelectOverlay { } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 6277afa8bb..da766c9e25 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -22,6 +22,7 @@ using osu.Game.Overlays.Dialog; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.OnlinePlay.Components; +using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; @@ -58,10 +59,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [CanBeNull] private IDisposable readyClickOperation; - private GridContainer mainContent; + // private GridContainer mainContent; private MultiplayerMatchSettingsOverlay settingsOverlay; public MultiplayerMatchSubScreen(Room room) + : base(room) { Room = room; @@ -72,191 +74,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [BackgroundDependencyLoader] private void load() { - AddRangeInternal(new Drawable[] - { - mainContent = new GridContainer - { - RelativeSizeAxes = Axes.Both, - Content = new[] - { - new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding - { - Horizontal = HORIZONTAL_OVERFLOW_PADDING + 55, - Vertical = 20 - }, - Child = new GridContainer - { - RelativeSizeAxes = Axes.Both, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - new Dimension(), - }, - Content = new[] - { - new Drawable[] - { - new MultiplayerMatchHeader - { - OpenSettings = () => settingsOverlay.Show() - } - }, - new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 5, Vertical = 10 }, - Child = new GridContainer - { - RelativeSizeAxes = Axes.Both, - ColumnDimensions = new[] - { - new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 400), - new Dimension(), - new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 600), - }, - Content = new[] - { - new Drawable[] - { - // Main left column - new GridContainer - { - RelativeSizeAxes = Axes.Both, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize) - }, - Content = new[] - { - new Drawable[] { new ParticipantsListHeader() }, - new Drawable[] - { - new ParticipantsList - { - RelativeSizeAxes = Axes.Both - }, - } - } - }, - // Spacer - null, - // Main right column - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new[] - { - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - new OverlinedHeader("Beatmap"), - new BeatmapSelectionControl { RelativeSizeAxes = Axes.X } - } - }, - UserModsSection = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Margin = new MarginPadding { Top = 10 }, - Children = new Drawable[] - { - new OverlinedHeader("Extra mods"), - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(10, 0), - Children = new Drawable[] - { - new UserModSelectButton - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Width = 90, - Text = "Select", - Action = ShowUserModSelect, - }, - new ModDisplay - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Current = UserMods, - Scale = new Vector2(0.8f), - }, - } - } - } - } - } - } - } - } - } - } - }, - new Drawable[] - { - new GridContainer - { - RelativeSizeAxes = Axes.Both, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize) - }, - Content = new[] - { - new Drawable[] { new OverlinedHeader("Chat") }, - new Drawable[] { new MatchChatDisplay { RelativeSizeAxes = Axes.Both } } - } - } - } - }, - } - } - }, - new Drawable[] - { - new MultiplayerMatchFooter - { - OnReadyClick = onReadyClick, - OnSpectateClick = onSpectateClick - } - } - }, - RowDimensions = new[] - { - new Dimension(), - new Dimension(GridSizeMode.AutoSize), - } - }, - settingsOverlay = new MultiplayerMatchSettingsOverlay - { - RelativeSizeAxes = Axes.Both, - State = { Value = client.Room == null ? Visibility.Visible : Visibility.Hidden } - } - }); - if (client.Room == null) { // A new room is being created. // The main content should be hidden until the settings overlay is hidden, signaling the room is ready to be displayed. - mainContent.Hide(); + // mainContent.Hide(); settingsOverlay.State.BindValueChanged(visibility => { - if (visibility.NewValue == Visibility.Hidden) - mainContent.Show(); + // if (visibility.NewValue == Visibility.Hidden) + // mainContent.Show(); }, true); } } @@ -303,6 +130,170 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer Mods.Value = client.LocalUser.Mods.Select(m => m.ToMod(ruleset)).Concat(SelectedItem.Value.RequiredMods).ToList(); } + protected override DrawableRoom CreateDrawableRoom(Room room) => new DrawableRoom(room); + + protected override Drawable CreateMainContent() => new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding + { + Horizontal = HORIZONTAL_OVERFLOW_PADDING + 55, + Vertical = 20 + }, + Child = new GridContainer + { + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(), + }, + Content = new[] + { + new Drawable[] + { + new MultiplayerMatchHeader + { + OpenSettings = () => settingsOverlay.Show() + } + }, + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = 5, Vertical = 10 }, + Child = new GridContainer + { + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 400), + new Dimension(), + new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 600), + }, + Content = new[] + { + new Drawable[] + { + // Main left column + new GridContainer + { + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize) + }, + Content = new[] + { + new Drawable[] { new ParticipantsListHeader() }, + new Drawable[] + { + new ParticipantsList + { + RelativeSizeAxes = Axes.Both + }, + } + } + }, + // Spacer + null, + // Main right column + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new OverlinedHeader("Beatmap"), + new BeatmapSelectionControl { RelativeSizeAxes = Axes.X } + } + }, + UserModsSection = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Top = 10 }, + Children = new Drawable[] + { + new OverlinedHeader("Extra mods"), + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10, 0), + Children = new Drawable[] + { + new UserModSelectButton + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Width = 90, + Text = "Select", + Action = ShowUserModSelect, + }, + new ModDisplay + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Current = UserMods, + Scale = new Vector2(0.8f), + }, + } + } + } + } + } + } + } + } + } + } + }, + new Drawable[] + { + new GridContainer + { + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize) + }, + Content = new[] + { + new Drawable[] { new OverlinedHeader("Chat") }, + new Drawable[] { new MatchChatDisplay { RelativeSizeAxes = Axes.Both } } + } + } + } + }, + } + }, + settingsOverlay = new MultiplayerMatchSettingsOverlay + { + RelativeSizeAxes = Axes.Both, + // State = { Value = client.Room == null ? Visibility.Visible : Visibility.Hidden } + } + } + }; + + protected override Drawable CreateFooter() => new MultiplayerMatchFooter + { + OnReadyClick = onReadyClick, + OnSpectateClick = onSpectateClick + }; + [Resolved(canBeNull: true)] private DialogOverlay dialogOverlay { get; set; } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 682b055766..8ca592ee2c 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -14,6 +14,7 @@ using osu.Game.Input; using osu.Game.Online.API; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Components; +using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.Play; @@ -45,6 +46,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private SelectionPollingComponent selectionPollingComponent; public PlaylistsRoomSubScreen(Room room) + : base(room) { Title = room.RoomID.Value == null ? "New playlist" : room.Name.Value; Activity.Value = new UserActivity.InLobby(room); @@ -295,5 +297,11 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { Exited = () => leaderboard.RefreshScores() }); + + protected override DrawableRoom CreateDrawableRoom(Room room) => new DrawableRoom(room); + + protected override Drawable CreateMainContent() => Empty(); + + protected override Drawable CreateFooter() => Empty(); } } From 6416e64e06a1695d03e3b524a1094de772bae2b3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 17 Aug 2021 17:13:25 +0900 Subject: [PATCH 1259/2442] Adjust sizings and paddings --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 11 ++++++++++- .../Multiplayer/MultiplayerMatchSubScreen.cs | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 8de8e5e897..7513ffa0f3 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -88,6 +88,11 @@ namespace osu.Game.Screens.OnlinePlay.Match new GridContainer { RelativeSizeAxes = Axes.Both, + RowDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.AutoSize) + }, Content = new[] { // Padded main content (drawable room + main content) @@ -96,7 +101,11 @@ namespace osu.Game.Screens.OnlinePlay.Match new Container { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 60 }, + Padding = new MarginPadding + { + Horizontal = WaveOverlayContainer.WIDTH_PADDING, + Bottom = 30 + }, // Main content Child = new GridContainer { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index da766c9e25..0a7ddb1254 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -142,8 +142,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { - Horizontal = HORIZONTAL_OVERFLOW_PADDING + 55, - Vertical = 20 + Horizontal = 10, + Vertical = 10 }, Child = new GridContainer { From 5d72c5911af23537b27addcd5b64e66022d0da40 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 17 Aug 2021 17:14:21 +0900 Subject: [PATCH 1260/2442] Rename MatchSettingsOverlay and related classes Because "match" is a multiplayer-only concept. --- .../Playlists/TestScenePlaylistsMatchSettingsOverlay.cs | 2 +- .../Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs | 4 ++-- .../{MatchSettingsOverlay.cs => RoomSettingsOverlay.cs} | 2 +- .../Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs | 2 +- ...atchSettingsOverlay.cs => PlaylistsRoomSettingsOverlay.cs} | 2 +- .../Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) rename osu.Game/Screens/OnlinePlay/Match/Components/{MatchSettingsOverlay.cs => RoomSettingsOverlay.cs} (97%) rename osu.Game/Screens/OnlinePlay/Playlists/{PlaylistsMatchSettingsOverlay.cs => PlaylistsRoomSettingsOverlay.cs} (99%) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs index 98882b659c..04b44efd32 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs @@ -110,7 +110,7 @@ namespace osu.Game.Tests.Visual.Playlists AddUntilStep("error not displayed", () => !settings.ErrorText.IsPresent); } - private class TestRoomSettings : PlaylistsMatchSettingsOverlay + private class TestRoomSettings : PlaylistsRoomSettingsOverlay { public TriangleButton ApplyButton => ((MatchSettings)Settings).ApplyButton; diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs index 9fc29049ef..ee93e728ac 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs @@ -96,7 +96,7 @@ namespace osu.Game.Tests.Visual.Playlists AddStep("move mouse to create button", () => { - InputManager.MoveMouseTo(this.ChildrenOfType().Single()); + InputManager.MoveMouseTo(this.ChildrenOfType().Single()); }); AddStep("click", () => InputManager.Click(MouseButton.Left)); @@ -147,7 +147,7 @@ namespace osu.Game.Tests.Visual.Playlists AddStep("create room", () => { - InputManager.MoveMouseTo(match.ChildrenOfType().Single()); + InputManager.MoveMouseTo(match.ChildrenOfType().Single()); InputManager.Click(MouseButton.Left); }); diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs similarity index 97% rename from osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs rename to osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs index 2676453a7e..bcefd5a333 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs @@ -14,7 +14,7 @@ using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay.Match.Components { - public abstract class MatchSettingsOverlay : FocusedOverlayContainer, IKeyBindingHandler + public abstract class RoomSettingsOverlay : FocusedOverlayContainer, IKeyBindingHandler { protected const float TRANSITION_DURATION = 350; protected const float FIELD_PADDING = 45; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 5f3921d742..3a95ac00a6 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -26,7 +26,7 @@ using osuTK; namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { - public class MultiplayerMatchSettingsOverlay : MatchSettingsOverlay + public class MultiplayerMatchSettingsOverlay : RoomSettingsOverlay { private MatchSettings settings; diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs similarity index 99% rename from osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs rename to osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs index 2640f99ea5..d28f886be4 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs @@ -22,7 +22,7 @@ using osuTK; namespace osu.Game.Screens.OnlinePlay.Playlists { - public class PlaylistsMatchSettingsOverlay : MatchSettingsOverlay + public class PlaylistsRoomSettingsOverlay : RoomSettingsOverlay { public Action EditPlaylist; diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 8ca592ee2c..c2f92d7e00 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -39,7 +39,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private readonly IBindable isIdle = new BindableBool(); - private MatchSettingsOverlay settingsOverlay; + private RoomSettingsOverlay settingsOverlay; private MatchLeaderboard leaderboard; private OverlinedHeader participantsHeader; private GridContainer mainContent; @@ -237,7 +237,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists new Dimension(GridSizeMode.AutoSize), } }, - settingsOverlay = new PlaylistsMatchSettingsOverlay + settingsOverlay = new PlaylistsRoomSettingsOverlay { RelativeSizeAxes = Axes.Both, EditPlaylist = () => From 9eb16fa61de76cd0432cb199503ef9a4f31f6d2c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Aug 2021 17:16:21 +0900 Subject: [PATCH 1261/2442] Move poll allowance logic based on signalr connection inside polling component --- .../Multiplayer/MultiplayerLoungeSubScreen.cs | 47 +++++++------------ 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs index 97fed2040d..d152fc3913 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs @@ -25,19 +25,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private MultiplayerClient client { get; set; } - private MultiplayerListingPollingComponent multiplayerListingPollingComponent => (MultiplayerListingPollingComponent)ListingPollingComponent; - - private readonly IBindable isConnected = new Bindable(); - - protected override void LoadComplete() - { - base.LoadComplete(); - - isConnected.BindTo(client.IsConnected); - isConnected.BindValueChanged(c => Scheduler.AddOnce(() => multiplayerListingPollingComponent.AllowPolling = c.NewValue)); - multiplayerListingPollingComponent.AllowPolling = isConnected.Value; - } - public override void OnResuming(IScreen last) { base.OnResuming(last); @@ -47,7 +34,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer if (last is MultiplayerMatchSubScreen match) { RoomManager.RemoveRoom(match.Room); - multiplayerListingPollingComponent.PollImmediately(); + ListingPollingComponent.PollImmediately(); } } @@ -84,27 +71,29 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private class MultiplayerListingPollingComponent : ListingPollingComponent { - private bool allowPolling; + [Resolved] + private MultiplayerClient client { get; set; } - public bool AllowPolling + private readonly IBindable isConnected = new Bindable(); + + [BackgroundDependencyLoader] + private void load() { - get => allowPolling; - set + isConnected.BindTo(client.IsConnected); + isConnected.BindValueChanged(c => Scheduler.AddOnce(() => { - if (allowPolling == value) - return; - - allowPolling = value; - - if (!allowPolling) - return; - - if (IsLoaded) + if (isConnected.Value && IsLoaded) PollImmediately(); - } + }), true); } - protected override Task Poll() => AllowPolling ? base.Poll() : Task.CompletedTask; + protected override Task Poll() + { + if (!isConnected.Value) + return Task.CompletedTask; + + return base.Poll(); + } } } } From 3b5fc6d10ff17fb06f43cdd107036a6e7da0164a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Aug 2021 17:18:23 +0900 Subject: [PATCH 1262/2442] Ensure `updateLoadingLayer` is run at least once --- osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 3e8d07d002..8bed3d6049 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -186,16 +186,16 @@ namespace osu.Game.Screens.OnlinePlay.Lounge searchTextBox.Current.BindValueChanged(_ => updateFilterDebounced()); ruleset.BindValueChanged(_ => UpdateFilter()); - ListingPollingComponent.InitialRoomsReceived.BindValueChanged(_ => updateLoadingLayer()); - isIdle.BindValueChanged(_ => updatePollingRate(this.IsCurrentScreen()), true); if (ongoingOperationTracker != null) { operationInProgress.BindTo(ongoingOperationTracker.InProgress); - operationInProgress.BindValueChanged(_ => updateLoadingLayer(), true); + operationInProgress.BindValueChanged(_ => updateLoadingLayer()); } + ListingPollingComponent.InitialRoomsReceived.BindValueChanged(_ => updateLoadingLayer(), true); + updateFilter(); } From c0b388cd74e864f6654d161e4bd631ca36408b0c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Aug 2021 17:50:30 +0900 Subject: [PATCH 1263/2442] Fix regression in `ModSettingsChangeTracker` --- osu.Game/Overlays/Settings/SettingsItem.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index c35690151c..6621caef4e 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -119,19 +119,19 @@ namespace osu.Game.Overlays.Settings }, }, }; - } - [BackgroundDependencyLoader] - private void load() - { - // all bindable logic is in constructor intentionally to support "CreateSettingsControls" being used in a context it is + // IMPORTANT: all bindable logic is in constructor intentionally to support "CreateSettingsControls" being used in a context it is // never loaded, but requires bindable storage. if (controlWithCurrent == null) throw new ArgumentException(@$"Control created via {nameof(CreateControl)} must implement {nameof(IHasCurrentValue)}"); controlWithCurrent.Current.ValueChanged += _ => SettingChanged?.Invoke(); controlWithCurrent.Current.DisabledChanged += _ => updateDisabled(); + } + [BackgroundDependencyLoader] + private void load() + { // intentionally done before LoadComplete to avoid overhead. if (ShowsDefaultIndicator) { From 6840ec671684501b2ca47675d17e4405fdd7a55e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 17 Aug 2021 17:58:24 +0900 Subject: [PATCH 1264/2442] Actually show the room in the sub screen --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 7513ffa0f3..afd7112b22 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -113,13 +113,15 @@ namespace osu.Game.Screens.OnlinePlay.Match RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.Absolute, 10) }, Content = new[] { new Drawable[] { - CreateDrawableRoom(room), + CreateDrawableRoom(room).With(d => d.MatchingFilter = true), }, + null, new Drawable[] { new Container From f16468b7063ea85c7b610f0d761151ad181f02f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Aug 2021 18:17:55 +0900 Subject: [PATCH 1265/2442] Improve visibility of repeat ticks / drag areas on timeline --- .../Timeline/TimelineHitObjectBlueprint.cs | 46 +++++++++---------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 6e57b8e88c..911c9fea51 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -166,14 +166,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } if (IsSelected) - { border.Show(); - colour = colour.Lighten(0.3f); - } else - { border.Hide(); - } if (Item is IHasDuration duration && duration.Duration > 0) circle.Colour = ColourInfo.GradientHorizontal(colour, colour.Lighten(0.4f)); @@ -212,14 +207,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline for (int i = 0; i < repeats.RepeatCount; i++) { - repeatsContainer.Add(new Circle + repeatsContainer.Add(new Tick { - Size = new Vector2(circle_size / 3), - Alpha = 0.2f, - Anchor = Anchor.CentreLeft, - Origin = Anchor.Centre, - RelativePositionAxes = Axes.X, - X = (float)(i + 1) / (repeats.RepeatCount + 1), + X = (float)(i + 1) / (repeats.RepeatCount + 1) }); } } @@ -233,6 +223,17 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public override Vector2 ScreenSpaceSelectionPoint => ScreenSpaceDrawQuad.TopLeft; + private class Tick : Circle + { + public Tick() + { + Size = new Vector2(circle_size / 4); + Anchor = Anchor.CentreLeft; + Origin = Anchor.Centre; + RelativePositionAxes = Axes.X; + } + } + public class DragArea : Circle { private readonly HitObject hitObject; @@ -304,20 +305,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private void updateState() { - if (hasMouseDown) - { - this.ScaleTo(0.7f, 200, Easing.OutQuint); - } - else if (IsHovered) - { - this.ScaleTo(0.8f, 200, Easing.OutQuint); - } - else - { - this.ScaleTo(0.6f, 200, Easing.OutQuint); - } + float scale = 0.5f; - this.FadeTo(IsHovered || hasMouseDown ? 0.8f : 0.2f, 200, Easing.OutQuint); + if (hasMouseDown) + scale = 0.6f; + else if (IsHovered) + scale = 0.7f; + + this.ScaleTo(scale, 200, Easing.OutQuint); + this.FadeTo(IsHovered || hasMouseDown ? 1f : 0.9f, 200, Easing.OutQuint); } [Resolved] From 590d814881e71dcb4ad353f40e7a5164df8adb41 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 17 Aug 2021 18:24:04 +0900 Subject: [PATCH 1266/2442] Move RoomSettingsOverlay to RoomSubScreen --- .../Match/Components/RoomSettingsOverlay.cs | 8 +- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 109 ++++++++++++------ .../Multiplayer/MultiplayerMatchSubScreen.cs | 44 +------ .../Playlists/PlaylistsRoomSubScreen.cs | 2 + 4 files changed, 82 insertions(+), 81 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs index bcefd5a333..5f0ce0feed 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs @@ -27,11 +27,15 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components protected abstract bool IsLoading { get; } + protected RoomSettingsOverlay() + { + RelativeSizeAxes = Axes.Both; + Masking = true; + } + [BackgroundDependencyLoader] private void load() { - Masking = true; - Add(Settings = CreateSettings()); } diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index afd7112b22..a79f9421a7 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -64,6 +64,7 @@ namespace osu.Game.Screens.OnlinePlay.Match private readonly Room room; private ModSelectOverlay userModsSelectOverlay; + private RoomSettingsOverlay settingsOverlay; protected RoomSubScreen(Room room) { @@ -82,6 +83,8 @@ namespace osu.Game.Screens.OnlinePlay.Match { sampleStart = audio.Samples.Get(@"SongSelect/confirm-selection"); + Drawable mainContent; + InternalChildren = new Drawable[] { BeatmapAvailabilityTracker, @@ -106,59 +109,62 @@ namespace osu.Game.Screens.OnlinePlay.Match Horizontal = WaveOverlayContainer.WIDTH_PADDING, Bottom = 30 }, - // Main content - Child = new GridContainer + Children = new[] { - RelativeSizeAxes = Axes.Both, - RowDimensions = new[] + mainContent = new GridContainer { - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.Absolute, 10) - }, - Content = new[] - { - new Drawable[] + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] { - CreateDrawableRoom(room).With(d => d.MatchingFilter = true), + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.Absolute, 10) }, - null, - new Drawable[] + Content = new[] { - new Container + new Drawable[] { - RelativeSizeAxes = Axes.Both, - Children = new[] + CreateDrawableRoom(room).With(d => d.MatchingFilter = true), + }, + null, + new Drawable[] + { + new Container { - new Container + RelativeSizeAxes = Axes.Both, + Children = new[] { - RelativeSizeAxes = Axes.Both, - Masking = true, - CornerRadius = 10, - Child = new Box + new Container { RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"3e3a44") // This is super temporary. + Masking = true, + CornerRadius = 10, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex(@"3e3a44") // This is super temporary. + }, }, - }, - CreateMainContent(), - new Container - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Child = userModsSelectOverlay = new UserModSelectOverlay + CreateMainContent(), + new Container { - SelectedMods = { BindTarget = UserMods }, - IsValidMod = _ => false - } - }, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Child = userModsSelectOverlay = new UserModSelectOverlay + { + SelectedMods = { BindTarget = UserMods }, + IsValidMod = _ => false + } + }, + } } } } - } - } - } + }, + settingsOverlay = CreateRoomSettingsOverlay() + }, + }, }, // Footer new[] @@ -168,6 +174,19 @@ namespace osu.Game.Screens.OnlinePlay.Match } } }; + + if (room.RoomID.Value == null) + { + // A new room is being created. + // The main content should be hidden until the settings overlay is hidden, signaling the room is ready to be displayed. + mainContent.Hide(); + + settingsOverlay.State.BindValueChanged(visibility => + { + if (visibility.NewValue == Visibility.Hidden) + mainContent.Show(); + }, true); + } } protected override void LoadComplete() @@ -184,12 +203,24 @@ namespace osu.Game.Screens.OnlinePlay.Match public override bool OnBackButton() { + if (room.RoomID.Value == null) + { + // room has not been created yet; exit immediately. + return base.OnBackButton(); + } + if (userModsSelectOverlay.State.Value == Visibility.Visible) { userModsSelectOverlay.Hide(); return true; } + if (settingsOverlay.State.Value == Visibility.Visible) + { + settingsOverlay.Hide(); + return true; + } + return base.OnBackButton(); } @@ -330,6 +361,8 @@ namespace osu.Game.Screens.OnlinePlay.Match protected abstract Drawable CreateFooter(); + protected abstract RoomSettingsOverlay CreateRoomSettingsOverlay(); + private class UserModSelectOverlay : LocalPlayerModSelectOverlay { } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 0a7ddb1254..1b9c2092dd 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -60,7 +60,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private IDisposable readyClickOperation; // private GridContainer mainContent; - private MultiplayerMatchSettingsOverlay settingsOverlay; public MultiplayerMatchSubScreen(Room room) : base(room) @@ -71,23 +70,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer Activity.Value = new UserActivity.InLobby(room); } - [BackgroundDependencyLoader] - private void load() - { - if (client.Room == null) - { - // A new room is being created. - // The main content should be hidden until the settings overlay is hidden, signaling the room is ready to be displayed. - // mainContent.Hide(); - - settingsOverlay.State.BindValueChanged(visibility => - { - // if (visibility.NewValue == Visibility.Hidden) - // mainContent.Show(); - }, true); - } - } - protected override void LoadComplete() { base.LoadComplete(); @@ -159,7 +141,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { new MultiplayerMatchHeader { - OpenSettings = () => settingsOverlay.Show() + // OpenSettings = () => settingsOverlay.Show() } }, new Drawable[] @@ -280,11 +262,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer }, } }, - settingsOverlay = new MultiplayerMatchSettingsOverlay - { - RelativeSizeAxes = Axes.Both, - // State = { Value = client.Room == null ? Visibility.Visible : Visibility.Hidden } - } } }; @@ -294,28 +271,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer OnSpectateClick = onSpectateClick }; + protected override RoomSettingsOverlay CreateRoomSettingsOverlay() => new MultiplayerMatchSettingsOverlay(); + [Resolved(canBeNull: true)] private DialogOverlay dialogOverlay { get; set; } private bool exitConfirmed; - public override bool OnBackButton() - { - if (client.Room == null) - { - // room has not been created yet; exit immediately. - return base.OnBackButton(); - } - - if (settingsOverlay.State.Value == Visibility.Visible) - { - settingsOverlay.Hide(); - return true; - } - - return base.OnBackButton(); - } - public override bool OnExiting(IScreen next) { // the room may not be left immediately after a disconnection due to async flow, diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index c2f92d7e00..7af74e7e64 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -303,5 +303,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists protected override Drawable CreateMainContent() => Empty(); protected override Drawable CreateFooter() => Empty(); + + protected override RoomSettingsOverlay CreateRoomSettingsOverlay() => new PlaylistsRoomSettingsOverlay(); } } From d66f7cb6b597ce62acd5460771958bfc59c1c97f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Aug 2021 19:21:22 +0900 Subject: [PATCH 1267/2442] Fix tests by allowing retrieval with files where required --- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 16 ++++++++-------- osu.Game/Skinning/SkinManager.cs | 10 ++++++++-- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index 8124bd4199..bab8dfc983 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -50,10 +50,10 @@ namespace osu.Game.Tests.Skins.IO var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin2.osk")); Assert.That(imported2.ID, Is.Not.EqualTo(imported.ID)); - Assert.That(osu.Dependencies.Get().GetAllUserSkins().Count, Is.EqualTo(1)); + Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).Count, Is.EqualTo(1)); // the first should be overwritten by the second import. - Assert.That(osu.Dependencies.Get().GetAllUserSkins().First().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID)); + Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).First().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID)); } finally { @@ -76,10 +76,10 @@ namespace osu.Game.Tests.Skins.IO var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk(string.Empty, string.Empty), "download.osk")); Assert.That(imported2.ID, Is.Not.EqualTo(imported.ID)); - Assert.That(osu.Dependencies.Get().GetAllUserSkins().Count, Is.EqualTo(2)); + Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).Count, Is.EqualTo(2)); - Assert.That(osu.Dependencies.Get().GetAllUserSkins().First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID)); - Assert.That(osu.Dependencies.Get().GetAllUserSkins().Last().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID)); + Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID)); + Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).Last().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID)); } finally { @@ -101,10 +101,10 @@ namespace osu.Game.Tests.Skins.IO var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin v2.1", "skinner"), "skin2.osk")); Assert.That(imported2.ID, Is.Not.EqualTo(imported.ID)); - Assert.That(osu.Dependencies.Get().GetAllUserSkins().Count, Is.EqualTo(2)); + Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).Count, Is.EqualTo(2)); - Assert.That(osu.Dependencies.Get().GetAllUserSkins().First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID)); - Assert.That(osu.Dependencies.Get().GetAllUserSkins().Last().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID)); + Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID)); + Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).Last().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID)); } finally { diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index fca1670419..51aaac1f79 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -95,7 +95,7 @@ namespace osu.Game.Skinning /// A newly allocated list of available . public List GetAllUsableSkins() { - var userSkins = GetAllUserSkins(); + var userSkins = GetAllUserSkins(false); userSkins.Insert(0, DefaultSkin.SkinInfo); userSkins.Insert(1, DefaultLegacySkin.SkinInfo); return userSkins; @@ -105,7 +105,13 @@ namespace osu.Game.Skinning /// Returns a list of all usable s that have been loaded by the user. /// /// A newly allocated list of available . - public List GetAllUserSkins() => ModelStore.Items.Where(s => !s.DeletePending).ToList(); + public List GetAllUserSkins(bool includeFiles = false) + { + if (includeFiles) + return ModelStore.ConsumableItems.Where(s => !s.DeletePending).ToList(); + + return ModelStore.Items.Where(s => !s.DeletePending).ToList(); + } public void SelectRandomSkin() { From 47d4a2e97f70de9b1cc307554de19b45eaa3a107 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 17 Aug 2021 20:05:26 +0900 Subject: [PATCH 1268/2442] Make SettingsOverlay protected --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 10 +++++----- .../Multiplayer/MultiplayerMatchSubScreen.cs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index a79f9421a7..3addf57048 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -64,7 +64,7 @@ namespace osu.Game.Screens.OnlinePlay.Match private readonly Room room; private ModSelectOverlay userModsSelectOverlay; - private RoomSettingsOverlay settingsOverlay; + protected RoomSettingsOverlay SettingsOverlay { get; private set; } protected RoomSubScreen(Room room) { @@ -162,7 +162,7 @@ namespace osu.Game.Screens.OnlinePlay.Match } } }, - settingsOverlay = CreateRoomSettingsOverlay() + SettingsOverlay = CreateRoomSettingsOverlay() }, }, }, @@ -181,7 +181,7 @@ namespace osu.Game.Screens.OnlinePlay.Match // The main content should be hidden until the settings overlay is hidden, signaling the room is ready to be displayed. mainContent.Hide(); - settingsOverlay.State.BindValueChanged(visibility => + SettingsOverlay.State.BindValueChanged(visibility => { if (visibility.NewValue == Visibility.Hidden) mainContent.Show(); @@ -215,9 +215,9 @@ namespace osu.Game.Screens.OnlinePlay.Match return true; } - if (settingsOverlay.State.Value == Visibility.Visible) + if (SettingsOverlay.State.Value == Visibility.Visible) { - settingsOverlay.Hide(); + SettingsOverlay.Hide(); return true; } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 1b9c2092dd..09c2a22850 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -141,7 +141,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { new MultiplayerMatchHeader { - // OpenSettings = () => settingsOverlay.Show() + OpenSettings = () => SettingsOverlay.Show() } }, new Drawable[] From c99a96a8c8ca9ed2ff1efcf7c353bfd4db0d8cc9 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Tue, 17 Aug 2021 13:39:18 +0000 Subject: [PATCH 1269/2442] initial rhythm calc testing --- .../Difficulty/Skills/Aim.cs | 30 +++- .../Difficulty/Skills/OsuStrainSkill.cs | 2 +- .../Difficulty/Skills/Speed.cs | 133 +++++++++++++++++- 3 files changed, 151 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index 16a18cbcb9..776cca8657 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -22,17 +22,19 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { } - protected override double SkillMultiplier => 26.25; - protected override double StrainDecayBase => 0.15; + private double currentStrain = 1; - protected override double StrainValueOf(DifficultyHitObject current) + private double skillMultiplier => 26.25; + private double strainDecayBase => 0.15; + + private double aimStrainOf(DifficultyHitObject current) { if (current.BaseObject is Spinner) return 0; var osuCurrent = (OsuDifficultyHitObject)current; - double result = 0; + double aimStrain = 0; if (Previous.Count > 0) { @@ -46,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills Math.Max(osuPrevious.JumpDistance - scale, 0) * Math.Pow(Math.Sin(osuCurrent.Angle.Value - angle_bonus_begin), 2) * Math.Max(osuCurrent.JumpDistance - scale, 0)); - result = 1.4 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, osuPrevious.StrainTime); + aimStrain = 1.4 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, osuPrevious.StrainTime); } } @@ -54,11 +56,27 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double travelDistanceExp = applyDiminishingExp(osuCurrent.TravelDistance); return Math.Max( - result + (jumpDistanceExp + travelDistanceExp + Math.Sqrt(travelDistanceExp * jumpDistanceExp)) / Math.Max(osuCurrent.StrainTime, timing_threshold), + aimStrain + (jumpDistanceExp + travelDistanceExp + Math.Sqrt(travelDistanceExp * jumpDistanceExp)) / Math.Max(osuCurrent.StrainTime, timing_threshold), (Math.Sqrt(travelDistanceExp * jumpDistanceExp) + jumpDistanceExp + travelDistanceExp) / osuCurrent.StrainTime ); } private double applyDiminishingExp(double val) => Math.Pow(val, 0.99); + + private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000); + + protected override double CalculateInitialStrain(double time) => currentStrain * strainDecay(time - Previous[0].StartTime); + + protected override double StrainValueAt(DifficultyHitObject current) + { + double aimStrain = aimStrainOf(current); + + currentStrain *= strainDecay(current.DeltaTime); + currentStrain += aimStrain * skillMultiplier; + +// Console.WriteLine(currentStrain); + + return currentStrain; + } } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index 7bcd867a9c..e47edc37cc 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -10,7 +10,7 @@ using osu.Framework.Utils; namespace osu.Game.Rulesets.Osu.Difficulty.Skills { - public abstract class OsuStrainSkill : StrainDecaySkill + public abstract class OsuStrainSkill : StrainSkill { /// /// The number of sections with the highest strains, which the peak strain reductions will apply to. diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index f0eb199e5f..314bb31465 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -20,24 +20,121 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private const double pi_over_4 = Math.PI / 4; private const double pi_over_2 = Math.PI / 2; - protected override double SkillMultiplier => 1400; - protected override double StrainDecayBase => 0.3; + private const double rhythmMultiplier = 1.5; + + private double skillMultiplier => 1400; + private double strainDecayBase => 0.3; + private double difficultyMultiplier => 1.04; + + private double currentTapStrain = 1; + private double currentMovementStrain = 1; + protected override int ReducedSectionCount => 5; - protected override double DifficultyMultiplier => 1.04; private const double min_speed_bonus = 75; // ~200BPM private const double max_speed_bonus = 45; // ~330BPM private const double speed_balancing_factor = 40; + protected override int HistoryLength => 32; + + private const int HistoryTimeMax = 3000; // 4 seconds of calculatingRhythmBonus max. + public Speed(Mod[] mods) : base(mods) { } - protected override double StrainValueOf(DifficultyHitObject current) + private bool isRatioEqual(double ratio, double a, double b) { - if (current.BaseObject is Spinner) - return 0; + return a + 15 > ratio * b && a - 15 < ratio * b; + } + + /// + /// Calculates a rhythm multiplier for the difficulty of the tap associated with historic data of the current . + /// + private double calculateRhythmBonus(double startTime) + { + // {doubles, triplets, quads, quints, 6-tuplets, 7 Tuplets, greater} + int previousIslandSize = -1; + double[] islandTimes = {0, 0, 0, 0, 0, 0, 0}; + int islandSize = 0; + + bool firstDeltaSwitch = false; + + for (int i = Previous.Count - 1; i > 0; i--) + { + double currDelta = ((OsuDifficultyHitObject)Previous[i - 1]).StrainTime; + double prevDelta = ((OsuDifficultyHitObject)Previous[i]).StrainTime; + double effectiveRatio = Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta); + + effectiveRatio *= Math.Sqrt(100 / ((currDelta + prevDelta) / 2)); // scale with bpm. + + if (effectiveRatio > 0.5) + effectiveRatio = 0.5 + (effectiveRatio - 0.5) * 5; // extra buff for 1/3 -> 1/4 etc transitions. + + double currHistoricalDecay = Math.Max(0, (HistoryTimeMax - (startTime - Previous[i - 1].StartTime))) / HistoryTimeMax; + + if (firstDeltaSwitch) + { + if (isRatioEqual(1.0, prevDelta, currDelta)) + { + islandSize++; // island is still progressing, count size. + } + + else + { + if (islandSize > 6) + islandSize = 6; + + if (Previous[i - 1].BaseObject is Slider) // bpm change is into slider, this is easy acc window + effectiveRatio *= 0.5; + + if (Previous[i].BaseObject is Slider) // bpm change was from a slider, this is easier typically than circle -> circle + effectiveRatio *= 0.75; + + if (previousIslandSize == islandSize) // repeated island size (ex: triplet -> triplet) + effectiveRatio *= 0.5; + + islandTimes[islandSize] = islandTimes[islandSize] + effectiveRatio * currHistoricalDecay; + + previousIslandSize = islandSize; // log the last island size. + + if (prevDelta * 1.25 < currDelta) // we're slowing down, stop counting + firstDeltaSwitch = false; // if we're speeding up, this stays true and we keep counting island size. + + islandSize = 0; + } + } + else if (prevDelta > 1.25 * currDelta) // we want to be speeding up. + { + // Begin counting island until we change speed again. + firstDeltaSwitch = true; + islandSize = 0; + } + } + + double rhythmComplexitySum = 0.0; + + for (int i = 0; i < islandTimes.Length; i++) + { + rhythmComplexitySum += islandTimes[i]; // sum the total amount of rhythm variance + } + +// Console.WriteLine(Math.Sqrt(4 + rhythmComplexitySum * rhythmMultiplier) / 2); + + return Math.Sqrt(4 + rhythmComplexitySum * rhythmMultiplier) / 2; + } + + private void setStrainValues(DifficultyHitObject current) + { + // if (current.BaseObject is Spinner) + // { + // currentTapStrain *= strainDecay(current.DeltaTime); + // + // currentMovementStrain *= strainDecay(current.DeltaTime); + // + // return; + // } var osuCurrent = (OsuDifficultyHitObject)current; @@ -64,7 +161,29 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } } - return (1 + (speedBonus - 1) * 0.75) * angleBonus * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / osuCurrent.StrainTime; + // return (1 + (speedBonus - 1) * 0.75) * angleBonus * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / osuCurrent.StrainTime; + + double tapStrain = ((1 + (speedBonus - 1) * 0.75) * 0.95) / osuCurrent.StrainTime; + double movementStrain = ((1 + (speedBonus - 1) * 0.75) * angleBonus * speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / osuCurrent.StrainTime; + + currentTapStrain *= strainDecay(current.DeltaTime); + currentTapStrain += tapStrain * skillMultiplier; + + currentMovementStrain *= strainDecay(current.DeltaTime); + currentMovementStrain += movementStrain * skillMultiplier; + } + + private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000); + + protected override double CalculateInitialStrain(double time) => (currentMovementStrain + currentTapStrain) * strainDecay(time - Previous[0].StartTime); + + protected override double StrainValueAt(DifficultyHitObject current) + { + setStrainValues(current); + +// Console.WriteLine(currentMovementStrain + currentTapStrain); + + return currentMovementStrain + currentTapStrain * calculateRhythmBonus(current.StartTime); } } } From 471ae9664e67fd164276ce07db3700ce2296631b Mon Sep 17 00:00:00 2001 From: Xexxar Date: Tue, 17 Aug 2021 13:47:45 +0000 Subject: [PATCH 1270/2442] cleaned up jank --- .../Difficulty/Skills/Speed.cs | 62 +++++++++++++------ 1 file changed, 43 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 314bb31465..89dd7efbdf 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -125,16 +125,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills return Math.Sqrt(4 + rhythmComplexitySum * rhythmMultiplier) / 2; } - private void setStrainValues(DifficultyHitObject current) + private double tapStrainOf(DifficultyHitObject current) { - // if (current.BaseObject is Spinner) - // { - // currentTapStrain *= strainDecay(current.DeltaTime); - // - // currentMovementStrain *= strainDecay(current.DeltaTime); - // - // return; - // } + if (current.BaseObject is Spinner) + { + return 0; + } var osuCurrent = (OsuDifficultyHitObject)current; @@ -161,16 +157,42 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } } - // return (1 + (speedBonus - 1) * 0.75) * angleBonus * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / osuCurrent.StrainTime; + return ((1 + (speedBonus - 1) * 0.75) * angleBonus * 0.95) / osuCurrent.StrainTime; + } - double tapStrain = ((1 + (speedBonus - 1) * 0.75) * 0.95) / osuCurrent.StrainTime; - double movementStrain = ((1 + (speedBonus - 1) * 0.75) * angleBonus * speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / osuCurrent.StrainTime; + private double movementStrainOf(DifficultyHitObject current) + { + if (current.BaseObject is Spinner) + { + return 0; + } - currentTapStrain *= strainDecay(current.DeltaTime); - currentTapStrain += tapStrain * skillMultiplier; + var osuCurrent = (OsuDifficultyHitObject)current; - currentMovementStrain *= strainDecay(current.DeltaTime); - currentMovementStrain += movementStrain * skillMultiplier; + double distance = Math.Min(single_spacing_threshold, osuCurrent.TravelDistance + osuCurrent.JumpDistance); + double deltaTime = Math.Max(max_speed_bonus, current.DeltaTime); + + double speedBonus = 1.0; + if (deltaTime < min_speed_bonus) + speedBonus = 1 + Math.Pow((min_speed_bonus - deltaTime) / speed_balancing_factor, 2); + + double angleBonus = 1.0; + + if (osuCurrent.Angle != null && osuCurrent.Angle.Value < angle_bonus_begin) + { + angleBonus = 1 + Math.Pow(Math.Sin(1.5 * (angle_bonus_begin - osuCurrent.Angle.Value)), 2) / 3.57; + + if (osuCurrent.Angle.Value < pi_over_2) + { + angleBonus = 1.28; + if (distance < 90 && osuCurrent.Angle.Value < pi_over_4) + angleBonus += (1 - angleBonus) * Math.Min((90 - distance) / 10, 1); + else if (distance < 90) + angleBonus += (1 - angleBonus) * Math.Min((90 - distance) / 10, 1) * Math.Sin((pi_over_2 - osuCurrent.Angle.Value) / pi_over_4); + } + } + + return ((1 + (speedBonus - 1) * 0.75) * angleBonus * speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / osuCurrent.StrainTime; } private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000); @@ -179,11 +201,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override double StrainValueAt(DifficultyHitObject current) { - setStrainValues(current); + currentTapStrain *= strainDecay(current.DeltaTime); + currentTapStrain += tapStrainOf(current) * skillMultiplier; -// Console.WriteLine(currentMovementStrain + currentTapStrain); + currentMovementStrain *= strainDecay(current.DeltaTime); + currentMovementStrain += movementStrainOf(current) * skillMultiplier; - return currentMovementStrain + currentTapStrain * calculateRhythmBonus(current.StartTime); + return currentMovementStrain + currentTapStrain;// * calculateRhythmBonus(current.StartTime); } } } From ac1ed00f3e0847619bbe11e37fc1f0260924dbf3 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Tue, 17 Aug 2021 14:39:43 +0000 Subject: [PATCH 1271/2442] added initial balance of rhythm complexity to tap --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 89dd7efbdf..52fc77b6f6 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -24,12 +24,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private double skillMultiplier => 1400; private double strainDecayBase => 0.3; - private double difficultyMultiplier => 1.04; private double currentTapStrain = 1; private double currentMovementStrain = 1; protected override int ReducedSectionCount => 5; + protected override double DifficultyMultiplier => 1.04; private const double min_speed_bonus = 75; // ~200BPM private const double max_speed_bonus = 45; // ~330BPM @@ -67,10 +67,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double prevDelta = ((OsuDifficultyHitObject)Previous[i]).StrainTime; double effectiveRatio = Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta); - effectiveRatio *= Math.Sqrt(100 / ((currDelta + prevDelta) / 2)); // scale with bpm. + effectiveRatio *= Math.Sqrt(100 / ((currDelta + prevDelta) / 2)); // scale with bpm slightly if (effectiveRatio > 0.5) - effectiveRatio = 0.5 + (effectiveRatio - 0.5) * 5; // extra buff for 1/3 -> 1/4 etc transitions. + effectiveRatio = 0.5 + (effectiveRatio - 0.5) * 10; // extra buff for 1/3 -> 1/4 etc transitions. double currHistoricalDecay = Math.Max(0, (HistoryTimeMax - (startTime - Previous[i - 1].StartTime))) / HistoryTimeMax; @@ -117,7 +117,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills for (int i = 0; i < islandTimes.Length; i++) { - rhythmComplexitySum += islandTimes[i]; // sum the total amount of rhythm variance + rhythmComplexitySum += islandTimes[i] * ((double)(i + islandTimes.Length) / (2 * islandTimes.Length)); // sum the total amount of rhythm variance } // Console.WriteLine(Math.Sqrt(4 + rhythmComplexitySum * rhythmMultiplier) / 2); @@ -157,7 +157,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } } - return ((1 + (speedBonus - 1) * 0.75) * angleBonus * 0.95) / osuCurrent.StrainTime; + return ((1 + (speedBonus - 1) * 0.75) * 0.95) / osuCurrent.StrainTime; } private double movementStrainOf(DifficultyHitObject current) @@ -197,7 +197,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000); - protected override double CalculateInitialStrain(double time) => (currentMovementStrain + currentTapStrain) * strainDecay(time - Previous[0].StartTime); + protected override double CalculateInitialStrain(double time) => (currentMovementStrain + currentTapStrain * calculateRhythmBonus(time)) * strainDecay(time - Previous[0].StartTime); protected override double StrainValueAt(DifficultyHitObject current) { @@ -207,7 +207,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills currentMovementStrain *= strainDecay(current.DeltaTime); currentMovementStrain += movementStrainOf(current) * skillMultiplier; - return currentMovementStrain + currentTapStrain;// * calculateRhythmBonus(current.StartTime); + return currentMovementStrain + currentTapStrain * calculateRhythmBonus(current.StartTime); } } } From 2296ee60598cea4d5fb1d276d624d3d54874e312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20Sch=C3=BCrz?= Date: Tue, 17 Aug 2021 16:56:06 +0200 Subject: [PATCH 1272/2442] Add test coverage --- .../Visual/Editing/TestSceneEditorClock.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs index 0b1617b6a6..0abf0c47f8 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs @@ -78,6 +78,24 @@ namespace osu.Game.Tests.Visual.Editing AddAssert("clock looped to start", () => Clock.IsRunning && Clock.CurrentTime < 500); } + [Test] + public void TestClampWhenSeekOutsideBeatmapBounds() + { + AddStep("stop clock", Clock.Stop); + + AddStep("seek before start time", () => Clock.Seek(-1000)); + AddAssert("time is clamped to 0", () => Clock.CurrentTime == 0); + + AddStep("seek beyond track length", () => Clock.Seek(Clock.TrackLength + 1000)); + AddAssert("time is clamped to track length", () => Clock.CurrentTime == Clock.TrackLength); + + AddStep("seek smoothly before start time", () => Clock.SeekSmoothlyTo(-1000)); + AddAssert("time is clamped to 0", () => Clock.CurrentTime == 0); + + AddStep("seek smoothly beyond track length", () => Clock.SeekSmoothlyTo(Clock.TrackLength + 1000)); + AddAssert("time is clamped to track length", () => Clock.CurrentTime == Clock.TrackLength); + } + protected override void Dispose(bool isDisposing) { Beatmap.Disabled = false; From b9ba4c1d9761e8eb5c8556f592303961033cd83d Mon Sep 17 00:00:00 2001 From: Xexxar Date: Tue, 17 Aug 2021 19:25:49 +0000 Subject: [PATCH 1273/2442] finalized change for PR --- .../Difficulty/Skills/Speed.cs | 63 +++++++------------ 1 file changed, 21 insertions(+), 42 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 52fc77b6f6..935e80ebc5 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -21,12 +21,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private const double pi_over_2 = Math.PI / 2; private const double rhythmMultiplier = 1.5; + private const int HistoryTimeMax = 3000; // 3 seconds of calculatingRhythmBonus max. private double skillMultiplier => 1400; private double strainDecayBase => 0.3; private double currentTapStrain = 1; private double currentMovementStrain = 1; + private double currentRhythm = 1; protected override int ReducedSectionCount => 5; protected override double DifficultyMultiplier => 1.04; @@ -37,8 +39,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override int HistoryLength => 32; - private const int HistoryTimeMax = 3000; // 4 seconds of calculatingRhythmBonus max. - public Speed(Mod[] mods) : base(mods) { @@ -52,8 +52,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// /// Calculates a rhythm multiplier for the difficulty of the tap associated with historic data of the current . /// - private double calculateRhythmBonus(double startTime) + private double calculateRhythmBonus(DifficultyHitObject current) { + if (current.BaseObject is Spinner) + return 0; + // {doubles, triplets, quads, quints, 6-tuplets, 7 Tuplets, greater} int previousIslandSize = -1; double[] islandTimes = {0, 0, 0, 0, 0, 0, 0}; @@ -72,7 +75,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills if (effectiveRatio > 0.5) effectiveRatio = 0.5 + (effectiveRatio - 0.5) * 10; // extra buff for 1/3 -> 1/4 etc transitions. - double currHistoricalDecay = Math.Max(0, (HistoryTimeMax - (startTime - Previous[i - 1].StartTime))) / HistoryTimeMax; + double currHistoricalDecay = Math.Max(0, (HistoryTimeMax - (current.StartTime - Previous[i - 1].StartTime))) / HistoryTimeMax; if (firstDeltaSwitch) { @@ -125,56 +128,24 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills return Math.Sqrt(4 + rhythmComplexitySum * rhythmMultiplier) / 2; } - private double tapStrainOf(DifficultyHitObject current) + private double tapStrainOf(DifficultyHitObject current, double speedBonus) { if (current.BaseObject is Spinner) - { return 0; - } var osuCurrent = (OsuDifficultyHitObject)current; - double distance = Math.Min(single_spacing_threshold, osuCurrent.TravelDistance + osuCurrent.JumpDistance); - double deltaTime = Math.Max(max_speed_bonus, current.DeltaTime); - - double speedBonus = 1.0; - if (deltaTime < min_speed_bonus) - speedBonus = 1 + Math.Pow((min_speed_bonus - deltaTime) / speed_balancing_factor, 2); - - double angleBonus = 1.0; - - if (osuCurrent.Angle != null && osuCurrent.Angle.Value < angle_bonus_begin) - { - angleBonus = 1 + Math.Pow(Math.Sin(1.5 * (angle_bonus_begin - osuCurrent.Angle.Value)), 2) / 3.57; - - if (osuCurrent.Angle.Value < pi_over_2) - { - angleBonus = 1.28; - if (distance < 90 && osuCurrent.Angle.Value < pi_over_4) - angleBonus += (1 - angleBonus) * Math.Min((90 - distance) / 10, 1); - else if (distance < 90) - angleBonus += (1 - angleBonus) * Math.Min((90 - distance) / 10, 1) * Math.Sin((pi_over_2 - osuCurrent.Angle.Value) / pi_over_4); - } - } - return ((1 + (speedBonus - 1) * 0.75) * 0.95) / osuCurrent.StrainTime; } - private double movementStrainOf(DifficultyHitObject current) + private double movementStrainOf(DifficultyHitObject current, double speedBonus) { if (current.BaseObject is Spinner) - { return 0; - } var osuCurrent = (OsuDifficultyHitObject)current; double distance = Math.Min(single_spacing_threshold, osuCurrent.TravelDistance + osuCurrent.JumpDistance); - double deltaTime = Math.Max(max_speed_bonus, current.DeltaTime); - - double speedBonus = 1.0; - if (deltaTime < min_speed_bonus) - speedBonus = 1 + Math.Pow((min_speed_bonus - deltaTime) / speed_balancing_factor, 2); double angleBonus = 1.0; @@ -197,17 +168,25 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000); - protected override double CalculateInitialStrain(double time) => (currentMovementStrain + currentTapStrain * calculateRhythmBonus(time)) * strainDecay(time - Previous[0].StartTime); + protected override double CalculateInitialStrain(double time) => (currentMovementStrain + currentTapStrain * currentRhythm) * strainDecay(time - Previous[0].StartTime); protected override double StrainValueAt(DifficultyHitObject current) { + double speedBonus = 1.0; + double deltaTime = Math.Max(max_speed_bonus, current.DeltaTime); + + if (deltaTime < min_speed_bonus) + speedBonus = 1 + Math.Pow((min_speed_bonus - deltaTime) / speed_balancing_factor, 2); + + currentRhythm = calculateRhythmBonus(current); + currentTapStrain *= strainDecay(current.DeltaTime); - currentTapStrain += tapStrainOf(current) * skillMultiplier; + currentTapStrain += tapStrainOf(current, speedBonus) * skillMultiplier; currentMovementStrain *= strainDecay(current.DeltaTime); - currentMovementStrain += movementStrainOf(current) * skillMultiplier; + currentMovementStrain += movementStrainOf(current, speedBonus) * skillMultiplier; - return currentMovementStrain + currentTapStrain * calculateRhythmBonus(current.StartTime); + return currentMovementStrain + currentTapStrain * currentRhythm; } } } From 58ecee543ad546c7c1e1f91ae6fb1187920f0b41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 17 Aug 2021 23:00:10 +0200 Subject: [PATCH 1274/2442] Trim redundant default argument value --- osu.Game/Skinning/SkinManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 51aaac1f79..0f805990b9 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -95,7 +95,7 @@ namespace osu.Game.Skinning /// A newly allocated list of available . public List GetAllUsableSkins() { - var userSkins = GetAllUserSkins(false); + var userSkins = GetAllUserSkins(); userSkins.Insert(0, DefaultSkin.SkinInfo); userSkins.Insert(1, DefaultLegacySkin.SkinInfo); return userSkins; From eaca331170c8fb7f7303a55fe630c3400d1ddde6 Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Wed, 18 Aug 2021 08:13:53 +0800 Subject: [PATCH 1275/2442] apply suggestions --- osu.Game/Rulesets/Ruleset.cs | 2 +- osu.Game/Users/UserActivity.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 3e7479643e..80be61ead1 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -224,7 +224,7 @@ namespace osu.Game.Rulesets public abstract string ShortName { get; } /// - /// The playing verb to be shown in the . + /// The playing verb to be shown in the activities. /// public virtual string PlayingVerb => "Playing"; diff --git a/osu.Game/Users/UserActivity.cs b/osu.Game/Users/UserActivity.cs index 1bbe38b416..88b91a3b42 100644 --- a/osu.Game/Users/UserActivity.cs +++ b/osu.Game/Users/UserActivity.cs @@ -25,13 +25,13 @@ namespace osu.Game.Users public override string Status => "Choosing a beatmap"; } - public class InGame : UserActivity + public abstract class InGame : UserActivity { public BeatmapInfo Beatmap { get; } public RulesetInfo Ruleset { get; } - public InGame(BeatmapInfo info, RulesetInfo ruleset) + protected InGame(BeatmapInfo info, RulesetInfo ruleset) { Beatmap = info; Ruleset = ruleset; From 8c5d99ab21c71fe5e1c743835c8b5245c8b17246 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 18 Aug 2021 04:16:57 +0300 Subject: [PATCH 1276/2442] Override `CreateInstance()` in osu! bindable subclasses Three bindables are left which don't have this overriden due to them already not having a value-only constructor and not supporting `GetBoundCopy()` properly: - `BeatmapDifficultyCache.BindableStarDifficulty`. - `TotalScoreBindable` - `TotalScoreStringBindable` I could add support for them by passing the required data to them, as they seem to be able to have that shared, but I'm hesitant to support something which was already broken and never used, not sure. --- osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs | 2 +- osu.Game/Rulesets/Mods/DifficultyBindable.cs | 2 +- osu.Game/Screens/Edit/BindableBeatDivisor.cs | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs index 186514e868..3978378c3a 100644 --- a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs +++ b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Mods { // Intercept and extract the internal number bindable from DifficultyBindable. // This will provide bounds and precision specifications for the slider bar. - difficultyBindable = ((DifficultyBindable)value).GetBoundCopy(); + difficultyBindable = (DifficultyBindable)value.GetBoundCopy(); sliderDisplayCurrent.BindTo(difficultyBindable.CurrentNumber); base.Current = difficultyBindable; diff --git a/osu.Game/Rulesets/Mods/DifficultyBindable.cs b/osu.Game/Rulesets/Mods/DifficultyBindable.cs index 664b88eef4..e4304795f2 100644 --- a/osu.Game/Rulesets/Mods/DifficultyBindable.cs +++ b/osu.Game/Rulesets/Mods/DifficultyBindable.cs @@ -128,6 +128,6 @@ namespace osu.Game.Rulesets.Mods ExtendedLimits.UnbindFrom(otherDifficultyBindable.ExtendedLimits); } - public new DifficultyBindable GetBoundCopy() => new DifficultyBindable { BindTarget = this }; + protected override Bindable CreateInstance() => new DifficultyBindable(); } } diff --git a/osu.Game/Screens/Edit/BindableBeatDivisor.cs b/osu.Game/Screens/Edit/BindableBeatDivisor.cs index ff33f0c70d..dfe2992a7c 100644 --- a/osu.Game/Screens/Edit/BindableBeatDivisor.cs +++ b/osu.Game/Screens/Edit/BindableBeatDivisor.cs @@ -41,6 +41,8 @@ namespace osu.Game.Screens.Edit protected override int DefaultMaxValue => VALID_DIVISORS.Last(); protected override int DefaultPrecision => 1; + protected override Bindable CreateInstance() => new BindableBeatDivisor(); + /// /// Retrieves the appropriate colour for a beat divisor. /// From 5671820d92011d83f3d170df01e0f5f07fcc1837 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Aug 2021 10:35:34 +0900 Subject: [PATCH 1277/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index ec223f98c2..24d07b4588 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index d4dba9330f..928620b32e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 7e514afe74..77f9052e85 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From f5923508564b8d24f3d9ca475c7144aa466b9332 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 18 Aug 2021 04:59:08 +0300 Subject: [PATCH 1278/2442] Fix config pollution in HUD overlay test scene --- .../Visual/Gameplay/TestSceneHUDOverlay.cs | 51 ++++++++----------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs index 3017428039..4f15032c62 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs @@ -19,6 +19,8 @@ namespace osu.Game.Tests.Visual.Gameplay { public class TestSceneHUDOverlay : OsuManualInputManagerTestScene { + private OsuConfigManager localConfig; + private HUDOverlay hudOverlay; [Cached] @@ -31,8 +33,14 @@ namespace osu.Game.Tests.Visual.Gameplay private Drawable hideTarget => hudOverlay.KeyCounter; private FillFlowContainer keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType>().First(); - [Resolved] - private OsuConfigManager config { get; set; } + [BackgroundDependencyLoader] + private void load() + { + Dependencies.Cache(localConfig = new OsuConfigManager(LocalStorage)); + } + + [SetUpSteps] + public void SetUp() => Schedule(() => localConfig.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always)); [Test] public void TestComboCounterIncrementing() @@ -85,11 +93,7 @@ namespace osu.Game.Tests.Visual.Gameplay { createNew(); - HUDVisibilityMode originalConfigValue = HUDVisibilityMode.HideDuringGameplay; - - AddStep("get original config value", () => originalConfigValue = config.Get(OsuSetting.HUDVisibilityMode)); - - AddStep("set hud to never show", () => config.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never)); + AddStep("set hud to never show", () => localConfig.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never)); AddUntilStep("wait for fade", () => !hideTarget.IsPresent); @@ -98,37 +102,28 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("stop trigering", () => InputManager.ReleaseKey(Key.ControlLeft)); AddUntilStep("wait for fade", () => !hideTarget.IsPresent); - - AddStep("set original config value", () => config.SetValue(OsuSetting.HUDVisibilityMode, originalConfigValue)); } [Test] public void TestExternalHideDoesntAffectConfig() { - HUDVisibilityMode originalConfigValue = HUDVisibilityMode.HideDuringGameplay; - createNew(); - AddStep("get original config value", () => originalConfigValue = config.Get(OsuSetting.HUDVisibilityMode)); - AddStep("set showhud false", () => hudOverlay.ShowHud.Value = false); - AddAssert("config unchanged", () => originalConfigValue == config.Get(OsuSetting.HUDVisibilityMode)); + AddAssert("config unchanged", () => localConfig.GetBindable(OsuSetting.HUDVisibilityMode).IsDefault); AddStep("set showhud true", () => hudOverlay.ShowHud.Value = true); - AddAssert("config unchanged", () => originalConfigValue == config.Get(OsuSetting.HUDVisibilityMode)); + AddAssert("config unchanged", () => localConfig.GetBindable(OsuSetting.HUDVisibilityMode).IsDefault); } [Test] public void TestChangeHUDVisibilityOnHiddenKeyCounter() { - bool keyCounterVisibleValue = false; - createNew(); - AddStep("save keycounter visible value", () => keyCounterVisibleValue = config.Get(OsuSetting.KeyOverlay)); - AddStep("set keycounter visible false", () => + AddStep("hide key overlay", () => { - config.SetValue(OsuSetting.KeyOverlay, false); + localConfig.SetValue(OsuSetting.KeyOverlay, false); hudOverlay.KeyCounter.AlwaysVisible.Value = false; }); @@ -139,24 +134,16 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("set showhud true", () => hudOverlay.ShowHud.Value = true); AddUntilStep("hidetarget is visible", () => hideTarget.IsPresent); AddAssert("key counters still hidden", () => !keyCounterFlow.IsPresent); - - AddStep("return value", () => config.SetValue(OsuSetting.KeyOverlay, keyCounterVisibleValue)); } [Test] public void TestHiddenHUDDoesntBlockSkinnableComponentsLoad() { - HUDVisibilityMode originalConfigValue = default; - - AddStep("get original config value", () => originalConfigValue = config.Get(OsuSetting.HUDVisibilityMode)); - - AddStep("set hud to never show", () => config.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never)); + AddStep("set hud to never show", () => localConfig.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never)); createNew(); AddUntilStep("wait for hud load", () => hudOverlay.IsLoaded); AddUntilStep("skinnable components loaded", () => hudOverlay.ChildrenOfType().Single().ComponentsLoaded); - - AddStep("set original config value", () => config.SetValue(OsuSetting.HUDVisibilityMode, originalConfigValue)); } private void createNew(Action action = null) @@ -175,5 +162,11 @@ namespace osu.Game.Tests.Visual.Gameplay Child = hudOverlay; }); } + + protected override void Dispose(bool isDisposing) + { + localConfig?.Dispose(); + base.Dispose(isDisposing); + } } } From 1fdaefef99d4b79cba8cbc428fb7d55cd5597cc9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Aug 2021 12:45:08 +0900 Subject: [PATCH 1279/2442] Revert unnecessary changes --- osu.Game/Overlays/SettingsPanel.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index fea797287e..376e36ea4e 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -14,7 +14,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; -using osu.Framework.Threading; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; @@ -180,7 +179,7 @@ namespace osu.Game.Overlays protected override void OnFocus(FocusEvent e) { - searchTextBox?.TakeFocus(); + searchTextBox.TakeFocus(); base.OnFocus(e); } @@ -274,8 +273,6 @@ namespace osu.Game.Overlays { public SearchContainer SearchContainer; - public new ScheduledDelegate Schedule(Action action) => Scheduler.AddDelayed(action, TransformDelay); - protected override FlowContainer CreateScrollContentContainer() => SearchContainer = new SearchContainer { From 5441fab6922bc05344b3797898502742d7d89dda Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Aug 2021 12:45:14 +0900 Subject: [PATCH 1280/2442] Avoid scheduling focus operation when not required --- osu.Game/Graphics/UserInterface/FocusedTextBox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs index 4a42027964..06a40e7245 100644 --- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs +++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs @@ -25,7 +25,7 @@ namespace osu.Game.Graphics.UserInterface if (!allowImmediateFocus) return; - Schedule(() => GetContainingInputManager().ChangeFocus(this)); + Scheduler.Add(() => GetContainingInputManager().ChangeFocus(this), false); } public bool HoldFocus From 6ed3e469f7549b0346314d31e606d2ff942e542c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 18 Aug 2021 06:50:01 +0300 Subject: [PATCH 1281/2442] Fix wrong attribute used for setup method --- osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs index 4f15032c62..290ba3317b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs @@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.Gameplay Dependencies.Cache(localConfig = new OsuConfigManager(LocalStorage)); } - [SetUpSteps] + [SetUp] public void SetUp() => Schedule(() => localConfig.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always)); [Test] From c66abf85f71d4a755262c805f06bccf7782b54d5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 18 Aug 2021 14:04:44 +0900 Subject: [PATCH 1282/2442] Remove match header --- .../Match/MultiplayerMatchHeader.cs | 106 ------------------ .../Multiplayer/MultiplayerMatchSubScreen.cs | 14 --- 2 files changed, 120 deletions(-) delete mode 100644 osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchHeader.cs diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchHeader.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchHeader.cs deleted file mode 100644 index bb351d06d3..0000000000 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchHeader.cs +++ /dev/null @@ -1,106 +0,0 @@ -// 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 osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.UserInterface; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Online.API; -using osu.Game.Screens.OnlinePlay.Match.Components; -using osu.Game.Users.Drawables; -using osuTK; -using FontWeight = osu.Game.Graphics.FontWeight; -using OsuColour = osu.Game.Graphics.OsuColour; -using OsuFont = osu.Game.Graphics.OsuFont; - -namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match -{ - public class MultiplayerMatchHeader : OnlinePlayComposite - { - public const float HEIGHT = 50; - - public Action OpenSettings; - - private UpdateableAvatar avatar; - private LinkFlowContainer hostText; - private Button openSettingsButton; - - [Resolved] - private IAPIProvider api { get; set; } - - public MultiplayerMatchHeader() - { - RelativeSizeAxes = Axes.X; - Height = HEIGHT; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - InternalChildren = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(10, 0), - Children = new Drawable[] - { - avatar = new UpdateableAvatar - { - Size = new Vector2(50), - Masking = true, - CornerRadius = 10, - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new OsuSpriteText - { - Font = OsuFont.GetFont(size: 30), - Current = { BindTarget = RoomName } - }, - hostText = new LinkFlowContainer(s => s.Font = OsuFont.GetFont(size: 20)) - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - } - } - } - } - }, - openSettingsButton = new PurpleTriangleButton - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Size = new Vector2(150, HEIGHT), - Text = "Open settings", - Action = () => OpenSettings?.Invoke(), - Alpha = 0 - } - }; - - Host.BindValueChanged(host => - { - avatar.User = host.NewValue; - - hostText.Clear(); - - if (host.NewValue != null) - { - hostText.AddText("hosted by "); - hostText.AddUserLink(host.NewValue, s => s.Font = s.Font.With(weight: FontWeight.SemiBold)); - } - - openSettingsButton.Alpha = host.NewValue?.Equals(api.LocalUser.Value) == true ? 1 : 0; - }, true); - } - } -} diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 09c2a22850..93377b221f 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -59,8 +59,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [CanBeNull] private IDisposable readyClickOperation; - // private GridContainer mainContent; - public MultiplayerMatchSubScreen(Room room) : base(room) { @@ -130,20 +128,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer Child = new GridContainer { RelativeSizeAxes = Axes.Both, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - new Dimension(), - }, Content = new[] { - new Drawable[] - { - new MultiplayerMatchHeader - { - OpenSettings = () => SettingsOverlay.Show() - } - }, new Drawable[] { new Container From 3d88a745cd2f5184e114447c329b89f45ca339ca Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 18 Aug 2021 14:27:05 +0900 Subject: [PATCH 1283/2442] Fix osu editor transforms not specified in the absolute time --- .../Edit/DrawableOsuEditorRuleset.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs index d4f1602a46..c89527d8bd 100644 --- a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs +++ b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs @@ -64,11 +64,14 @@ namespace osu.Game.Rulesets.Osu.Edit if (hitObject is DrawableHitCircle circle) { - circle.ApproachCircle - .FadeOutFromOne(EDITOR_HIT_OBJECT_FADE_OUT_EXTENSION * 4) - .Expire(); + using (circle.BeginAbsoluteSequence(circle.HitStateUpdateTime)) + { + circle.ApproachCircle + .FadeOutFromOne(EDITOR_HIT_OBJECT_FADE_OUT_EXTENSION * 4) + .Expire(); - circle.ApproachCircle.ScaleTo(1.1f, 300, Easing.OutQuint); + circle.ApproachCircle.ScaleTo(1.1f, 300, Easing.OutQuint); + } } if (hitObject is IHasMainCirclePiece mainPieceContainer) From 0853554c2403700be54db9763466788bd19379f8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 18 Aug 2021 15:11:52 +0900 Subject: [PATCH 1284/2442] Fix settings overlay not being initially visible --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 3addf57048..9e729515d2 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -162,7 +162,10 @@ namespace osu.Game.Screens.OnlinePlay.Match } } }, - SettingsOverlay = CreateRoomSettingsOverlay() + SettingsOverlay = CreateRoomSettingsOverlay().With(s => + { + s.State.Value = room.RoomID.Value == null ? Visibility.Visible : Visibility.Hidden; + }) }, }, }, From 704af94d399517093b084809c7144d165f57965c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 18 Aug 2021 15:12:16 +0900 Subject: [PATCH 1285/2442] Add edit button to room panel --- .../Lounge/Components/DrawableRoom.cs | 20 ++++--- .../Match/DrawableMultiplayerRoom.cs | 54 +++++++++++++++++++ .../Multiplayer/MultiplayerMatchSubScreen.cs | 5 +- 3 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 osu.Game/Screens/OnlinePlay/Multiplayer/Match/DrawableMultiplayerRoom.cs diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index c8ecd65574..4e81739dc4 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -124,17 +124,23 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } + public bool FilteringActive { get; set; } + + protected readonly Container ButtonsContainer = new Container + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X + }; + private readonly Bindable roomCategory = new Bindable(); + private readonly Bindable hasPassword = new Bindable(); private RecentParticipantsList recentParticipantsList; private RoomSpecialCategoryPill specialCategoryPill; - - public bool FilteringActive { get; set; } - private PasswordProtectedIcon passwordIcon; - private readonly Bindable hasPassword = new Bindable(); - public DrawableRoom(Room room) { Room = room; @@ -289,13 +295,15 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Origin = Anchor.CentreRight, AutoSizeAxes = Axes.X, RelativeSizeAxes = Axes.Y, + Spacing = new Vector2(5), Padding = new MarginPadding { Right = 10, - Vertical = 5 + Vertical = 20, }, Children = new Drawable[] { + ButtonsContainer, recentParticipantsList = new RecentParticipantsList { Anchor = Anchor.CentreRight, diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/DrawableMultiplayerRoom.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/DrawableMultiplayerRoom.cs new file mode 100644 index 0000000000..84ae6108f6 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/DrawableMultiplayerRoom.cs @@ -0,0 +1,54 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Online.API; +using osu.Game.Online.Rooms; +using osu.Game.Screens.OnlinePlay.Lounge.Components; +using osu.Game.Screens.OnlinePlay.Match.Components; +using osu.Game.Users; +using osuTK; + +namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match +{ + public class DrawableMultiplayerRoom : DrawableRoom + { + public Action OnEdit; + + [Resolved] + private IAPIProvider api { get; set; } + + private readonly IBindable host = new Bindable(); + + private Drawable editButton; + + public DrawableMultiplayerRoom(Room room) + : base(room) + { + host.BindTo(room.Host); + } + + [BackgroundDependencyLoader] + private void load() + { + ButtonsContainer.Add(editButton = new PurpleTriangleButton + { + RelativeSizeAxes = Axes.Y, + Size = new Vector2(100, 1), + Text = "Edit", + Action = () => OnEdit?.Invoke() + }); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + host.BindValueChanged(h => editButton.Alpha = h.NewValue?.Equals(api.LocalUser.Value) == true ? 1 : 0, true); + } + + protected override bool ShouldBeConsideredForInput(Drawable child) => true; + } +} diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 93377b221f..3cbc6c36b6 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -110,7 +110,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer Mods.Value = client.LocalUser.Mods.Select(m => m.ToMod(ruleset)).Concat(SelectedItem.Value.RequiredMods).ToList(); } - protected override DrawableRoom CreateDrawableRoom(Room room) => new DrawableRoom(room); + protected override DrawableRoom CreateDrawableRoom(Room room) => new DrawableMultiplayerRoom(room) + { + OnEdit = () => SettingsOverlay.Show() + }; protected override Drawable CreateMainContent() => new Container { From 1ae4a1910a135a8c1084d332bbf2650fa13abdf2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 18 Aug 2021 09:17:42 +0300 Subject: [PATCH 1286/2442] Cache mod settings rather than fetching everytime --- osu.Game/Rulesets/Mods/Mod.cs | 36 +++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index a99ddc6924..8c2a8a8e8b 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using JetBrains.Annotations; using Newtonsoft.Json; using osu.Framework.Bindables; using osu.Framework.Extensions.TypeExtensions; @@ -129,6 +130,17 @@ namespace osu.Game.Rulesets.Mods [JsonIgnore] public virtual Type[] IncompatibleMods => Array.Empty(); + private IReadOnlyList settingsBacking; + + /// + /// A list of the all settings within this mod. + /// + internal IReadOnlyList Settings => + settingsBacking ??= this.GetSettingsSourceProperties() + .Select(p => p.Item2.GetValue(this)) + .Cast() + .ToList(); + /// /// Creates a copy of this initialised to a default state. /// @@ -191,10 +203,7 @@ namespace osu.Game.Rulesets.Mods if (ReferenceEquals(this, other)) return true; return GetType() == other.GetType() && - this.GetSettingsSourceProperties().All(pair => - EqualityComparer.Default.Equals( - ModUtils.GetSettingUnderlyingValue(pair.Item2.GetValue(this)), - ModUtils.GetSettingUnderlyingValue(pair.Item2.GetValue(other)))); + Settings.SequenceEqual(other.Settings, ModSettingsEqualityComparer.Default); } public override int GetHashCode() @@ -203,8 +212,8 @@ namespace osu.Game.Rulesets.Mods hashCode.Add(GetType()); - foreach (var (_, prop) in this.GetSettingsSourceProperties()) - hashCode.Add(ModUtils.GetSettingUnderlyingValue(prop.GetValue(this))); + foreach (var setting in Settings) + hashCode.Add(ModUtils.GetSettingUnderlyingValue(setting)); return hashCode.ToHashCode(); } @@ -213,5 +222,20 @@ namespace osu.Game.Rulesets.Mods /// Reset all custom settings for this mod back to their defaults. /// public virtual void ResetSettingsToDefaults() => CopyFrom((Mod)Activator.CreateInstance(GetType())); + + private class ModSettingsEqualityComparer : IEqualityComparer + { + public static ModSettingsEqualityComparer Default { get; } = new ModSettingsEqualityComparer(); + + public bool Equals(IBindable x, IBindable y) + { + object xValue = x == null ? null : ModUtils.GetSettingUnderlyingValue(x); + object yValue = y == null ? null : ModUtils.GetSettingUnderlyingValue(y); + + return EqualityComparer.Default.Equals(xValue, yValue); + } + + public int GetHashCode(IBindable obj) => ModUtils.GetSettingUnderlyingValue(obj).GetHashCode(); + } } } From 96b6670d1fe2e3018acdfe8ee0dd98a0981b5f6f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 18 Aug 2021 09:18:03 +0300 Subject: [PATCH 1287/2442] Add mod benchmark --- osu.Game.Benchmarks/BenchmarkMod.cs | 34 +++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 osu.Game.Benchmarks/BenchmarkMod.cs diff --git a/osu.Game.Benchmarks/BenchmarkMod.cs b/osu.Game.Benchmarks/BenchmarkMod.cs new file mode 100644 index 0000000000..050ddf36d5 --- /dev/null +++ b/osu.Game.Benchmarks/BenchmarkMod.cs @@ -0,0 +1,34 @@ +// 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 BenchmarkDotNet.Attributes; +using osu.Game.Rulesets.Osu.Mods; + +namespace osu.Game.Benchmarks +{ + public class BenchmarkMod : BenchmarkTest + { + private OsuModDoubleTime mod; + + [Params(1, 10, 100)] + public int Times { get; set; } + + [GlobalSetup] + public void GlobalSetup() + { + mod = new OsuModDoubleTime(); + } + + [Benchmark] + public int ModHashCode() + { + var hashCode = new HashCode(); + + for (int i = 0; i < Times; i++) + hashCode.Add(mod); + + return hashCode.ToHashCode(); + } + } +} From c5268c9a99d820d16534fc912e15cd02a1d08894 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 18 Aug 2021 15:16:48 +0900 Subject: [PATCH 1288/2442] Simplify by reusing same room panel --- .../DrawableMatchRoom.cs} | 6 +++--- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 19 ++++++++++--------- .../Multiplayer/MultiplayerMatchSubScreen.cs | 6 ------ .../Playlists/PlaylistsRoomSubScreen.cs | 3 --- 4 files changed, 13 insertions(+), 21 deletions(-) rename osu.Game/Screens/OnlinePlay/{Multiplayer/Match/DrawableMultiplayerRoom.cs => Match/DrawableMatchRoom.cs} (89%) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/DrawableMultiplayerRoom.cs b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs similarity index 89% rename from osu.Game/Screens/OnlinePlay/Multiplayer/Match/DrawableMultiplayerRoom.cs rename to osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs index 84ae6108f6..f7b4dd6920 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/DrawableMultiplayerRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs @@ -12,9 +12,9 @@ using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Users; using osuTK; -namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match +namespace osu.Game.Screens.OnlinePlay.Match { - public class DrawableMultiplayerRoom : DrawableRoom + public class DrawableMatchRoom : DrawableRoom { public Action OnEdit; @@ -25,7 +25,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private Drawable editButton; - public DrawableMultiplayerRoom(Room room) + public DrawableMatchRoom(Room room) : base(room) { host.BindTo(room.Host); diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 9e729515d2..8dc5c0cc4a 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -19,7 +19,6 @@ using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Overlays.Mods; using osu.Game.Rulesets.Mods; -using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Match.Components; namespace osu.Game.Screens.OnlinePlay.Match @@ -64,7 +63,7 @@ namespace osu.Game.Screens.OnlinePlay.Match private readonly Room room; private ModSelectOverlay userModsSelectOverlay; - protected RoomSettingsOverlay SettingsOverlay { get; private set; } + private RoomSettingsOverlay settingsOverlay; protected RoomSubScreen(Room room) { @@ -123,7 +122,11 @@ namespace osu.Game.Screens.OnlinePlay.Match { new Drawable[] { - CreateDrawableRoom(room).With(d => d.MatchingFilter = true), + new DrawableMatchRoom(room) + { + MatchingFilter = true, + OnEdit = () => settingsOverlay.Show() + } }, null, new Drawable[] @@ -162,7 +165,7 @@ namespace osu.Game.Screens.OnlinePlay.Match } } }, - SettingsOverlay = CreateRoomSettingsOverlay().With(s => + settingsOverlay = CreateRoomSettingsOverlay().With(s => { s.State.Value = room.RoomID.Value == null ? Visibility.Visible : Visibility.Hidden; }) @@ -184,7 +187,7 @@ namespace osu.Game.Screens.OnlinePlay.Match // The main content should be hidden until the settings overlay is hidden, signaling the room is ready to be displayed. mainContent.Hide(); - SettingsOverlay.State.BindValueChanged(visibility => + settingsOverlay.State.BindValueChanged(visibility => { if (visibility.NewValue == Visibility.Hidden) mainContent.Show(); @@ -218,9 +221,9 @@ namespace osu.Game.Screens.OnlinePlay.Match return true; } - if (SettingsOverlay.State.Value == Visibility.Visible) + if (settingsOverlay.State.Value == Visibility.Visible) { - SettingsOverlay.Hide(); + settingsOverlay.Hide(); return true; } @@ -358,8 +361,6 @@ namespace osu.Game.Screens.OnlinePlay.Match track.Looping = false; } - protected abstract DrawableRoom CreateDrawableRoom(Room room); - protected abstract Drawable CreateMainContent(); protected abstract Drawable CreateFooter(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 3cbc6c36b6..73c8053c1a 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -22,7 +22,6 @@ using osu.Game.Overlays.Dialog; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.OnlinePlay.Components; -using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; @@ -110,11 +109,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer Mods.Value = client.LocalUser.Mods.Select(m => m.ToMod(ruleset)).Concat(SelectedItem.Value.RequiredMods).ToList(); } - protected override DrawableRoom CreateDrawableRoom(Room room) => new DrawableMultiplayerRoom(room) - { - OnEdit = () => SettingsOverlay.Show() - }; - protected override Drawable CreateMainContent() => new Container { RelativeSizeAxes = Axes.Both, diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 7af74e7e64..c655f49820 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -14,7 +14,6 @@ using osu.Game.Input; using osu.Game.Online.API; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Components; -using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.Play; @@ -298,8 +297,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists Exited = () => leaderboard.RefreshScores() }); - protected override DrawableRoom CreateDrawableRoom(Room room) => new DrawableRoom(room); - protected override Drawable CreateMainContent() => Empty(); protected override Drawable CreateFooter() => Empty(); From 228ad98b39ad6056eb387ed9e478b4de92db02fa Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 18 Aug 2021 15:27:23 +0900 Subject: [PATCH 1289/2442] Remove extra corner radius on DrawableRoom --- osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 4e81739dc4..b627dfbb8e 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -149,7 +149,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Height = height; Masking = true; - CornerRadius = corner_radius + SELECTION_BORDER_WIDTH / 2; + CornerRadius = corner_radius; EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, From 744b6749d18ca282ba3eb6426b950ecd5cb071e7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 18 Aug 2021 15:29:01 +0900 Subject: [PATCH 1290/2442] Resolve room settings layout issues --- .../Match/Components/RoomSettingsOverlay.cs | 3 +++ osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 12 +++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs index eec512347b..86687d7da8 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs @@ -31,6 +31,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components { RelativeSizeAxes = Axes.Both; Masking = true; + CornerRadius = 10; } [BackgroundDependencyLoader] @@ -47,12 +48,14 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components { base.PopIn(); Settings.MoveToY(0, TRANSITION_DURATION, Easing.OutQuint); + Settings.FadeIn(TRANSITION_DURATION / 2); } protected override void PopOut() { base.PopOut(); Settings.MoveToY(-1, TRANSITION_DURATION, Easing.InSine); + Settings.Delay(TRANSITION_DURATION / 2).FadeOut(TRANSITION_DURATION / 2); } public bool OnPressed(GlobalAction action) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 8dc5c0cc4a..9c7b255213 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -165,10 +165,16 @@ namespace osu.Game.Screens.OnlinePlay.Match } } }, - settingsOverlay = CreateRoomSettingsOverlay().With(s => + new Container { - s.State.Value = room.RoomID.Value == null ? Visibility.Visible : Visibility.Hidden; - }) + RelativeSizeAxes = Axes.Both, + // Resolves 1px masking errors between the settings overlay and the room panel. + Padding = new MarginPadding(-1), + Child = settingsOverlay = CreateRoomSettingsOverlay().With(s => + { + s.State.Value = room.RoomID.Value == null ? Visibility.Visible : Visibility.Hidden; + }) + } }, }, }, From 90a1be2e6158b2f2cb7bdb6533d0fa22e77056aa Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 18 Aug 2021 15:54:33 +0900 Subject: [PATCH 1291/2442] Move paddings up one level --- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 7 +- .../Multiplayer/MultiplayerMatchSubScreen.cs | 216 ++++++++---------- 2 files changed, 106 insertions(+), 117 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 9c7b255213..8e6cfe2acb 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -147,7 +147,12 @@ namespace osu.Game.Screens.OnlinePlay.Match Colour = Color4Extensions.FromHex(@"3e3a44") // This is super temporary. }, }, - CreateMainContent(), + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(10), + Child = CreateMainContent(), + }, new Container { Anchor = Anchor.BottomLeft, diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 73c8053c1a..b9bcb27d60 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -109,115 +109,99 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer Mods.Value = client.LocalUser.Mods.Select(m => m.ToMod(ruleset)).Concat(SelectedItem.Value.RequiredMods).ToList(); } - protected override Drawable CreateMainContent() => new Container + protected override Drawable CreateMainContent() => new GridContainer { RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + Content = new[] { - new Container + new Drawable[] { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding - { - Horizontal = 10, - Vertical = 10 - }, - Child = new GridContainer + new Container { RelativeSizeAxes = Axes.Both, - Content = new[] + Padding = new MarginPadding { Horizontal = 5, Vertical = 10 }, + Child = new GridContainer { - new Drawable[] + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] { - new Container + new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 400), + new Dimension(), + new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 600), + }, + Content = new[] + { + new Drawable[] { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 5, Vertical = 10 }, - Child = new GridContainer + // Main left column + new GridContainer { RelativeSizeAxes = Axes.Both, - ColumnDimensions = new[] + RowDimensions = new[] { - new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 400), - new Dimension(), - new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 600), + new Dimension(GridSizeMode.AutoSize) }, Content = new[] { + new Drawable[] { new ParticipantsListHeader() }, new Drawable[] { - // Main left column - new GridContainer + new ParticipantsList { - RelativeSizeAxes = Axes.Both, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize) - }, - Content = new[] - { - new Drawable[] { new ParticipantsListHeader() }, - new Drawable[] - { - new ParticipantsList - { - RelativeSizeAxes = Axes.Both - }, - } - } + RelativeSizeAxes = Axes.Both }, - // Spacer - null, - // Main right column - new FillFlowContainer + } + } + }, + // Spacer + null, + // Main right column + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new[] + new OverlinedHeader("Beatmap"), + new BeatmapSelectionControl { RelativeSizeAxes = Axes.X } + } + }, + UserModsSection = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Top = 10 }, + Children = new Drawable[] + { + new OverlinedHeader("Extra mods"), + new FillFlowContainer { - new FillFlowContainer + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10, 0), + Children = new Drawable[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] + new UserModSelectButton { - new OverlinedHeader("Beatmap"), - new BeatmapSelectionControl { RelativeSizeAxes = Axes.X } - } - }, - UserModsSection = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Margin = new MarginPadding { Top = 10 }, - Children = new Drawable[] + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Width = 90, + Text = "Select", + Action = ShowUserModSelect, + }, + new ModDisplay { - new OverlinedHeader("Extra mods"), - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(10, 0), - Children = new Drawable[] - { - new UserModSelectButton - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Width = 90, - Text = "Select", - Action = ShowUserModSelect, - }, - new ModDisplay - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Current = UserMods, - Scale = new Vector2(0.8f), - }, - } - } - } + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Current = UserMods, + Scale = new Vector2(0.8f), + }, } } } @@ -225,27 +209,27 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } } } - }, - new Drawable[] - { - new GridContainer - { - RelativeSizeAxes = Axes.Both, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize) - }, - Content = new[] - { - new Drawable[] { new OverlinedHeader("Chat") }, - new Drawable[] { new MatchChatDisplay { RelativeSizeAxes = Axes.Both } } - } - } } - }, + } } }, - } + new Drawable[] + { + new GridContainer + { + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize) + }, + Content = new[] + { + new Drawable[] { new OverlinedHeader("Chat") }, + new Drawable[] { new MatchChatDisplay { RelativeSizeAxes = Axes.Both } } + } + } + } + }, }; protected override Drawable CreateFooter() => new MultiplayerMatchFooter @@ -444,19 +428,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } } - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - if (client != null) - { - client.RoomUpdated -= onRoomUpdated; - client.LoadRequested -= onLoadRequested; - } - - modSettingChangeTracker?.Dispose(); - } - public void PresentBeatmap(WorkingBeatmap beatmap, RulesetInfo ruleset) { if (!this.IsCurrentScreen()) @@ -472,5 +443,18 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer this.Push(new MultiplayerMatchSongSelect(beatmap, ruleset)); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (client != null) + { + client.RoomUpdated -= onRoomUpdated; + client.LoadRequested -= onLoadRequested; + } + + modSettingChangeTracker?.Dispose(); + } } } From dc44cc0eb34783f306437bd90716c914ec7c0174 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Aug 2021 15:32:59 +0900 Subject: [PATCH 1292/2442] Update scenarios to use new `TestRunHeadlessGameHost` where feasible --- .../Collections/IO/ImportCollectionsTest.cs | 5 +++-- .../NonVisual/CustomDataDirectoryTest.cs | 15 ++++++++++++--- osu.Game/Tests/CleanRunHeadlessGameHost.cs | 4 ++-- osu.Game/Tests/Visual/OsuTestScene.cs | 2 +- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs b/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs index 8f5ebf53bd..d87ac29d75 100644 --- a/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs +++ b/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs @@ -7,6 +7,7 @@ using System.Text; using System.Threading.Tasks; using NUnit.Framework; using osu.Framework.Platform; +using osu.Framework.Testing; using osu.Game.Tests.Resources; namespace osu.Game.Tests.Collections.IO @@ -127,7 +128,7 @@ namespace osu.Game.Tests.Collections.IO [Test] public async Task TestSaveAndReload() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + using (HeadlessGameHost host = new TestRunHeadlessGameHost("TestSaveAndReload", bypassCleanup: true)) { try { @@ -148,7 +149,7 @@ namespace osu.Game.Tests.Collections.IO } } - using (HeadlessGameHost host = new HeadlessGameHost("TestSaveAndReload")) + using (HeadlessGameHost host = new TestRunHeadlessGameHost("TestSaveAndReload")) { try { diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs index 4c44e2ec72..c9f5774735 100644 --- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -6,10 +6,10 @@ using System.IO; using System.Linq; using System.Runtime.CompilerServices; using NUnit.Framework; -using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Platform; +using osu.Framework.Testing; using osu.Game.Configuration; using osu.Game.IO; @@ -278,7 +278,7 @@ namespace osu.Game.Tests.NonVisual private static string getDefaultLocationFor(string testTypeName) { - string path = Path.Combine(RuntimeInfo.StartupDirectory, "headless", testTypeName); + string path = Path.Combine(TestRunHeadlessGameHost.TemporaryTestDirectory, testTypeName); if (Directory.Exists(path)) Directory.Delete(path, true); @@ -288,7 +288,7 @@ namespace osu.Game.Tests.NonVisual private string prepareCustomPath(string suffix = "") { - string path = Path.Combine(RuntimeInfo.StartupDirectory, $"custom-path{suffix}"); + string path = Path.Combine(TestRunHeadlessGameHost.TemporaryTestDirectory, $"custom-path{suffix}"); if (Directory.Exists(path)) Directory.Delete(path, true); @@ -308,6 +308,15 @@ namespace osu.Game.Tests.NonVisual InitialStorage = new DesktopStorage(defaultStorageLocation, this); InitialStorage.DeleteDirectory(string.Empty); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + // the storage may have changed from the initial location. + // this handles cleanup of the initial location. + InitialStorage.DeleteDirectory(string.Empty); + } } } } diff --git a/osu.Game/Tests/CleanRunHeadlessGameHost.cs b/osu.Game/Tests/CleanRunHeadlessGameHost.cs index 03ab94d1da..d7ab769ac4 100644 --- a/osu.Game/Tests/CleanRunHeadlessGameHost.cs +++ b/osu.Game/Tests/CleanRunHeadlessGameHost.cs @@ -2,14 +2,14 @@ // See the LICENCE file in the repository root for full licence text. using System.Runtime.CompilerServices; -using osu.Framework.Platform; +using osu.Framework.Testing; namespace osu.Game.Tests { /// /// A headless host which cleans up before running (removing any remnants from a previous execution). /// - public class CleanRunHeadlessGameHost : HeadlessGameHost + public class CleanRunHeadlessGameHost : TestRunHeadlessGameHost { /// /// Create a new instance. diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 57e400a77e..ef1a35ffa5 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -155,7 +155,7 @@ namespace osu.Game.Tests.Visual } localStorage = - new Lazy(() => isolatedHostStorage ?? new NativeStorage(Path.Combine(RuntimeInfo.StartupDirectory, $"{GetType().Name}-{Guid.NewGuid()}"))); + new Lazy(() => isolatedHostStorage ?? new TemporaryNativeStorage($"{GetType().Name}-{Guid.NewGuid()}")); } [Resolved] From 568f1fd345fd7f51660623c90916036aa84dbdce Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 18 Aug 2021 16:09:00 +0900 Subject: [PATCH 1293/2442] Fix initial RoomId state not being handled correctly --- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 49 ++++++++++--------- .../Multiplayer/MultiplayerMatchSubScreen.cs | 4 -- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 8e6cfe2acb..ae92ab92c1 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -44,6 +44,8 @@ namespace osu.Game.Screens.OnlinePlay.Match /// protected readonly Bindable> UserMods = new Bindable>(Array.Empty()); + protected readonly IBindable RoomId = new Bindable(); + [Resolved] private MusicController music { get; set; } @@ -60,14 +62,15 @@ namespace osu.Game.Screens.OnlinePlay.Match protected IBindable BeatmapAvailability => BeatmapAvailabilityTracker.Availability; - private readonly Room room; + public readonly Room Room; private ModSelectOverlay userModsSelectOverlay; private RoomSettingsOverlay settingsOverlay; + private Drawable mainContent; protected RoomSubScreen(Room room) { - this.room = room; + Room = room; Padding = new MarginPadding { Top = Header.HEIGHT }; @@ -75,6 +78,8 @@ namespace osu.Game.Screens.OnlinePlay.Match { SelectedItem = { BindTarget = SelectedItem } }; + + RoomId.BindTo(room.RoomID); } [BackgroundDependencyLoader] @@ -82,8 +87,6 @@ namespace osu.Game.Screens.OnlinePlay.Match { sampleStart = audio.Samples.Get(@"SongSelect/confirm-selection"); - Drawable mainContent; - InternalChildren = new Drawable[] { BeatmapAvailabilityTracker, @@ -122,7 +125,7 @@ namespace osu.Game.Screens.OnlinePlay.Match { new Drawable[] { - new DrawableMatchRoom(room) + new DrawableMatchRoom(Room) { MatchingFilter = true, OnEdit = () => settingsOverlay.Show() @@ -175,10 +178,7 @@ namespace osu.Game.Screens.OnlinePlay.Match RelativeSizeAxes = Axes.Both, // Resolves 1px masking errors between the settings overlay and the room panel. Padding = new MarginPadding(-1), - Child = settingsOverlay = CreateRoomSettingsOverlay().With(s => - { - s.State.Value = room.RoomID.Value == null ? Visibility.Visible : Visibility.Hidden; - }) + Child = settingsOverlay = CreateRoomSettingsOverlay() } }, }, @@ -191,25 +191,28 @@ namespace osu.Game.Screens.OnlinePlay.Match } } }; - - if (room.RoomID.Value == null) - { - // A new room is being created. - // The main content should be hidden until the settings overlay is hidden, signaling the room is ready to be displayed. - mainContent.Hide(); - - settingsOverlay.State.BindValueChanged(visibility => - { - if (visibility.NewValue == Visibility.Hidden) - mainContent.Show(); - }, true); - } } protected override void LoadComplete() { base.LoadComplete(); + RoomId.BindValueChanged(id => + { + if (id.NewValue == null) + { + // A new room is being created. + // The main content should be hidden until the settings overlay is hidden, signaling the room is ready to be displayed. + mainContent.Hide(); + settingsOverlay.Show(); + } + else + { + mainContent.Show(); + settingsOverlay.Hide(); + } + }, true); + SelectedItem.BindValueChanged(_ => Scheduler.AddOnce(selectedItemChanged)); managerUpdated = beatmapManager.ItemUpdated.GetBoundCopy(); @@ -220,7 +223,7 @@ namespace osu.Game.Screens.OnlinePlay.Match public override bool OnBackButton() { - if (room.RoomID.Value == null) + if (Room.RoomID.Value == null) { // room has not been created yet; exit immediately. return base.OnBackButton(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index b9bcb27d60..f8e5d7d901 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -42,8 +42,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public override string ShortTitle => "room"; - public readonly Room Room; - [Resolved] private MultiplayerClient client { get; set; } @@ -61,8 +59,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public MultiplayerMatchSubScreen(Room room) : base(room) { - Room = room; - Title = room.RoomID.Value == null ? "New room" : room.Name.Value; Activity.Value = new UserActivity.InLobby(room); } From 66e11fe75eb0424af82fc03c384d372df7304fc2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 18 Aug 2021 16:09:15 +0900 Subject: [PATCH 1294/2442] Initial integration of playlists with the new structure --- .../Playlists/PlaylistsRoomSubScreen.cs | 390 ++++++++---------- 1 file changed, 166 insertions(+), 224 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index c655f49820..bf0107d3c3 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -30,18 +30,13 @@ namespace osu.Game.Screens.OnlinePlay.Playlists public override string ShortTitle => "playlist"; - [Resolved(typeof(Room), nameof(Room.RoomID))] - private Bindable roomId { get; set; } - - [Resolved(typeof(Room), nameof(Room.Playlist))] - private BindableList playlist { get; set; } + [Resolved] + private IAPIProvider api { get; set; } private readonly IBindable isIdle = new BindableBool(); - private RoomSettingsOverlay settingsOverlay; private MatchLeaderboard leaderboard; private OverlinedHeader participantsHeader; - private GridContainer mainContent; private SelectionPollingComponent selectionPollingComponent; public PlaylistsRoomSubScreen(Room room) @@ -57,231 +52,21 @@ namespace osu.Game.Screens.OnlinePlay.Playlists if (idleTracker != null) isIdle.BindTo(idleTracker.IsIdle); - AddRangeInternal(new Drawable[] - { - selectionPollingComponent = new SelectionPollingComponent(), - mainContent = new GridContainer - { - RelativeSizeAxes = Axes.Both, - Content = new[] - { - new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding - { - Horizontal = 105, - Vertical = 20 - }, - Child = new GridContainer - { - RelativeSizeAxes = Axes.Both, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.AutoSize), - new Dimension(), - }, - Content = new[] - { - new Drawable[] { new Match.Components.Header() }, - new Drawable[] - { - participantsHeader = new OverlinedHeader("Participants") - { - ShowLine = false - } - }, - new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Margin = new MarginPadding { Top = 5 }, - Child = new ParticipantsDisplay(Direction.Horizontal) - { - Details = { BindTarget = participantsHeader.Details } - } - } - }, - new Drawable[] - { - new GridContainer - { - RelativeSizeAxes = Axes.Both, - Content = new[] - { - new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Right = 5 }, - Child = new GridContainer - { - RelativeSizeAxes = Axes.Both, - Content = new[] - { - new Drawable[] { new OverlinedPlaylistHeader(), }, - new Drawable[] - { - new DrawableRoomPlaylistWithResults - { - RelativeSizeAxes = Axes.Both, - Items = { BindTarget = playlist }, - SelectedItem = { BindTarget = SelectedItem }, - RequestShowResults = item => - { - Debug.Assert(roomId.Value != null); - ParentScreen?.Push(new PlaylistsResultsScreen(null, roomId.Value.Value, item, false)); - } - } - }, - }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - new Dimension(), - } - } - }, - null, - new GridContainer - { - RelativeSizeAxes = Axes.Both, - Content = new[] - { - new[] - { - UserModsSection = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Margin = new MarginPadding { Bottom = 10 }, - Children = new Drawable[] - { - new OverlinedHeader("Extra mods"), - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(10, 0), - Children = new Drawable[] - { - new UserModSelectButton - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Width = 90, - Text = "Select", - Action = ShowUserModSelect, - }, - new ModDisplay - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Current = UserMods, - Scale = new Vector2(0.8f), - }, - } - } - } - }, - }, - new Drawable[] - { - new OverlinedHeader("Leaderboard") - }, - new Drawable[] { leaderboard = new MatchLeaderboard { RelativeSizeAxes = Axes.Both }, }, - new Drawable[] { new OverlinedHeader("Chat"), }, - new Drawable[] { new MatchChatDisplay { RelativeSizeAxes = Axes.Both } } - }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.AutoSize), - new Dimension(), - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.Relative, size: 0.4f, minSize: 120), - } - }, - null - }, - }, - ColumnDimensions = new[] - { - new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 400), - new Dimension(), - new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 600), - new Dimension(), - } - } - } - }, - } - } - }, - new Drawable[] - { - new Footer { OnStart = StartPlay } - } - }, - RowDimensions = new[] - { - new Dimension(), - new Dimension(GridSizeMode.AutoSize), - } - }, - settingsOverlay = new PlaylistsRoomSettingsOverlay - { - RelativeSizeAxes = Axes.Both, - EditPlaylist = () => - { - if (this.IsCurrentScreen()) - this.Push(new PlaylistsSongSelect()); - }, - State = { Value = roomId.Value == null ? Visibility.Visible : Visibility.Hidden } - } - }); - - if (roomId.Value == null) - { - // A new room is being created. - // The main content should be hidden until the settings overlay is hidden, signaling the room is ready to be displayed. - mainContent.Hide(); - - settingsOverlay.State.BindValueChanged(visibility => - { - if (visibility.NewValue == Visibility.Hidden) - mainContent.Show(); - }, true); - } + AddInternal(selectionPollingComponent = new SelectionPollingComponent()); } - [Resolved] - private IAPIProvider api { get; set; } - protected override void LoadComplete() { base.LoadComplete(); isIdle.BindValueChanged(_ => updatePollingRate(), true); - - roomId.BindValueChanged(id => + RoomId.BindValueChanged(id => { - if (id.NewValue == null) - settingsOverlay.Show(); - else + if (id.NewValue != null) { - settingsOverlay.Hide(); - // Set the first playlist item. // This is scheduled since updating the room and playlist may happen in an arbitrary order (via Room.CopyFrom()). - Schedule(() => SelectedItem.Value = playlist.FirstOrDefault()); + Schedule(() => SelectedItem.Value = Room.Playlist.FirstOrDefault()); } }, true); } @@ -297,10 +82,167 @@ namespace osu.Game.Screens.OnlinePlay.Playlists Exited = () => leaderboard.RefreshScores() }); - protected override Drawable CreateMainContent() => Empty(); + protected override Drawable CreateMainContent() => new GridContainer + { + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.AutoSize), + new Dimension(), + }, + Content = new[] + { + new Drawable[] { new Match.Components.Header() }, + new Drawable[] + { + participantsHeader = new OverlinedHeader("Participants") + { + ShowLine = false + } + }, + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Top = 5 }, + Child = new ParticipantsDisplay(Direction.Horizontal) + { + Details = { BindTarget = participantsHeader.Details } + } + } + }, + new Drawable[] + { + new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Right = 5 }, + Child = new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] { new OverlinedPlaylistHeader(), }, + new Drawable[] + { + new DrawableRoomPlaylistWithResults + { + RelativeSizeAxes = Axes.Both, + Items = { BindTarget = Room.Playlist }, + SelectedItem = { BindTarget = SelectedItem }, + RequestShowResults = item => + { + Debug.Assert(RoomId.Value != null); + ParentScreen?.Push(new PlaylistsResultsScreen(null, RoomId.Value.Value, item, false)); + } + } + }, + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(), + } + } + }, + null, + new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new[] + { + UserModsSection = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Bottom = 10 }, + Children = new Drawable[] + { + new OverlinedHeader("Extra mods"), + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10, 0), + Children = new Drawable[] + { + new UserModSelectButton + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Width = 90, + Text = "Select", + Action = ShowUserModSelect, + }, + new ModDisplay + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Current = UserMods, + Scale = new Vector2(0.8f), + }, + } + } + } + }, + }, + new Drawable[] + { + new OverlinedHeader("Leaderboard") + }, + new Drawable[] { leaderboard = new MatchLeaderboard { RelativeSizeAxes = Axes.Both }, }, + new Drawable[] { new OverlinedHeader("Chat"), }, + new Drawable[] { new MatchChatDisplay { RelativeSizeAxes = Axes.Both } } + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.AutoSize), + new Dimension(), + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.Relative, size: 0.4f, minSize: 120), + } + }, + null + }, + }, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 400), + new Dimension(), + new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 600), + new Dimension(), + } + } + } + }, + }; - protected override Drawable CreateFooter() => Empty(); + protected override Drawable CreateFooter() => new Footer + { + OnStart = StartPlay + }; - protected override RoomSettingsOverlay CreateRoomSettingsOverlay() => new PlaylistsRoomSettingsOverlay(); + protected override RoomSettingsOverlay CreateRoomSettingsOverlay() => new PlaylistsRoomSettingsOverlay + { + EditPlaylist = () => + { + if (this.IsCurrentScreen()) + this.Push(new PlaylistsSongSelect()); + }, + }; } } From 87a5922f0eef0eb69becbdf3a1ea1b12d3ceb501 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 18 Aug 2021 16:26:45 +0900 Subject: [PATCH 1295/2442] Remove participants from playlists screen --- .../Playlists/PlaylistsRoomSubScreen.cs | 193 +++++++----------- 1 file changed, 76 insertions(+), 117 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index bf0107d3c3..d799f09972 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -36,7 +36,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private readonly IBindable isIdle = new BindableBool(); private MatchLeaderboard leaderboard; - private OverlinedHeader participantsHeader; private SelectionPollingComponent selectionPollingComponent; public PlaylistsRoomSubScreen(Room room) @@ -85,150 +84,110 @@ namespace osu.Game.Screens.OnlinePlay.Playlists protected override Drawable CreateMainContent() => new GridContainer { RelativeSizeAxes = Axes.Both, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.AutoSize), - new Dimension(), - }, Content = new[] { - new Drawable[] { new Match.Components.Header() }, - new Drawable[] - { - participantsHeader = new OverlinedHeader("Participants") - { - ShowLine = false - } - }, new Drawable[] { new Container { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Margin = new MarginPadding { Top = 5 }, - Child = new ParticipantsDisplay(Direction.Horizontal) + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Right = 5 }, + Child = new GridContainer { - Details = { BindTarget = participantsHeader.Details } + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] { new OverlinedPlaylistHeader(), }, + new Drawable[] + { + new DrawableRoomPlaylistWithResults + { + RelativeSizeAxes = Axes.Both, + Items = { BindTarget = Room.Playlist }, + SelectedItem = { BindTarget = SelectedItem }, + RequestShowResults = item => + { + Debug.Assert(RoomId.Value != null); + ParentScreen?.Push(new PlaylistsResultsScreen(null, RoomId.Value.Value, item, false)); + } + } + }, + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(), + } } - } - }, - new Drawable[] - { + }, + null, new GridContainer { RelativeSizeAxes = Axes.Both, Content = new[] { - new Drawable[] + new[] { - new Container + UserModsSection = new FillFlowContainer { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Right = 5 }, - Child = new GridContainer + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Bottom = 10 }, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Content = new[] + new OverlinedHeader("Extra mods"), + new FillFlowContainer { - new Drawable[] { new OverlinedPlaylistHeader(), }, - new Drawable[] + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10, 0), + Children = new Drawable[] { - new DrawableRoomPlaylistWithResults + new UserModSelectButton { - RelativeSizeAxes = Axes.Both, - Items = { BindTarget = Room.Playlist }, - SelectedItem = { BindTarget = SelectedItem }, - RequestShowResults = item => - { - Debug.Assert(RoomId.Value != null); - ParentScreen?.Push(new PlaylistsResultsScreen(null, RoomId.Value.Value, item, false)); - } - } - }, - }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - new Dimension(), + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Width = 90, + Text = "Select", + Action = ShowUserModSelect, + }, + new ModDisplay + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Current = UserMods, + Scale = new Vector2(0.8f), + }, + } } } }, - null, - new GridContainer - { - RelativeSizeAxes = Axes.Both, - Content = new[] - { - new[] - { - UserModsSection = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Margin = new MarginPadding { Bottom = 10 }, - Children = new Drawable[] - { - new OverlinedHeader("Extra mods"), - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(10, 0), - Children = new Drawable[] - { - new UserModSelectButton - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Width = 90, - Text = "Select", - Action = ShowUserModSelect, - }, - new ModDisplay - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Current = UserMods, - Scale = new Vector2(0.8f), - }, - } - } - } - }, - }, - new Drawable[] - { - new OverlinedHeader("Leaderboard") - }, - new Drawable[] { leaderboard = new MatchLeaderboard { RelativeSizeAxes = Axes.Both }, }, - new Drawable[] { new OverlinedHeader("Chat"), }, - new Drawable[] { new MatchChatDisplay { RelativeSizeAxes = Axes.Both } } - }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.AutoSize), - new Dimension(), - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.Relative, size: 0.4f, minSize: 120), - } - }, - null }, + new Drawable[] + { + new OverlinedHeader("Leaderboard") + }, + new Drawable[] { leaderboard = new MatchLeaderboard { RelativeSizeAxes = Axes.Both }, }, + new Drawable[] { new OverlinedHeader("Chat"), }, + new Drawable[] { new MatchChatDisplay { RelativeSizeAxes = Axes.Both } } }, - ColumnDimensions = new[] + RowDimensions = new[] { - new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 400), - new Dimension(), - new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 600), + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.AutoSize), new Dimension(), + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.Relative, size: 0.4f, minSize: 120), } - } - } + }, + }, }, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 400), + new Dimension(), + new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 600), + } }; protected override Drawable CreateFooter() => new Footer From 2758a83d5556cbea8e4d7b95977b053b8dcb6702 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Aug 2021 16:23:02 +0900 Subject: [PATCH 1296/2442] Fix `TestSettingsMigration`'s usage of `RecycleLocalStorage` --- .../Input/ConfineMouseTrackerTest.cs | 2 +- .../Menus/TestSceneMusicActionHandling.cs | 1 - .../Visual/Menus/TestSceneSideOverlays.cs | 1 - .../Visual/Navigation/OsuGameTestScene.cs | 9 +++++- .../Visual/Navigation/TestSceneOsuGame.cs | 32 ++++++++++++------- .../Navigation/TestSettingsMigration.cs | 7 ++-- .../TestSceneBeatmapRecommendations.cs | 1 - osu.Game/Tests/Visual/OsuTestScene.cs | 7 ++-- 8 files changed, 37 insertions(+), 23 deletions(-) diff --git a/osu.Game.Tests/Input/ConfineMouseTrackerTest.cs b/osu.Game.Tests/Input/ConfineMouseTrackerTest.cs index 27cece42e8..b612899d79 100644 --- a/osu.Game.Tests/Input/ConfineMouseTrackerTest.cs +++ b/osu.Game.Tests/Input/ConfineMouseTrackerTest.cs @@ -8,7 +8,7 @@ using osu.Framework.Input; using osu.Framework.Testing; using osu.Game.Configuration; using osu.Game.Input; -using osu.Game.Tests.Visual.Navigation; +using osu.Game.Tests.Visual; namespace osu.Game.Tests.Input { diff --git a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs index aaf3323432..9037338e23 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs @@ -10,7 +10,6 @@ using osu.Game.Database; using osu.Game.Input.Bindings; using osu.Game.Overlays; using osu.Game.Tests.Resources; -using osu.Game.Tests.Visual.Navigation; namespace osu.Game.Tests.Visual.Menus { diff --git a/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs b/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs index e58f85b0b3..5230e026bc 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs @@ -4,7 +4,6 @@ using NUnit.Framework; using osu.Framework.Testing; using osu.Game.Overlays; -using osu.Game.Tests.Visual.Navigation; namespace osu.Game.Tests.Visual.Menus { diff --git a/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs b/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs index c9a1471e41..e392301568 100644 --- a/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs +++ b/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs @@ -62,7 +62,7 @@ namespace osu.Game.Tests.Visual.Navigation Game.Dispose(); } - RecycleLocalStorage(); + RecycleLocalStorage(false); CreateGame(); }); @@ -73,6 +73,13 @@ namespace osu.Game.Tests.Visual.Navigation ConfirmAtMainMenu(); } + [TearDownSteps] + public void TearDownSteps() + { + AddStep("exit game", () => Game.Exit()); + AddUntilStep("wait for game exit", () => Game.Parent == null); + } + protected void CreateGame() { Game = new TestOsuGame(LocalStorage, API); diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs index 26641214b1..48e68b03fb 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; using osu.Framework.Platform; +using osu.Framework.Testing; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Configuration; @@ -88,25 +89,32 @@ namespace osu.Game.Tests.Visual.Navigation [Resolved] private OsuGameBase gameBase { get; set; } - [BackgroundDependencyLoader] - private void load(GameHost host) - { - game = new OsuGame(); - game.SetHost(host); + [Resolved] + private GameHost host { get; set; } - Children = new Drawable[] + [SetUpSteps] + public void SetUpSteps() + { + AddStep("create game", () => { - new Box + game = new OsuGame(); + game.SetHost(host); + + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - }, - game - }; + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + game + }; + }); AddUntilStep("wait for load", () => game.IsLoaded); } + [Test] public void TestNullRulesetHandled() { diff --git a/osu.Game.Tests/Visual/Navigation/TestSettingsMigration.cs b/osu.Game.Tests/Visual/Navigation/TestSettingsMigration.cs index c1c968e862..7e3d8290be 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSettingsMigration.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSettingsMigration.cs @@ -9,9 +9,12 @@ namespace osu.Game.Tests.Visual.Navigation { public class TestSettingsMigration : OsuGameTestScene { - public override void RecycleLocalStorage() + public override void RecycleLocalStorage(bool isDisposing) { - base.RecycleLocalStorage(); + base.RecycleLocalStorage(isDisposing); + + if (isDisposing) + return; using (var config = new OsuConfigManager(LocalStorage)) { diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs index 5e2d5eba5d..53cb628bb3 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -14,7 +14,6 @@ using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Taiko; -using osu.Game.Tests.Visual.Navigation; using osu.Game.Users; namespace osu.Game.Tests.Visual.SongSelect diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index ef1a35ffa5..ef9181c8a6 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -7,7 +7,6 @@ using System.IO; using System.Linq; using System.Threading.Tasks; using JetBrains.Annotations; -using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Track; @@ -100,7 +99,7 @@ namespace osu.Game.Tests.Visual return factory; }); - RecycleLocalStorage(); + RecycleLocalStorage(false); var baseDependencies = base.CreateChildDependencies(parent); @@ -140,7 +139,7 @@ namespace osu.Game.Tests.Visual protected virtual bool UseFreshStoragePerRun => false; - public virtual void RecycleLocalStorage() + public virtual void RecycleLocalStorage(bool isDisposing) { if (localStorage?.IsValueCreated == true) { @@ -199,7 +198,7 @@ namespace osu.Game.Tests.Visual if (contextFactory?.IsValueCreated == true) contextFactory.Value.ResetDatabase(); - RecycleLocalStorage(); + RecycleLocalStorage(true); } protected override ITestSceneTestRunner CreateRunner() => new OsuTestSceneTestRunner(); From 4725b802b017c19ecb31c9526a49e544f33377be Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Aug 2021 16:38:54 +0900 Subject: [PATCH 1297/2442] Share `OsuGameTestScene` with implementations across template projects --- .../TestSceneOsuGame.cs | 23 +----------- .../TestSceneOsuGame.cs | 23 +----------- .../TestSceneOsuGame.cs | 23 +----------- .../TestSceneOsuGame.cs | 23 +----------- .../Visual/Navigation/TestSceneOsuGame.cs | 37 +------------------ .../Tests/Visual}/OsuGameTestScene.cs | 7 +--- 6 files changed, 7 insertions(+), 129 deletions(-) rename {osu.Game.Tests/Visual/Navigation => osu.Game/Tests/Visual}/OsuGameTestScene.cs (93%) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/TestSceneOsuGame.cs b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/TestSceneOsuGame.cs index 9c512a01ea..fb63f69f72 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/TestSceneOsuGame.cs +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/TestSceneOsuGame.cs @@ -1,32 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Platform; using osu.Game.Tests.Visual; -using osuTK.Graphics; namespace osu.Game.Rulesets.EmptyFreeform.Tests { - public class TestSceneOsuGame : OsuTestScene + public class TestSceneOsuGame : OsuGameTestScene { - [BackgroundDependencyLoader] - private void load(GameHost host, OsuGameBase gameBase) - { - OsuGame game = new OsuGame(); - game.SetHost(host); - - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - }, - game - }; - } } } diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs index 270d906b01..a99a400afa 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs @@ -1,32 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Platform; using osu.Game.Tests.Visual; -using osuTK.Graphics; namespace osu.Game.Rulesets.Pippidon.Tests { - public class TestSceneOsuGame : OsuTestScene + public class TestSceneOsuGame : OsuGameTestScene { - [BackgroundDependencyLoader] - private void load(GameHost host, OsuGameBase gameBase) - { - OsuGame game = new OsuGame(); - game.SetHost(host); - - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - }, - game - }; - } } } diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/TestSceneOsuGame.cs b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/TestSceneOsuGame.cs index aed6abb6bf..debdc14b57 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/TestSceneOsuGame.cs +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/TestSceneOsuGame.cs @@ -1,32 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Platform; using osu.Game.Tests.Visual; -using osuTK.Graphics; namespace osu.Game.Rulesets.EmptyScrolling.Tests { - public class TestSceneOsuGame : OsuTestScene + public class TestSceneOsuGame : OsuGameTestScene { - [BackgroundDependencyLoader] - private void load(GameHost host, OsuGameBase gameBase) - { - OsuGame game = new OsuGame(); - game.SetHost(host); - - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - }, - game - }; - } } } diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs index 270d906b01..a99a400afa 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs @@ -1,32 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Platform; using osu.Game.Tests.Visual; -using osuTK.Graphics; namespace osu.Game.Rulesets.Pippidon.Tests { - public class TestSceneOsuGame : OsuTestScene + public class TestSceneOsuGame : OsuGameTestScene { - [BackgroundDependencyLoader] - private void load(GameHost host, OsuGameBase gameBase) - { - OsuGame game = new OsuGame(); - game.SetHost(host); - - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - }, - game - }; - } } } diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs index 48e68b03fb..d3f1a852d1 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs @@ -6,11 +6,7 @@ using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; -using osu.Framework.Platform; -using osu.Framework.Testing; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Configuration; @@ -28,12 +24,11 @@ using osu.Game.Scoring; using osu.Game.Screens.Menu; using osu.Game.Skinning; using osu.Game.Utils; -using osuTK.Graphics; namespace osu.Game.Tests.Visual.Navigation { [TestFixture] - public class TestSceneOsuGame : OsuTestScene + public class TestSceneOsuGame : OsuGameTestScene { private IReadOnlyList requiredGameDependencies => new[] { @@ -84,37 +79,9 @@ namespace osu.Game.Tests.Visual.Navigation typeof(PreviewTrackManager), }; - private OsuGame game; - [Resolved] private OsuGameBase gameBase { get; set; } - [Resolved] - private GameHost host { get; set; } - - [SetUpSteps] - public void SetUpSteps() - { - AddStep("create game", () => - { - game = new OsuGame(); - game.SetHost(host); - - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - }, - game - }; - }); - - AddUntilStep("wait for load", () => game.IsLoaded); - } - - [Test] public void TestNullRulesetHandled() { @@ -150,7 +117,7 @@ namespace osu.Game.Tests.Visual.Navigation { foreach (var type in requiredGameDependencies) { - if (game.Dependencies.Get(type) == null) + if (Game.Dependencies.Get(type) == null) throw new InvalidOperationException($"{type} has not been cached"); } diff --git a/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs b/osu.Game/Tests/Visual/OsuGameTestScene.cs similarity index 93% rename from osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs rename to osu.Game/Tests/Visual/OsuGameTestScene.cs index e392301568..f38aaa9358 100644 --- a/osu.Game.Tests/Visual/Navigation/OsuGameTestScene.cs +++ b/osu.Game/Tests/Visual/OsuGameTestScene.cs @@ -22,9 +22,8 @@ using osu.Game.Scoring; using osu.Game.Screens; using osu.Game.Screens.Menu; using osuTK.Graphics; -using IntroSequence = osu.Game.Configuration.IntroSequence; -namespace osu.Game.Tests.Visual.Navigation +namespace osu.Game.Tests.Visual { /// /// A scene which tests full game flow. @@ -85,10 +84,6 @@ namespace osu.Game.Tests.Visual.Navigation Game = new TestOsuGame(LocalStorage, API); Game.SetHost(host); - // todo: this can be removed once we can run audio tracks without a device present - // see https://github.com/ppy/osu/issues/1302 - Game.LocalConfig.SetValue(OsuSetting.IntroSequence, IntroSequence.Circles); - Add(Game); } From 6dc96fdb830b78dc11e539ebeb24b20e7619b615 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 18 Aug 2021 16:35:18 +0900 Subject: [PATCH 1298/2442] Disable edit button in playlists --- .../TestScenePlaylistsRoomSubScreen.cs | 7 +++-- .../OnlinePlay/Match/DrawableMatchRoom.cs | 26 +++++++++++++------ .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 11 ++++++-- .../Playlists/PlaylistsRoomSubScreen.cs | 2 +- 4 files changed, 31 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs index ee93e728ac..b8df90be2b 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs @@ -19,7 +19,6 @@ using osu.Game.Screens.OnlinePlay.Playlists; using osu.Game.Screens.Play; using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Visual.OnlinePlay; -using osu.Game.Users; using osuTK.Input; namespace osu.Game.Tests.Visual.Playlists @@ -66,7 +65,7 @@ namespace osu.Game.Tests.Visual.Playlists { SelectedRoom.Value.RoomID.Value = 1; SelectedRoom.Value.Name.Value = "my awesome room"; - SelectedRoom.Value.Host.Value = new User { Id = 2, Username = "peppy" }; + SelectedRoom.Value.Host.Value = API.LocalUser.Value; SelectedRoom.Value.RecentParticipants.Add(SelectedRoom.Value.Host.Value); SelectedRoom.Value.EndDate.Value = DateTimeOffset.Now.AddMinutes(5); SelectedRoom.Value.Playlist.Add(new PlaylistItem @@ -86,7 +85,7 @@ namespace osu.Game.Tests.Visual.Playlists AddStep("set room properties", () => { SelectedRoom.Value.Name.Value = "my awesome room"; - SelectedRoom.Value.Host.Value = new User { Id = 2, Username = "peppy" }; + SelectedRoom.Value.Host.Value = API.LocalUser.Value; SelectedRoom.Value.Playlist.Add(new PlaylistItem { Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, @@ -137,7 +136,7 @@ namespace osu.Game.Tests.Visual.Playlists AddStep("load room", () => { SelectedRoom.Value.Name.Value = "my awesome room"; - SelectedRoom.Value.Host.Value = new User { Id = 2, Username = "peppy" }; + SelectedRoom.Value.Host.Value = API.LocalUser.Value; SelectedRoom.Value.Playlist.Add(new PlaylistItem { Beatmap = { Value = importedSet.Beatmaps[0] }, diff --git a/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs index f7b4dd6920..ead219bee2 100644 --- a/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -22,31 +23,40 @@ namespace osu.Game.Screens.OnlinePlay.Match private IAPIProvider api { get; set; } private readonly IBindable host = new Bindable(); + private readonly bool allowEdit; + [CanBeNull] private Drawable editButton; - public DrawableMatchRoom(Room room) + public DrawableMatchRoom(Room room, bool allowEdit = true) : base(room) { + this.allowEdit = allowEdit; + host.BindTo(room.Host); } [BackgroundDependencyLoader] private void load() { - ButtonsContainer.Add(editButton = new PurpleTriangleButton + if (allowEdit) { - RelativeSizeAxes = Axes.Y, - Size = new Vector2(100, 1), - Text = "Edit", - Action = () => OnEdit?.Invoke() - }); + ButtonsContainer.Add(editButton = new PurpleTriangleButton + { + RelativeSizeAxes = Axes.Y, + Size = new Vector2(100, 1), + Text = "Edit", + Action = () => OnEdit?.Invoke() + }); + } } protected override void LoadComplete() { base.LoadComplete(); - host.BindValueChanged(h => editButton.Alpha = h.NewValue?.Equals(api.LocalUser.Value) == true ? 1 : 0, true); + + if (editButton != null) + host.BindValueChanged(h => editButton.Alpha = h.NewValue?.Equals(api.LocalUser.Value) == true ? 1 : 0, true); } protected override bool ShouldBeConsideredForInput(Drawable child) => true; diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index ae92ab92c1..e95b4b221e 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -63,14 +63,21 @@ namespace osu.Game.Screens.OnlinePlay.Match protected IBindable BeatmapAvailability => BeatmapAvailabilityTracker.Availability; public readonly Room Room; + private readonly bool allowEdit; private ModSelectOverlay userModsSelectOverlay; private RoomSettingsOverlay settingsOverlay; private Drawable mainContent; - protected RoomSubScreen(Room room) + /// + /// Creates a new . + /// + /// The . + /// Whether to allow editing room settings post-creation. + protected RoomSubScreen(Room room, bool allowEdit = true) { Room = room; + this.allowEdit = allowEdit; Padding = new MarginPadding { Top = Header.HEIGHT }; @@ -125,7 +132,7 @@ namespace osu.Game.Screens.OnlinePlay.Match { new Drawable[] { - new DrawableMatchRoom(Room) + new DrawableMatchRoom(Room, allowEdit) { MatchingFilter = true, OnEdit = () => settingsOverlay.Show() diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index d799f09972..9855d1cb95 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -39,7 +39,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private SelectionPollingComponent selectionPollingComponent; public PlaylistsRoomSubScreen(Room room) - : base(room) + : base(room, false) // Editing is temporarily not allowed. { Title = room.RoomID.Value == null ? "New playlist" : room.Name.Value; Activity.Value = new UserActivity.InLobby(room); From 5faf2df9b459bd00d669ff13db6d1f93a412a8d4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 18 Aug 2021 16:42:49 +0900 Subject: [PATCH 1299/2442] Revert unintentional change --- .../Visual/Multiplayer/TestSceneMultiplayer.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 035bdbea1c..e618b28f40 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -83,19 +83,6 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("load multiplayer", () => LoadScreen(multiplayerScreen)); AddUntilStep("wait for multiplayer to load", () => multiplayerScreen.IsLoaded); AddUntilStep("wait for lounge to load", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); - - createRoom(() => new Room - { - Name = { Value = "Test Room" }, - Playlist = - { - new PlaylistItem - { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo }, - Ruleset = { Value = new OsuRuleset().RulesetInfo }, - } - } - }); } [Test] From 74d6c2652023e2f603c053ded54bac6b768aa551 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 18 Aug 2021 11:03:35 +0300 Subject: [PATCH 1300/2442] Refactor star rating display layout with flexibility in mind --- .../TestSceneStarRatingDisplay.cs | 11 ++-- .../Beatmaps/Drawables/StarRatingDisplay.cs | 63 ++++++++++++++----- 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs index a8bc5664f3..7883049df2 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs @@ -8,17 +8,15 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; -using osu.Game.Graphics.Containers; using osuTK; namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneStarRatingDisplay : OsuTestScene { - [TestCase(52f, 20f)] - [TestCase(52f, 16f)] - [TestCase(50f, 14f)] - public void TestDisplay(float width, float height) + [TestCase(StarRatingDisplaySize.Regular)] + [TestCase(StarRatingDisplaySize.Small)] + public void TestDisplay(StarRatingDisplaySize size) { AddStep("load displays", () => { @@ -36,11 +34,10 @@ namespace osu.Game.Tests.Visual.UserInterface AutoSizeAxes = Axes.Both, Spacing = new Vector2(2f), Direction = FillDirection.Vertical, - ChildrenEnumerable = Enumerable.Range(0, 10).Select(j => new StarRatingDisplay(new StarDifficulty(i * (i >= 11 ? 25f : 1f) + j * 0.1f, 0)) + ChildrenEnumerable = Enumerable.Range(0, 10).Select(j => new StarRatingDisplay(new StarDifficulty(i * (i >= 11 ? 25f : 1f) + j * 0.1f, 0), size) { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(width, height), }), }) }; diff --git a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs index 2c40aebbe1..ed2fee23d5 100644 --- a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs +++ b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs @@ -44,40 +44,63 @@ namespace osu.Game.Beatmaps.Drawables /// Creates a new using an already computed . /// /// The already computed to display. - public StarRatingDisplay(StarDifficulty starDifficulty) + /// The size of the star rating display. + public StarRatingDisplay(StarDifficulty starDifficulty, StarRatingDisplaySize size = StarRatingDisplaySize.Regular) { Current.Value = starDifficulty; - Size = new Vector2(52f, 20f); + AutoSizeAxes = Axes.Both; InternalChild = new CircularContainer { Masking = true, - RelativeSizeAxes = Axes.Both, + AutoSizeAxes = Axes.Both, Children = new Drawable[] { background = new Box { RelativeSizeAxes = Axes.Both, }, - starIcon = new SpriteIcon + new GridContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Margin = new MarginPadding { Right = 30f }, - Icon = FontAwesome.Solid.Star, - Size = new Vector2(8f), + AutoSizeAxes = Axes.Both, + Margin = size == StarRatingDisplaySize.Small + ? new MarginPadding { Horizontal = 7f } + : new MarginPadding { Horizontal = 8f, Vertical = 2f }, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.Absolute, 3f), + new Dimension(GridSizeMode.AutoSize, minSize: 25f), + }, + RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }, + Content = new[] + { + new[] + { + starIcon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.Solid.Star, + Size = new Vector2(8f), + }, + Empty(), + starsText = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding { Bottom = 1.5f }, + // todo: this should be size: 12f, but to match up with the design, it needs to be 14.4f + // see https://github.com/ppy/osu-framework/issues/3271. + Font = OsuFont.Torus.With(size: 14.4f, weight: FontWeight.Bold), + Shadow = false, + } + } + } }, - starsText = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Margin = new MarginPadding { Left = 10f, Bottom = 2f }, - // todo: this should be size: 12f, but to match up with the design, it needs to be 14.4f - // see https://github.com/ppy/osu-framework/issues/3271. - Font = OsuFont.Torus.With(size: 14.4f, weight: FontWeight.Bold), - Shadow = false, - } } }; } @@ -97,4 +120,10 @@ namespace osu.Game.Beatmaps.Drawables }, true); } } + + public enum StarRatingDisplaySize + { + Small, + Regular, + } } From 9220d17202fb032110d7804a29ae109e5520692f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 18 Aug 2021 17:28:20 +0900 Subject: [PATCH 1301/2442] Adjust paddings/spacings --- .../Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs | 2 +- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 2 +- .../OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs index 86687d7da8..80a22880d4 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs @@ -17,7 +17,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components public abstract class RoomSettingsOverlay : FocusedOverlayContainer, IKeyBindingHandler { protected const float TRANSITION_DURATION = 350; - protected const float FIELD_PADDING = 45; + protected const float FIELD_PADDING = 25; protected OnlinePlayComposite Settings { get; set; } diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index e95b4b221e..e1412c894c 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -160,7 +160,7 @@ namespace osu.Game.Screens.OnlinePlay.Match new Container { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding(10), + Padding = new MarginPadding(20), Child = CreateMainContent(), }, new Container diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs index d28f886be4..3eea59006a 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs @@ -193,7 +193,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists Child = new GridContainer { RelativeSizeAxes = Axes.X, - Height = 500, + Height = 448, Content = new[] { new Drawable[] From 209929844414101678998d6c3fa63b61dd9acf5e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Aug 2021 17:30:50 +0900 Subject: [PATCH 1302/2442] Disallow clicking on chat textbox during gameplay --- .../Multiplayer/TestSceneGameplayChatDisplay.cs | 14 ++++++++++++++ .../OnlinePlay/Multiplayer/GameplayChatDisplay.cs | 2 ++ 2 files changed, 16 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs index 795963a1ba..ffbc75ae15 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs @@ -49,6 +49,20 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("expand", () => chatDisplay.Expanded.Value = true); } + [Test] + public void TestCantClickWhenPlaying() + { + setLocalUserPlaying(true); + + AddStep("attempt focus chat", () => + { + InputManager.MoveMouseTo(textBox); + InputManager.Click(MouseButton.Left); + }); + + assertChatFocused(false); + } + [Test] public void TestFocusDroppedWhenPlaying() { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs index 40da8d0a84..6a2609480e 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs @@ -18,6 +18,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private IBindable localUserPlaying = new Bindable(); + public override bool PropagatePositionalInputSubTree => !localUserPlaying.Value; + public Bindable Expanded = new Bindable(); private const float height = 100; From 5e91ec73e3bf888f5aaf305478d5fceb317106ec Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 18 Aug 2021 11:36:27 +0300 Subject: [PATCH 1303/2442] Handle star rating range display sizing --- .../Beatmaps/Drawables/StarRatingDisplay.cs | 22 ++++++++++++++++--- .../Components/StarRatingRangeDisplay.cs | 4 ++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs index ed2fee23d5..192ebe79d7 100644 --- a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs +++ b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs @@ -51,6 +51,23 @@ namespace osu.Game.Beatmaps.Drawables AutoSizeAxes = Axes.Both; + MarginPadding margin = default; + + switch (size) + { + case StarRatingDisplaySize.Small: + margin = new MarginPadding { Horizontal = 7f }; + break; + + case StarRatingDisplaySize.Range: + margin = new MarginPadding { Horizontal = 8f }; + break; + + case StarRatingDisplaySize.Regular: + margin = new MarginPadding { Horizontal = 8f, Vertical = 2f }; + break; + } + InternalChild = new CircularContainer { Masking = true, @@ -66,9 +83,7 @@ namespace osu.Game.Beatmaps.Drawables Anchor = Anchor.Centre, Origin = Anchor.Centre, AutoSizeAxes = Axes.Both, - Margin = size == StarRatingDisplaySize.Small - ? new MarginPadding { Horizontal = 7f } - : new MarginPadding { Horizontal = 8f, Vertical = 2f }, + Margin = margin, ColumnDimensions = new[] { new Dimension(GridSizeMode.AutoSize), @@ -124,6 +139,7 @@ namespace osu.Game.Beatmaps.Drawables public enum StarRatingDisplaySize { Small, + Range, Regular, } } diff --git a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs index 8e62153225..7b14acf924 100644 --- a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs @@ -64,8 +64,8 @@ namespace osu.Game.Screens.OnlinePlay.Components AutoSizeAxes = Axes.Both, Children = new Drawable[] { - minDisplay = new StarRatingDisplay(default) { Size = new Vector2(52f, 16f) }, - maxDisplay = new StarRatingDisplay(default) { Size = new Vector2(52f, 16f) } + minDisplay = new StarRatingDisplay(default, StarRatingDisplaySize.Range), + maxDisplay = new StarRatingDisplay(default, StarRatingDisplaySize.Range) } } }; From d2df09432f040d29ae08a92b2104c8d4fca5eda2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 18 Aug 2021 11:49:33 +0300 Subject: [PATCH 1304/2442] Center the star rating display text rather than left --- osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs index 192ebe79d7..25cde5fb82 100644 --- a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs +++ b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs @@ -105,8 +105,8 @@ namespace osu.Game.Beatmaps.Drawables Empty(), starsText = new OsuSpriteText { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Margin = new MarginPadding { Bottom = 1.5f }, // todo: this should be size: 12f, but to match up with the design, it needs to be 14.4f // see https://github.com/ppy/osu-framework/issues/3271. From 365c1bccc67764695e940006176ab1b676eedee0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Aug 2021 18:24:19 +0900 Subject: [PATCH 1305/2442] Fix multiplayer channel being unintentionally left after gameplay session --- .../OnlinePlay/Match/Components/MatchChatDisplay.cs | 9 +++++++-- .../OnlinePlay/Multiplayer/GameplayChatDisplay.cs | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs index a96d64cb5d..5f960c1b5c 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs @@ -19,9 +19,12 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components [Resolved(CanBeNull = true)] private ChannelManager channelManager { get; set; } - public MatchChatDisplay() + private readonly bool leaveChannelOnDispose; + + public MatchChatDisplay(bool leaveChannelOnDispose = true) : base(true) { + this.leaveChannelOnDispose = leaveChannelOnDispose; } protected override void LoadComplete() @@ -42,7 +45,9 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - channelManager?.LeaveChannel(Channel.Value); + + if (leaveChannelOnDispose) + channelManager?.LeaveChannel(Channel.Value); } } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs index 6a2609480e..94542fb804 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs @@ -25,6 +25,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private const float height = 100; public GameplayChatDisplay() + : base(leaveChannelOnDispose: false) { RelativeSizeAxes = Axes.X; From 1faf789f0e70a9d8b7431f127c3a1e76ebf18942 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Aug 2021 18:25:21 +0900 Subject: [PATCH 1306/2442] Allow expanding chat using key binding even when it is hidden --- .../TestSceneGameplayChatDisplay.cs | 17 ++++++- osu.Game/Online/Chat/StandAloneChatDisplay.cs | 11 +++- .../Multiplayer/GameplayChatDisplay.cs | 50 +++++++++++++------ 3 files changed, 61 insertions(+), 17 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs index ffbc75ae15..51960680d6 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs @@ -83,13 +83,28 @@ namespace osu.Game.Tests.Visual.Multiplayer } [Test] - public void TestFocusOnTabKey() + public void TestFocusOnTabKeyWhenExpanded() { assertChatFocused(false); AddStep("press tab", () => InputManager.Key(Key.Tab)); assertChatFocused(true); } + [Test] + public void TestFocusOnTabKeyWhenNotExpanded() + { + AddStep("set not expanded", () => chatDisplay.Expanded.Value = false); + AddUntilStep("is not visible", () => !chatDisplay.IsPresent); + + AddStep("press tab", () => InputManager.Key(Key.Tab)); + assertChatFocused(true); + AddUntilStep("is visible", () => chatDisplay.IsPresent); + + AddStep("press enter", () => InputManager.Key(Key.Enter)); + assertChatFocused(false); + AddUntilStep("is not visible", () => !chatDisplay.IsPresent); + } + private void assertChatFocused(bool isFocused) => AddAssert($"chat {(isFocused ? "focused" : "not focused")}", () => textBox.HasFocus == isFocused); diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs index 9d23a089df..6ed2055e65 100644 --- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs +++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Chat; @@ -22,7 +23,7 @@ namespace osu.Game.Online.Chat { public readonly Bindable Channel = new Bindable(); - protected readonly FocusedTextBox Textbox; + protected readonly ChatTextBox Textbox; protected ChannelManager ChannelManager; @@ -121,6 +122,14 @@ namespace osu.Game.Online.Chat BackgroundUnfocused = new Color4(10, 10, 10, 10); BackgroundFocused = new Color4(10, 10, 10, 255); } + + protected override void OnFocusLost(FocusLostEvent e) + { + base.OnFocusLost(e); + FocusLost?.Invoke(); + } + + public Action FocusLost; } public class StandAloneDrawableChannel : DrawableChannel diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs index 94542fb804..5d494379e6 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs @@ -22,28 +22,20 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public Bindable Expanded = new Bindable(); + private readonly Bindable expandedFromTextboxFocus = new Bindable(); + private const float height = 100; + public override bool PropagateNonPositionalInputSubTree => true; + public GameplayChatDisplay() : base(leaveChannelOnDispose: false) { RelativeSizeAxes = Axes.X; Background.Alpha = 0.2f; - } - private void expandedChanged(ValueChangedEvent expanded) - { - if (expanded.NewValue) - { - this.FadeIn(300, Easing.OutQuint); - this.ResizeHeightTo(height, 500, Easing.OutQuint); - } - else - { - this.FadeOut(300, Easing.OutQuint); - this.ResizeHeightTo(0, 500, Easing.OutQuint); - } + Textbox.FocusLost = () => expandedFromTextboxFocus.Value = false; } protected override void LoadComplete() @@ -61,7 +53,18 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer Textbox.ReleaseFocusOnCommit = playing.NewValue; }, true); - Expanded.BindValueChanged(expandedChanged, true); + Expanded.BindValueChanged(_ => updateExpandedState(), true); + expandedFromTextboxFocus.BindValueChanged(focus => + { + if (focus.NewValue) + updateExpandedState(); + else + { + // on finishing typing a message there should be a brief delay before hiding. + using (BeginDelayedSequence(600)) + updateExpandedState(); + } + }, true); } public bool OnPressed(GlobalAction action) @@ -69,7 +72,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer switch (action) { case GlobalAction.FocusChatInput: - Textbox.TakeFocus(); + expandedFromTextboxFocus.Value = true; + + // schedule required to ensure the textbox has become present from above bindable update. + Schedule(() => Textbox.TakeFocus()); return true; } @@ -79,5 +85,19 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public void OnReleased(GlobalAction action) { } + + private void updateExpandedState() + { + if (Expanded.Value || expandedFromTextboxFocus.Value) + { + this.FadeIn(300, Easing.OutQuint); + this.ResizeHeightTo(height, 500, Easing.OutQuint); + } + else + { + this.FadeOut(300, Easing.OutQuint); + this.ResizeHeightTo(0, 500, Easing.OutQuint); + } + } } } From 28e2b6cec7cff82d007d44a7aa2026ff0679ed73 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Aug 2021 18:34:09 +0900 Subject: [PATCH 1307/2442] Add transform test for fun --- .../TestSceneStarRatingDisplay.cs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs index 7883049df2..052251d5a8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs @@ -3,6 +3,7 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Utils; @@ -44,6 +45,31 @@ namespace osu.Game.Tests.Visual.UserInterface }); } + [Test] + public void TestSpectrum() + { + StarRatingDisplay starRating = null; + + BindableDouble starRatingNumeric; + + AddStep("load display", () => + { + Child = starRating = new StarRatingDisplay(new StarDifficulty(5.55, 1)) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(3f), + }; + }); + + AddStep("transform over spectrum", () => + { + starRatingNumeric = new BindableDouble(); + starRatingNumeric.BindValueChanged(val => starRating.Current.Value = new StarDifficulty(val.NewValue, 1)); + this.TransformBindableTo(starRatingNumeric, 10, 10000, Easing.OutQuint); + }); + } + [Test] public void TestChangingStarRatingDisplay() { From 8172ffc401a5ff0abe8a779cc7fd2dba8aee3448 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 18 Aug 2021 13:12:58 +0300 Subject: [PATCH 1308/2442] Fix lounge sub screen loading layer displaying in the background --- osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 8bed3d6049..24aad8c124 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -84,7 +84,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge InternalChildren = new Drawable[] { ListingPollingComponent = CreatePollingComponent().With(c => c.Filter.BindTarget = filter), - loadingLayer = new LoadingLayer(true), new Container { RelativeSizeAxes = Axes.Both, @@ -162,6 +161,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge Filter = { BindTarget = filter } } }, + loadingLayer = new LoadingLayer(true), } }, } From 63af67f61b5a69918b34ae7f4dc764deffa7b747 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 18 Aug 2021 20:21:29 +0900 Subject: [PATCH 1309/2442] Cleanup around footers --- .../Multiplayer/TestSceneMatchHeader.cs | 55 ------------- .../OnlinePlay/Match/Components/Footer.cs | 48 ----------- .../OnlinePlay/Match/Components/Header.cs | 79 ------------------- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 29 ++++++- .../Match/MultiplayerMatchFooter.cs | 63 ++++++--------- .../Multiplayer/MultiplayerMatchSubScreen.cs | 22 +++--- .../Playlists/PlaylistsRoomFooter.cs | 32 ++++++++ .../Playlists/PlaylistsRoomSubScreen.cs | 25 +++--- 8 files changed, 103 insertions(+), 250 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs delete mode 100644 osu.Game/Screens/OnlinePlay/Match/Components/Footer.cs delete mode 100644 osu.Game/Screens/OnlinePlay/Match/Components/Header.cs create mode 100644 osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomFooter.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs deleted file mode 100644 index 71ba5db481..0000000000 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using NUnit.Framework; -using osu.Game.Beatmaps; -using osu.Game.Online.Rooms; -using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Screens.OnlinePlay.Match.Components; -using osu.Game.Tests.Visual.OnlinePlay; -using osu.Game.Users; - -namespace osu.Game.Tests.Visual.Multiplayer -{ - public class TestSceneMatchHeader : OnlinePlayTestScene - { - [SetUp] - public new void Setup() => Schedule(() => - { - SelectedRoom.Value = new Room - { - Name = { Value = "A very awesome room" }, - Host = { Value = new User { Id = 2, Username = "peppy" } }, - Playlist = - { - new PlaylistItem - { - Beatmap = - { - Value = new BeatmapInfo - { - Metadata = new BeatmapMetadata - { - Title = "Title", - Artist = "Artist", - AuthorString = "Author", - }, - Version = "Version", - Ruleset = new OsuRuleset().RulesetInfo - } - }, - RequiredMods = - { - new OsuModDoubleTime(), - new OsuModNoFail(), - new OsuModRelax(), - } - } - } - }; - - Child = new Header(); - }); - } -} diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/Footer.cs b/osu.Game/Screens/OnlinePlay/Match/Components/Footer.cs deleted file mode 100644 index e91c46beed..0000000000 --- a/osu.Game/Screens/OnlinePlay/Match/Components/Footer.cs +++ /dev/null @@ -1,48 +0,0 @@ -// 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 osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using osu.Game.Screens.OnlinePlay.Playlists; -using osuTK; - -namespace osu.Game.Screens.OnlinePlay.Match.Components -{ - public class Footer : CompositeDrawable - { - public const float HEIGHT = 50; - - public Action OnStart; - - private readonly Drawable background; - - public Footer() - { - RelativeSizeAxes = Axes.X; - Height = HEIGHT; - - InternalChildren = new[] - { - background = new Box { RelativeSizeAxes = Axes.Both }, - new PlaylistsReadyButton - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(600, 50), - Action = () => OnStart?.Invoke() - } - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - background.Colour = Color4Extensions.FromHex(@"28242d"); - } - } -} diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/Header.cs b/osu.Game/Screens/OnlinePlay/Match/Components/Header.cs deleted file mode 100644 index a2d11c54c1..0000000000 --- a/osu.Game/Screens/OnlinePlay/Match/Components/Header.cs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Users.Drawables; -using osuTK; - -namespace osu.Game.Screens.OnlinePlay.Match.Components -{ - public class Header : OnlinePlayComposite - { - public const float HEIGHT = 50; - - private UpdateableAvatar avatar; - private LinkFlowContainer hostText; - - public Header() - { - RelativeSizeAxes = Axes.X; - Height = HEIGHT; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - InternalChild = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(10, 0), - Children = new Drawable[] - { - avatar = new UpdateableAvatar - { - Size = new Vector2(50), - Masking = true, - CornerRadius = 10, - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new OsuSpriteText - { - Font = OsuFont.GetFont(size: 30), - Current = { BindTarget = RoomName } - }, - hostText = new LinkFlowContainer(s => s.Font = OsuFont.GetFont(size: 20)) - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - } - } - } - } - }; - - Host.BindValueChanged(host => - { - avatar.User = host.NewValue; - - hostText.Clear(); - - if (host.NewValue != null) - { - hostText.AddText("hosted by "); - hostText.AddUserLink(host.NewValue, s => s.Font = s.Font.With(weight: FontWeight.SemiBold)); - } - }, true); - } - } -} diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index e1412c894c..42d76e5a15 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -103,7 +103,7 @@ namespace osu.Game.Screens.OnlinePlay.Match RowDimensions = new[] { new Dimension(), - new Dimension(GridSizeMode.AutoSize) + new Dimension(GridSizeMode.Absolute, 50) }, Content = new[] { @@ -154,7 +154,7 @@ namespace osu.Game.Screens.OnlinePlay.Match Child = new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"3e3a44") // This is super temporary. + Colour = Color4Extensions.FromHex(@"3e3a44") // Temporary. }, }, new Container @@ -191,9 +191,21 @@ namespace osu.Game.Screens.OnlinePlay.Match }, }, // Footer - new[] + new Drawable[] { - CreateFooter() + new Container + { + RelativeSizeAxes = Axes.Both, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex(@"28242d") // Temporary. + }, + CreateFooter() + } + } } } } @@ -382,10 +394,19 @@ namespace osu.Game.Screens.OnlinePlay.Match track.Looping = false; } + /// + /// Creates the main centred content. + /// protected abstract Drawable CreateMainContent(); + /// + /// Creates the footer content. + /// protected abstract Drawable CreateFooter(); + /// + /// Creates the room settings overlay. + /// protected abstract RoomSettingsOverlay CreateRoomSettingsOverlay(); private class UserModSelectOverlay : LocalPlayerModSelectOverlay diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs index d4f5428bfb..9e8d51e64e 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs @@ -2,18 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { public class MultiplayerMatchFooter : CompositeDrawable { - public const float HEIGHT = 50; private const float ready_button_width = 600; private const float spectate_button_width = 200; @@ -27,54 +22,42 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match set => spectateButton.OnSpectateClick = value; } - private readonly Drawable background; private readonly MultiplayerReadyButton readyButton; private readonly MultiplayerSpectateButton spectateButton; public MultiplayerMatchFooter() { - RelativeSizeAxes = Axes.X; - Height = HEIGHT; + RelativeSizeAxes = Axes.Both; - InternalChildren = new[] + InternalChild = new GridContainer { - background = new Box { RelativeSizeAxes = Axes.Both }, - new GridContainer + RelativeSizeAxes = Axes.Both, + Content = new[] { - RelativeSizeAxes = Axes.Both, - Content = new[] + new Drawable[] { - new Drawable[] + null, + spectateButton = new MultiplayerSpectateButton { - null, - spectateButton = new MultiplayerSpectateButton - { - RelativeSizeAxes = Axes.Both, - }, - null, - readyButton = new MultiplayerReadyButton - { - RelativeSizeAxes = Axes.Both, - }, - null - } - }, - ColumnDimensions = new[] - { - new Dimension(), - new Dimension(maxSize: spectate_button_width), - new Dimension(GridSizeMode.Absolute, 10), - new Dimension(maxSize: ready_button_width), - new Dimension() + RelativeSizeAxes = Axes.Both, + }, + null, + readyButton = new MultiplayerReadyButton + { + RelativeSizeAxes = Axes.Both, + }, + null } + }, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(maxSize: spectate_button_width), + new Dimension(GridSizeMode.Absolute, 10), + new Dimension(maxSize: ready_button_width), + new Dimension() } }; } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - background.Colour = Color4Extensions.FromHex(@"28242d"); - } } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index f8e5d7d901..3f5837cbbe 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -94,17 +94,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer }, true); } - protected override void UpdateMods() - { - if (SelectedItem.Value == null || client.LocalUser == null) - return; - - // update local mods based on room's reported status for the local user (omitting the base call implementation). - // this makes the server authoritative, and avoids the local user potentially setting mods that the server is not aware of (ie. if the match was started during the selection being changed). - var ruleset = Ruleset.Value.CreateInstance(); - Mods.Value = client.LocalUser.Mods.Select(m => m.ToMod(ruleset)).Concat(SelectedItem.Value.RequiredMods).ToList(); - } - protected override Drawable CreateMainContent() => new GridContainer { RelativeSizeAxes = Axes.Both, @@ -236,6 +225,17 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override RoomSettingsOverlay CreateRoomSettingsOverlay() => new MultiplayerMatchSettingsOverlay(); + protected override void UpdateMods() + { + if (SelectedItem.Value == null || client.LocalUser == null) + return; + + // update local mods based on room's reported status for the local user (omitting the base call implementation). + // this makes the server authoritative, and avoids the local user potentially setting mods that the server is not aware of (ie. if the match was started during the selection being changed). + var ruleset = Ruleset.Value.CreateInstance(); + Mods.Value = client.LocalUser.Mods.Select(m => m.ToMod(ruleset)).Concat(SelectedItem.Value.RequiredMods).ToList(); + } + [Resolved(canBeNull: true)] private DialogOverlay dialogOverlay { get; set; } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomFooter.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomFooter.cs new file mode 100644 index 0000000000..3eb1cde0a4 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomFooter.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. + +using System; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osuTK; + +namespace osu.Game.Screens.OnlinePlay.Playlists +{ + public class PlaylistsRoomFooter : CompositeDrawable + { + public Action OnStart; + + public PlaylistsRoomFooter() + { + RelativeSizeAxes = Axes.Both; + + InternalChildren = new[] + { + new PlaylistsReadyButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + Size = new Vector2(600, 1), + Action = () => OnStart?.Invoke() + } + }; + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 9855d1cb95..27de781d5f 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -20,7 +20,6 @@ using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD; using osu.Game.Users; using osuTK; -using Footer = osu.Game.Screens.OnlinePlay.Match.Components.Footer; namespace osu.Game.Screens.OnlinePlay.Playlists { @@ -70,17 +69,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists }, true); } - private void updatePollingRate() - { - selectionPollingComponent.TimeBetweenPolls.Value = isIdle.Value ? 30000 : 5000; - Logger.Log($"Polling adjusted (selection: {selectionPollingComponent.TimeBetweenPolls.Value})"); - } - - protected override Screen CreateGameplayScreen() => new PlayerLoader(() => new PlaylistsPlayer(SelectedItem.Value) - { - Exited = () => leaderboard.RefreshScores() - }); - protected override Drawable CreateMainContent() => new GridContainer { RelativeSizeAxes = Axes.Both, @@ -190,7 +178,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists } }; - protected override Drawable CreateFooter() => new Footer + protected override Drawable CreateFooter() => new PlaylistsRoomFooter { OnStart = StartPlay }; @@ -203,5 +191,16 @@ namespace osu.Game.Screens.OnlinePlay.Playlists this.Push(new PlaylistsSongSelect()); }, }; + + private void updatePollingRate() + { + selectionPollingComponent.TimeBetweenPolls.Value = isIdle.Value ? 30000 : 5000; + Logger.Log($"Polling adjusted (selection: {selectionPollingComponent.TimeBetweenPolls.Value})"); + } + + protected override Screen CreateGameplayScreen() => new PlayerLoader(() => new PlaylistsPlayer(SelectedItem.Value) + { + Exited = () => leaderboard.RefreshScores() + }); } } From eadf02933a735ea97ce8dbd69fb6a35f2318525d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 18 Aug 2021 20:53:17 +0900 Subject: [PATCH 1310/2442] Split lounge-specific implementation from DrawableRoom --- .../Multiplayer/TestSceneDrawableRoom.cs | 7 +- .../TestSceneLoungeRoomsContainer.cs | 3 +- .../Multiplayer/TestSceneMultiplayer.cs | 4 +- .../TestSceneMultiplayerLoungeSubScreen.cs | 13 +- .../Lounge/Components/DrawableRoom.cs | 263 ++---------------- .../Lounge/Components/RoomsContainer.cs | 22 +- .../OnlinePlay/Lounge/DrawableLoungeRoom.cs | 225 +++++++++++++++ .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 1 - 8 files changed, 269 insertions(+), 269 deletions(-) create mode 100644 osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index 299bbacf08..489ece3271 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -10,11 +10,11 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Framework.Utils; -using osu.Game.Graphics.UserInterface; using osu.Game.Online.Rooms; using osu.Game.Online.Rooms.RoomStatuses; using osu.Game.Overlays; using osu.Game.Rulesets.Osu; +using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Tests.Beatmaps; using osu.Game.Users; @@ -159,10 +159,7 @@ namespace osu.Game.Tests.Visual.Multiplayer })); } - var drawableRoom = new DrawableRoom(room) { MatchingFilter = true }; - drawableRoom.Action = () => drawableRoom.State = drawableRoom.State == SelectionState.Selected ? SelectionState.NotSelected : SelectionState.Selected; - - return drawableRoom; + return new DrawableLoungeRoom(room) { MatchingFilter = true }; } } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs index 7e822f898e..b23638e514 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs @@ -9,6 +9,7 @@ using osu.Framework.Testing; using osu.Game.Online.Rooms; using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Osu; +using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Tests.Visual.OnlinePlay; using osuTK.Input; @@ -150,6 +151,6 @@ namespace osu.Game.Tests.Visual.Multiplayer private bool checkRoomSelected(Room room) => SelectedRoom.Value == room; private Room getRoomInFlow(int index) => - (container.ChildrenOfType>().First().FlowingChildren.ElementAt(index) as DrawableRoom)?.Room; + (container.ChildrenOfType>().First().FlowingChildren.ElementAt(index) as DrawableRoom)?.Room; } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index e618b28f40..6c1aed71e6 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -236,8 +236,8 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("select room", () => InputManager.Key(Key.Down)); AddStep("join room", () => InputManager.Key(Key.Enter)); - DrawableRoom.PasswordEntryPopover passwordEntryPopover = null; - AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null); + DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null; + AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null); AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password"); AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().TriggerClick()); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs index c66d5429d6..4ec02a6ec8 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs @@ -9,7 +9,6 @@ using osu.Framework.Testing; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Lounge; -using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Tests.Visual.OnlinePlay; using osuTK.Input; @@ -59,20 +58,20 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("select room", () => InputManager.Key(Key.Down)); AddStep("attempt join room", () => InputManager.Key(Key.Enter)); - AddUntilStep("password prompt appeared", () => InputManager.ChildrenOfType().Any()); + AddUntilStep("password prompt appeared", () => InputManager.ChildrenOfType().Any()); AddStep("exit screen", () => Stack.Exit()); - AddUntilStep("password prompt hidden", () => !InputManager.ChildrenOfType().Any()); + AddUntilStep("password prompt hidden", () => !InputManager.ChildrenOfType().Any()); } [Test] public void TestJoinRoomWithPassword() { - DrawableRoom.PasswordEntryPopover passwordEntryPopover = null; + DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null; AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true)); AddStep("select room", () => InputManager.Key(Key.Down)); AddStep("attempt join room", () => InputManager.Key(Key.Enter)); - AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null); + AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null); AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password"); AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().TriggerClick()); @@ -83,12 +82,12 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestJoinRoomWithPasswordViaKeyboardOnly() { - DrawableRoom.PasswordEntryPopover passwordEntryPopover = null; + DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null; AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true)); AddStep("select room", () => InputManager.Key(Key.Down)); AddStep("attempt join room", () => InputManager.Key(Key.Enter)); - AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null); + AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null); AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password"); AddStep("press enter", () => InputManager.Key(Key.Enter)); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index b627dfbb8e..c5b8bffbc6 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -1,32 +1,19 @@ // 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 osu.Framework; using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; using osu.Framework.Bindables; -using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.Bindings; -using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Graphics.UserInterfaceV2; -using osu.Game.Input.Bindings; using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Screens.OnlinePlay.Components; @@ -35,104 +22,17 @@ using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { - public class DrawableRoom : OsuClickableContainer, IStateful, IFilterable, IHasContextMenu, IHasPopover, IKeyBindingHandler + public class DrawableRoom : CompositeDrawable { - public const float SELECTION_BORDER_WIDTH = 4; - private const float corner_radius = 10; - private const float transition_duration = 60; + protected const float CORNER_RADIUS = 10; private const float height = 100; - public event Action StateChanged; - - protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds(); - - private Drawable selectionBox; - - [Resolved(canBeNull: true)] - private LoungeSubScreen loungeScreen { get; set; } + public readonly Room Room; [Resolved] private BeatmapManager beatmaps { get; set; } - [Resolved(canBeNull: true)] - private Bindable selectedRoom { get; set; } - - [Resolved(canBeNull: true)] - private LoungeSubScreen lounge { get; set; } - - public readonly Room Room; - - private SelectionState state; - - private Sample sampleSelect; - private Sample sampleJoin; - - public SelectionState State - { - get => state; - set - { - if (value == state) - return; - - state = value; - - if (selectionBox != null) - { - if (state == SelectionState.Selected) - selectionBox.FadeIn(transition_duration); - else - selectionBox.FadeOut(transition_duration); - } - - StateChanged?.Invoke(State); - } - } - - public IEnumerable FilterTerms => new[] { Room.Name.Value }; - - private bool matchingFilter; - - public bool MatchingFilter - { - get => matchingFilter; - set - { - matchingFilter = value; - - if (!IsLoaded) - return; - - if (matchingFilter) - this.FadeIn(200); - else - Hide(); - } - } - - private int numberOfAvatars = 7; - - public int NumberOfAvatars - { - get => numberOfAvatars; - set - { - numberOfAvatars = value; - - if (recentParticipantsList != null) - recentParticipantsList.NumberOfCircles = value; - } - } - - public bool FilteringActive { get; set; } - - protected readonly Container ButtonsContainer = new Container - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X - }; + protected Container ButtonsContainer { get; private set; } private readonly Bindable roomCategory = new Bindable(); private readonly Bindable hasPassword = new Bindable(); @@ -149,7 +49,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Height = height; Masking = true; - CornerRadius = corner_radius; + CornerRadius = CORNER_RADIUS; EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, @@ -159,9 +59,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } [BackgroundDependencyLoader] - private void load(OverlayColourProvider colours, AudioManager audio) + private void load(OverlayColourProvider colours) { - Children = new Drawable[] + InternalChildren = new Drawable[] { // This resolves internal 1px gaps due to applying the (parenting) corner radius and masking across multiple filling background sprites. new Box @@ -183,7 +83,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { RelativeSizeAxes = Axes.Both, Masking = true, - CornerRadius = corner_radius, + CornerRadius = CORNER_RADIUS, Children = new Drawable[] { new GridContainer @@ -303,7 +203,13 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components }, Children = new Drawable[] { - ButtonsContainer, + ButtonsContainer = new Container + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X + }, recentParticipantsList = new RecentParticipantsList { Anchor = Anchor.CentreRight, @@ -316,36 +222,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components }, }, }, - new StatusColouredContainer(transition_duration) - { - RelativeSizeAxes = Axes.Both, - Child = selectionBox = new Container - { - RelativeSizeAxes = Axes.Both, - Alpha = state == SelectionState.Selected ? 1 : 0, - Masking = true, - CornerRadius = corner_radius, - BorderThickness = SELECTION_BORDER_WIDTH, - BorderColour = Color4.White, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true - } - } - }, - }; - - sampleSelect = audio.Samples.Get($@"UI/{HoverSampleSet.Default.GetDescription()}-select"); - sampleJoin = audio.Samples.Get($@"UI/{HoverSampleSet.Submit.GetDescription()}-select"); - } - - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) - { - return new CachedModelDependencyContainer(base.CreateChildDependencies(parent)) - { - Model = { Value = Room } }; } @@ -353,11 +229,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { base.LoadComplete(); - if (matchingFilter) - this.FadeInFromZero(transition_duration); - else - Alpha = 0; - roomCategory.BindTo(Room.Category); roomCategory.BindValueChanged(c => { @@ -371,57 +242,26 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components hasPassword.BindValueChanged(v => passwordIcon.Alpha = v.NewValue ? 1 : 0, true); } - public Popover GetPopover() => new PasswordEntryPopover(Room) { JoinRequested = lounge.Join }; - - public MenuItem[] ContextMenuItems => new MenuItem[] + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { - new OsuMenuItem("Create copy", MenuItemType.Standard, () => + return new CachedModelDependencyContainer(base.CreateChildDependencies(parent)) { - lounge?.Open(Room.DeepClone()); - }) - }; - - public bool OnPressed(GlobalAction action) - { - if (selectedRoom.Value != Room) - return false; - - switch (action) - { - case GlobalAction.Select: - TriggerClick(); - return true; - } - - return false; + Model = { Value = Room } + }; } - public void OnReleased(GlobalAction action) + private int numberOfAvatars = 7; + + public int NumberOfAvatars { - } - - protected override bool ShouldBeConsideredForInput(Drawable child) => state == SelectionState.Selected || child is HoverSounds; - - protected override bool OnClick(ClickEvent e) - { - if (Room != selectedRoom.Value) + get => numberOfAvatars; + set { - sampleSelect?.Play(); - selectedRoom.Value = Room; - return true; + numberOfAvatars = value; + + if (recentParticipantsList != null) + recentParticipantsList.NumberOfCircles = value; } - - if (Room.HasPassword.Value) - { - sampleJoin?.Play(); - this.ShowPopover(); - return true; - } - - sampleJoin?.Play(); - lounge?.Join(Room, null); - - return base.OnClick(e); } private class RoomNameText : OsuSpriteText @@ -508,52 +348,5 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components }; } } - - public class PasswordEntryPopover : OsuPopover - { - private readonly Room room; - - public Action JoinRequested; - - public PasswordEntryPopover(Room room) - { - this.room = room; - } - - private OsuPasswordTextBox passwordTextbox; - - [BackgroundDependencyLoader] - private void load() - { - Child = new FillFlowContainer - { - Margin = new MarginPadding(10), - Spacing = new Vector2(5), - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Children = new Drawable[] - { - passwordTextbox = new OsuPasswordTextBox - { - Width = 200, - }, - new TriangleButton - { - Width = 80, - Text = "Join Room", - Action = () => JoinRequested?.Invoke(room, passwordTextbox.Text) - } - } - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - Schedule(() => GetContainingInputManager().ChangeFocus(passwordTextbox)); - passwordTextbox.OnCommit += (_, __) => JoinRequested?.Invoke(room, passwordTextbox.Text); - } - } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index e243477a8c..be5558ed0b 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -15,7 +15,6 @@ using osu.Framework.Input.Events; using osu.Framework.Threading; using osu.Game.Extensions; using osu.Game.Graphics.Cursor; -using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Online.Rooms; using osuTK; @@ -26,7 +25,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { private readonly IBindableList rooms = new BindableList(); - private readonly FillFlowContainer roomFlow; + private readonly FillFlowContainer roomFlow; public IReadOnlyList Rooms => roomFlow.FlowingChildren.Cast().ToArray(); @@ -56,7 +55,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Child = roomFlow = new FillFlowContainer + Child = roomFlow = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -74,16 +73,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components rooms.BindTo(roomManager.Rooms); Filter?.BindValueChanged(criteria => applyFilterCriteria(criteria.NewValue), true); - - selectedRoom.BindValueChanged(selection => - { - updateSelection(); - }, true); } - private void updateSelection() => - roomFlow.Children.ForEach(r => r.State = r.Room == selectedRoom.Value ? SelectionState.Selected : SelectionState.NotSelected); - private void applyFilterCriteria(FilterCriteria criteria) { roomFlow.Children.ForEach(r => @@ -122,22 +113,17 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { foreach (var room in rooms) { - roomFlow.Add(new DrawableRoom(room)); + roomFlow.Add(new DrawableLoungeRoom(room)); } applyFilterCriteria(Filter?.Value); - - updateSelection(); } private void removeRooms(IEnumerable rooms) { foreach (var r in rooms) { - var toRemove = roomFlow.Single(d => d.Room == r); - toRemove.Action = null; - - roomFlow.Remove(toRemove); + roomFlow.RemoveAll(d => d.Room == r); // selection may have a lease due to being in a sub screen. if (!selectedRoom.Disabled) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs new file mode 100644 index 0000000000..764d4e9d3a --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -0,0 +1,225 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; +using osu.Framework.Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; +using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Input.Bindings; +using osu.Game.Online.Rooms; +using osu.Game.Screens.OnlinePlay.Components; +using osu.Game.Screens.OnlinePlay.Lounge.Components; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Screens.OnlinePlay.Lounge +{ + /// + /// A with lounge-specific interactions such as selection and hover sounds. + /// + public class DrawableLoungeRoom : DrawableRoom, IFilterable, IHasContextMenu, IHasPopover, IKeyBindingHandler + { + private const float transition_duration = 60; + private const float selection_border_width = 4; + + [Resolved(canBeNull: true)] + private LoungeSubScreen lounge { get; set; } + + [Resolved(canBeNull: true)] + private Bindable selectedRoom { get; set; } + + private Sample sampleSelect; + private Sample sampleJoin; + private Drawable selectionBox; + + public DrawableLoungeRoom(Room room) + : base(room) + { + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sampleSelect = audio.Samples.Get($@"UI/{HoverSampleSet.Default.GetDescription()}-select"); + sampleJoin = audio.Samples.Get($@"UI/{HoverSampleSet.Submit.GetDescription()}-select"); + + AddRangeInternal(new Drawable[] + { + new StatusColouredContainer(transition_duration) + { + RelativeSizeAxes = Axes.Both, + Child = selectionBox = new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = CORNER_RADIUS, + BorderThickness = selection_border_width, + BorderColour = Color4.White, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + } + }, + new HoverSounds() + }); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + if (matchingFilter) + this.FadeInFromZero(transition_duration); + else + Alpha = 0; + + selectedRoom.BindValueChanged(updateSelectedRoom, true); + } + + private void updateSelectedRoom(ValueChangedEvent selected) + { + if (selected.NewValue == Room) + selectionBox.FadeIn(transition_duration); + else + selectionBox.FadeOut(transition_duration); + } + + public bool FilteringActive { get; set; } + + public IEnumerable FilterTerms => new[] { Room.Name.Value }; + + private bool matchingFilter; + + public bool MatchingFilter + { + get => matchingFilter; + set + { + matchingFilter = value; + + if (!IsLoaded) + return; + + if (matchingFilter) + this.FadeIn(200); + else + Hide(); + } + } + + public Popover GetPopover() => new PasswordEntryPopover(Room) { JoinRequested = lounge.Join }; + + public MenuItem[] ContextMenuItems => new MenuItem[] + { + new OsuMenuItem("Create copy", MenuItemType.Standard, () => + { + lounge?.Open(Room.DeepClone()); + }) + }; + + public bool OnPressed(GlobalAction action) + { + if (selectedRoom.Value != Room) + return false; + + switch (action) + { + case GlobalAction.Select: + TriggerClick(); + return true; + } + + return false; + } + + public void OnReleased(GlobalAction action) + { + } + + protected override bool ShouldBeConsideredForInput(Drawable child) => selectedRoom.Value == Room || child is HoverSounds; + + protected override bool OnClick(ClickEvent e) + { + if (Room != selectedRoom.Value) + { + sampleSelect?.Play(); + selectedRoom.Value = Room; + return true; + } + + if (Room.HasPassword.Value) + { + sampleJoin?.Play(); + this.ShowPopover(); + return true; + } + + sampleJoin?.Play(); + lounge?.Join(Room, null); + + return base.OnClick(e); + } + + public class PasswordEntryPopover : OsuPopover + { + private readonly Room room; + + public Action JoinRequested; + + public PasswordEntryPopover(Room room) + { + this.room = room; + } + + private OsuPasswordTextBox passwordTextbox; + + [BackgroundDependencyLoader] + private void load() + { + Child = new FillFlowContainer + { + Margin = new MarginPadding(10), + Spacing = new Vector2(5), + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + passwordTextbox = new OsuPasswordTextBox + { + Width = 200, + }, + new TriangleButton + { + Width = 80, + Text = "Join Room", + Action = () => JoinRequested?.Invoke(room, passwordTextbox.Text) + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Schedule(() => GetContainingInputManager().ChangeFocus(passwordTextbox)); + passwordTextbox.OnCommit += (_, __) => JoinRequested?.Invoke(room, passwordTextbox.Text); + } + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 42d76e5a15..81fae558c8 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -134,7 +134,6 @@ namespace osu.Game.Screens.OnlinePlay.Match { new DrawableMatchRoom(Room, allowEdit) { - MatchingFilter = true, OnEdit = () => settingsOverlay.Show() } }, From e9221f012635c9e63a7049d8c7a046681b65b0a6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 18 Aug 2021 21:00:11 +0900 Subject: [PATCH 1311/2442] Cleanup unnecessary implementation --- osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs index ead219bee2..b3e6292f45 100644 --- a/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs @@ -58,7 +58,5 @@ namespace osu.Game.Screens.OnlinePlay.Match if (editButton != null) host.BindValueChanged(h => editButton.Alpha = h.NewValue?.Equals(api.LocalUser.Value) == true ? 1 : 0, true); } - - protected override bool ShouldBeConsideredForInput(Drawable child) => true; } } From d40023bcc1ff1e612a48dd78f60b32e31c8e868e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 18 Aug 2021 21:09:34 +0900 Subject: [PATCH 1312/2442] Hide border by default --- osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index 764d4e9d3a..4e106b844c 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -63,6 +63,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge Child = selectionBox = new Container { RelativeSizeAxes = Axes.Both, + Alpha = 0, Masking = true, CornerRadius = CORNER_RADIUS, BorderThickness = selection_border_width, From af0c7ed108387b4e77ee8bf4f902dafbd2f12551 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Aug 2021 01:25:57 +0900 Subject: [PATCH 1313/2442] Fix searching in settings not correctly invalidating the scroll position --- osu.Game/Graphics/Containers/SectionsContainer.cs | 11 ++++++++++- osu.Game/Overlays/SettingsPanel.cs | 10 ++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 8ab146efe7..b3dd44f0e1 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -170,13 +170,22 @@ namespace osu.Game.Graphics.Containers if (source == InvalidationSource.Child && (invalidation & Invalidation.DrawSize) != 0) { - lastKnownScroll = null; + InvalidateScrollPosition(); result = true; } return result; } + protected void InvalidateScrollPosition() + { + Schedule(() => + { + lastKnownScroll = null; + lastClickedSection = null; + }); + } + protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index 376e36ea4e..83b0d9c7dc 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -273,6 +273,16 @@ namespace osu.Game.Overlays { public SearchContainer SearchContainer; + public string SearchTerm + { + get => SearchContainer.SearchTerm; + set + { + SearchContainer.SearchTerm = value; + InvalidateScrollPosition(); + } + } + protected override FlowContainer CreateScrollContentContainer() => SearchContainer = new SearchContainer { From a0374d4a67c2274673f05452e9d050451f7d07e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Aug 2021 01:26:11 +0900 Subject: [PATCH 1314/2442] Adjust centre point slightly to make dim feel better --- osu.Game/Graphics/Containers/SectionsContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index b3dd44f0e1..2dc69b0bd0 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -111,7 +111,7 @@ namespace osu.Game.Graphics.Containers /// /// The percentage of the container to consider the centre-point for deciding the active section (and scrolling to a requested section). /// - private const float scroll_y_centre = 0.1f; + private const float scroll_y_centre = 0.2f; public SectionsContainer() { From e87accafc88cc5ea384b7441893d1a95a3739354 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Aug 2021 01:26:33 +0900 Subject: [PATCH 1315/2442] Fix current section logic not accounting for hidden sections --- osu.Game/Graphics/Containers/SectionsContainer.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 2dc69b0bd0..e11d1e1300 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -22,6 +22,7 @@ namespace osu.Game.Graphics.Containers where T : Drawable { public Bindable SelectedSection { get; } = new Bindable(); + private Drawable lastClickedSection; public Drawable ExpandableHeader @@ -233,15 +234,17 @@ namespace osu.Game.Graphics.Containers float scrollCentre = fixedHeaderSize + scrollContainer.DisplayableContent * scroll_y_centre + selectionLenienceAboveSection; + var presentChildren = Children.Where(c => c.IsPresent); + if (Precision.AlmostBigger(0, scrollContainer.Current)) - SelectedSection.Value = lastClickedSection as T ?? Children.FirstOrDefault(); + SelectedSection.Value = lastClickedSection as T ?? presentChildren.FirstOrDefault(); else if (Precision.AlmostBigger(scrollContainer.Current, scrollContainer.ScrollableExtent)) - SelectedSection.Value = lastClickedSection as T ?? Children.LastOrDefault(); + SelectedSection.Value = lastClickedSection as T ?? presentChildren.LastOrDefault(); else { - SelectedSection.Value = Children + SelectedSection.Value = presentChildren .TakeWhile(section => scrollContainer.GetChildPosInContent(section) - currentScroll - scrollCentre <= 0) - .LastOrDefault() ?? Children.FirstOrDefault(); + .LastOrDefault() ?? presentChildren.FirstOrDefault(); } } } From 6637c64501aab585fe3e110e3833a1b2eab35fc0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Aug 2021 01:27:14 +0900 Subject: [PATCH 1316/2442] Dim all but the current section --- osu.Game/Overlays/Settings/SettingsSection.cs | 49 ++++++++++++++++++- osu.Game/Overlays/SettingsPanel.cs | 13 +++-- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index f993a46dc6..8d98ae484f 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -4,9 +4,11 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -19,6 +21,10 @@ namespace osu.Game.Overlays.Settings protected FillFlowContainer FlowContent; protected override Container Content => FlowContent; + private IBindable selectedSection; + + private Container content; + public abstract Drawable CreateIcon(); public abstract LocalisableString Header { get; } @@ -36,6 +42,9 @@ namespace osu.Game.Overlays.Settings public bool FilteringActive { get; set; } + [Resolved] + private SettingsPanel settingsPanel { get; set; } + protected SettingsSection() { Margin = new MarginPadding { Top = margin }; @@ -65,7 +74,7 @@ namespace osu.Game.Overlays.Settings RelativeSizeAxes = Axes.X, Height = border_size, }, - new Container + content = new Container { Padding = new MarginPadding { @@ -91,6 +100,44 @@ namespace osu.Game.Overlays.Settings } }, }); + + selectedSection = settingsPanel.CurrentSection.GetBoundCopy(); + selectedSection.BindValueChanged(selected => + { + if (selected.NewValue == this) + content.FadeIn(500, Easing.OutQuint); + else + content.FadeTo(0.25f, 500, Easing.OutQuint); + }, true); + } + + private bool isCurrentSection => selectedSection.Value == this; + + protected override bool OnHover(HoverEvent e) + { + if (!isCurrentSection) + content.FadeTo(0.6f, 500, Easing.OutQuint); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + if (!isCurrentSection) + content.FadeTo(0.25f, 500, Easing.OutQuint); + base.OnHoverLost(e); + } + + protected override bool OnClick(ClickEvent e) + { + if (!isCurrentSection) + settingsPanel.SectionsContainer.ScrollTo(this); + + return base.OnClick(e); + } + + protected override bool ShouldBeConsideredForInput(Drawable child) + { + return isCurrentSection; } } } diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index 83b0d9c7dc..e4e76592d8 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using osuTK; using osuTK.Graphics; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -21,6 +22,7 @@ using osu.Game.Overlays.Settings; namespace osu.Game.Overlays { + [Cached] public abstract class SettingsPanel : OsuFocusedOverlayContainer { public const float CONTENT_MARGINS = 15; @@ -46,7 +48,7 @@ namespace osu.Game.Overlays protected Sidebar Sidebar; private SidebarButton selectedSidebarButton; - protected SettingsSectionsContainer SectionsContainer; + public SettingsSectionsContainer SectionsContainer { get; private set; } private SeekLimitedSearchTextBox searchTextBox; @@ -65,6 +67,8 @@ namespace osu.Game.Overlays private Task sectionsLoadingTask; + public IBindable CurrentSection = new Bindable(); + protected SettingsPanel(bool showSidebar) { this.showSidebar = showSidebar; @@ -105,6 +109,7 @@ namespace osu.Game.Overlays Masking = true, RelativeSizeAxes = Axes.Both, ExpandableHeader = CreateHeader(), + SelectedSection = { BindTarget = CurrentSection }, FixedHeader = searchTextBox = new SeekLimitedSearchTextBox { RelativeSizeAxes = Axes.X, @@ -238,8 +243,10 @@ namespace osu.Game.Overlays if (selectedSidebarButton != null) selectedSidebarButton.Selected = false; - selectedSidebarButton = Sidebar.Children.Single(b => b.Section == section.NewValue); - selectedSidebarButton.Selected = true; + selectedSidebarButton = Sidebar.Children.FirstOrDefault(b => b.Section == section.NewValue); + + if (selectedSidebarButton != null) + selectedSidebarButton.Selected = true; }, true); }); } From 61675eefe0a5fecb4f01661ad717e49b1d398a87 Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Wed, 18 Aug 2021 17:36:57 +0100 Subject: [PATCH 1317/2442] Add length limit to match settings textboxes --- .../Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs index 61bb39d0c5..7bad661cf4 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs @@ -48,6 +48,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components { BackgroundUnfocused = Color4.Black; BackgroundFocused = Color4.Black; + LengthLimit = 100; } } @@ -63,6 +64,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components { BackgroundUnfocused = Color4.Black; BackgroundFocused = Color4.Black; + LengthLimit = 255; } } From ea08d48d273cb2253ed1ddba751d24a3cf369e19 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 18 Aug 2021 18:53:01 +0200 Subject: [PATCH 1318/2442] Remove unecessary localised string due to online view container handling visibility when user is not logged in. --- osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs index 441ef2f6cf..c0460a4536 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs @@ -29,7 +29,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons private readonly bool noVideo; - public LocalisableString TooltipText => button.Enabled.Value ? BeatmapsetsStrings.ShowDetailsDownloadDefault : BeatmapsetsStrings.ShowDetailsLoggedOut; + public LocalisableString TooltipText => BeatmapsetsStrings.ShowDetailsDownloadDefault; private readonly IBindable localUser = new Bindable(); From 43cbd10a38a8c148ba03a62caf46bcf9c9b73758 Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Wed, 18 Aug 2021 18:30:50 +0100 Subject: [PATCH 1319/2442] change limit to be on actual usage site --- .../Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs | 2 -- .../Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs index 7bad661cf4..61bb39d0c5 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs @@ -48,7 +48,6 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components { BackgroundUnfocused = Color4.Black; BackgroundFocused = Color4.Black; - LengthLimit = 100; } } @@ -64,7 +63,6 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components { BackgroundUnfocused = Color4.Black; BackgroundFocused = Color4.Black; - LengthLimit = 255; } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 338d2c9e84..53d494106b 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -136,6 +136,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { RelativeSizeAxes = Axes.X, TabbableContentContainer = this, + LengthLimit = 100, }, }, new Section("Room visibility") @@ -195,6 +196,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { RelativeSizeAxes = Axes.X, TabbableContentContainer = this, + LengthLimit = 255, }, }, } From ffbe8ddfa4ed5a97c02e9c296479549482224714 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 19 Aug 2021 04:23:02 +0300 Subject: [PATCH 1320/2442] Refactor lounge sub screen layout for better loading screen appearance --- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 117 ++++++++---------- 1 file changed, 51 insertions(+), 66 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 24aad8c124..ee9331b71a 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -76,6 +76,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge [BackgroundDependencyLoader(true)] private void load([CanBeNull] IdleTracker idleTracker) { + const float controls_area_height = 25f; + if (idleTracker != null) isIdle.BindTo(idleTracker.IsIdle); @@ -84,86 +86,69 @@ namespace osu.Game.Screens.OnlinePlay.Lounge InternalChildren = new Drawable[] { ListingPollingComponent = CreatePollingComponent().With(c => c.Filter.BindTarget = filter), - new Container + scrollContainer = new OsuScrollContainer { + Name = @"Scrollable rooms container", RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { - Left = WaveOverlayContainer.WIDTH_PADDING, - Right = WaveOverlayContainer.WIDTH_PADDING, + Horizontal = WaveOverlayContainer.WIDTH_PADDING, + Top = Header.HEIGHT + controls_area_height + 20, }, - Child = new GridContainer + ScrollbarOverlapsContent = false, + Child = roomsContainer = new RoomsContainer { - RelativeSizeAxes = Axes.Both, - RowDimensions = new[] + Filter = { BindTarget = filter } + } + }, + loadingLayer = new LoadingLayer(true), + new FillFlowContainer + { + Name = @"Header area flow", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = WaveOverlayContainer.WIDTH_PADDING }, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new Container { - new Dimension(GridSizeMode.Absolute, Header.HEIGHT), - new Dimension(GridSizeMode.Absolute, 25), - new Dimension(GridSizeMode.Absolute, 20) + RelativeSizeAxes = Axes.X, + Height = Header.HEIGHT, + Child = searchTextBox = new LoungeSearchTextBox + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.X, + Width = 0.6f, + }, }, - Content = new[] + new Container { - new Drawable[] + RelativeSizeAxes = Axes.X, + Height = controls_area_height, + Children = new Drawable[] { - searchTextBox = new LoungeSearchTextBox + Buttons.WithChild(CreateNewRoomButton().With(d => { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - RelativeSizeAxes = Axes.X, - Width = 0.6f, - }, - }, - new Drawable[] - { - new Container + d.Anchor = Anchor.BottomLeft; + d.Origin = Anchor.BottomLeft; + d.Size = new Vector2(150, 37.5f); + d.Action = () => Open(); + })), + new FillFlowContainer { - RelativeSizeAxes = Axes.Both, - Depth = float.MinValue, // Contained filters should appear over the top of rooms. - Children = new Drawable[] + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10), + ChildrenEnumerable = CreateFilterControls().Select(f => f.With(d => { - Buttons.WithChild(CreateNewRoomButton().With(d => - { - d.Anchor = Anchor.BottomLeft; - d.Origin = Anchor.BottomLeft; - d.Size = new Vector2(150, 37.5f); - d.Action = () => Open(); - })), - new FillFlowContainer - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(10), - ChildrenEnumerable = CreateFilterControls().Select(f => f.With(d => - { - d.Anchor = Anchor.TopRight; - d.Origin = Anchor.TopRight; - })) - } - } + d.Anchor = Anchor.TopRight; + d.Origin = Anchor.TopRight; + })) } - }, - null, - new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - scrollContainer = new OsuScrollContainer - { - RelativeSizeAxes = Axes.Both, - ScrollbarOverlapsContent = false, - Child = roomsContainer = new RoomsContainer - { - Filter = { BindTarget = filter } - } - }, - loadingLayer = new LoadingLayer(true), - } - }, } } }, From 0e2f3dff4dda07cd8b388d952f92172a657e9f83 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 19 Aug 2021 05:09:49 +0300 Subject: [PATCH 1321/2442] Fix rooms scroll container not masking properly due to padding --- .../Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index ee9331b71a..0664e44a73 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -86,20 +86,24 @@ namespace osu.Game.Screens.OnlinePlay.Lounge InternalChildren = new Drawable[] { ListingPollingComponent = CreatePollingComponent().With(c => c.Filter.BindTarget = filter), - scrollContainer = new OsuScrollContainer + new Container { - Name = @"Scrollable rooms container", + Name = @"Rooms area", RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Horizontal = WaveOverlayContainer.WIDTH_PADDING, Top = Header.HEIGHT + controls_area_height + 20, }, - ScrollbarOverlapsContent = false, - Child = roomsContainer = new RoomsContainer + Child = scrollContainer = new OsuScrollContainer { - Filter = { BindTarget = filter } - } + RelativeSizeAxes = Axes.Both, + ScrollbarOverlapsContent = false, + Child = roomsContainer = new RoomsContainer + { + Filter = { BindTarget = filter } + } + }, }, loadingLayer = new LoadingLayer(true), new FillFlowContainer From f4ae587a3385c63f90295c02fb118c9af55935eb Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 19 Aug 2021 06:24:06 +0300 Subject: [PATCH 1322/2442] Extract room request handling logic to its own class --- ...stRequestHandlingMultiplayerRoomManager.cs | 138 ++-------------- .../OnlinePlay/TestRoomRequestsHandler.cs | 147 ++++++++++++++++++ 2 files changed, 159 insertions(+), 126 deletions(-) create mode 100644 osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs diff --git a/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs b/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs index c3a944f93c..5de518990a 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs @@ -1,150 +1,36 @@ // 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 osu.Framework.Allocation; using osu.Game.Online.API; -using osu.Game.Online.API.Requests; using osu.Game.Online.Rooms; -using osu.Game.Rulesets.Scoring; -using osu.Game.Scoring; using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Multiplayer; +using osu.Game.Tests.Visual.OnlinePlay; namespace osu.Game.Tests.Visual.Multiplayer { /// - /// A for use in multiplayer test scenes. Should generally not be used by itself outside of a . + /// A for use in multiplayer test scenes, backed by a . + /// Should generally not be used by itself outside of a . /// - /// - /// This implementation will pretend to be a server, handling room retrieval and manipulation requests - /// and returning a roughly expected state, without the need for a server to be running. - /// public class TestRequestHandlingMultiplayerRoomManager : MultiplayerRoomManager { - [Resolved] - private IAPIProvider api { get; set; } + public IReadOnlyList ServerSideRooms => handler.ServerSideRooms; - [Resolved] - private OsuGameBase game { get; set; } - - public IReadOnlyList ServerSideRooms => serverSideRooms; - private readonly List serverSideRooms = new List(); - - private int currentRoomId; - private int currentPlaylistItemId; + private readonly TestRoomRequestsHandler handler = new TestRoomRequestsHandler(); [BackgroundDependencyLoader] - private void load() + private void load(IAPIProvider api, OsuGameBase game) { - int currentScoreId = 0; - - // Handling here is pretending to be a server, while also updating the local state to match - // how the server would eventually respond and update the RoomManager. - ((DummyAPIAccess)api).HandleRequest = req => - { - switch (req) - { - case CreateRoomRequest createRoomRequest: - var apiRoom = new Room(); - - apiRoom.CopyFrom(createRoomRequest.Room); - - // Passwords are explicitly not copied between rooms. - apiRoom.HasPassword.Value = !string.IsNullOrEmpty(createRoomRequest.Room.Password.Value); - apiRoom.Password.Value = createRoomRequest.Room.Password.Value; - - AddServerSideRoom(apiRoom); - - var responseRoom = new APICreatedRoom(); - responseRoom.CopyFrom(createResponseRoom(apiRoom, false)); - - createRoomRequest.TriggerSuccess(responseRoom); - return true; - - case JoinRoomRequest joinRoomRequest: - { - var room = ServerSideRooms.Single(r => r.RoomID.Value == joinRoomRequest.Room.RoomID.Value); - - if (joinRoomRequest.Password != room.Password.Value) - { - joinRoomRequest.TriggerFailure(new InvalidOperationException("Invalid password.")); - return true; - } - - joinRoomRequest.TriggerSuccess(); - return true; - } - - case PartRoomRequest partRoomRequest: - partRoomRequest.TriggerSuccess(); - return true; - - case GetRoomsRequest getRoomsRequest: - var roomsWithoutParticipants = new List(); - - foreach (var r in ServerSideRooms) - roomsWithoutParticipants.Add(createResponseRoom(r, false)); - - getRoomsRequest.TriggerSuccess(roomsWithoutParticipants); - return true; - - case GetRoomRequest getRoomRequest: - getRoomRequest.TriggerSuccess(createResponseRoom(ServerSideRooms.Single(r => r.RoomID.Value == getRoomRequest.RoomId), true)); - return true; - - case GetBeatmapSetRequest getBeatmapSetRequest: - var onlineReq = new GetBeatmapSetRequest(getBeatmapSetRequest.ID, getBeatmapSetRequest.Type); - onlineReq.Success += res => getBeatmapSetRequest.TriggerSuccess(res); - onlineReq.Failure += e => getBeatmapSetRequest.TriggerFailure(e); - - // Get the online API from the game's dependencies. - game.Dependencies.Get().Queue(onlineReq); - return true; - - case CreateRoomScoreRequest createRoomScoreRequest: - createRoomScoreRequest.TriggerSuccess(new APIScoreToken { ID = 1 }); - return true; - - case SubmitRoomScoreRequest submitRoomScoreRequest: - submitRoomScoreRequest.TriggerSuccess(new MultiplayerScore - { - ID = currentScoreId++, - Accuracy = 1, - EndedAt = DateTimeOffset.Now, - Passed = true, - Rank = ScoreRank.S, - MaxCombo = 1000, - TotalScore = 1000000, - User = api.LocalUser.Value, - Statistics = new Dictionary() - }); - return true; - } - - return false; - }; + ((DummyAPIAccess)api).HandleRequest = request => handler.HandleRequest(request, api.LocalUser.Value, game); } - public void AddServerSideRoom(Room room) - { - room.RoomID.Value ??= currentRoomId++; - for (int i = 0; i < room.Playlist.Count; i++) - room.Playlist[i].ID = currentPlaylistItemId++; - - serverSideRooms.Add(room); - } - - private Room createResponseRoom(Room room, bool withParticipants) - { - var responseRoom = new Room(); - responseRoom.CopyFrom(room); - responseRoom.Password.Value = null; - if (!withParticipants) - responseRoom.RecentParticipants.Clear(); - return responseRoom; - } + /// + /// Adds a room to a local "server-side" list that's returned when a is fired. + /// + /// The room. + public void AddServerSideRoom(Room room) => handler.AddServerSideRoom(room); } } diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs new file mode 100644 index 0000000000..7f975c9985 --- /dev/null +++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs @@ -0,0 +1,147 @@ +// 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 osu.Framework.Allocation; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.Rooms; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; +using osu.Game.Screens.OnlinePlay.Components; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.OnlinePlay +{ + /// + /// Represents a handler which pretends to be a server, handling room retrieval and manipulation requests + /// and returning a roughly expected state, without the need for a server to be running. + /// + public class TestRoomRequestsHandler + { + public IReadOnlyList ServerSideRooms => serverSideRooms; + + private readonly List serverSideRooms = new List(); + + private int currentRoomId; + private int currentPlaylistItemId; + private int currentScoreId; + + /// + /// Handles an API request, while also updating the local state to match + /// how the server would eventually respond and update an . + /// + /// The API request to handle. + /// The local user to store in responses where required. + /// The game base for cases where actual online requests need to be sent. + /// Whether the request was successfully handled. + public bool HandleRequest(APIRequest request, User localUser, OsuGameBase game) + { + switch (request) + { + case CreateRoomRequest createRoomRequest: + var apiRoom = new Room(); + + apiRoom.CopyFrom(createRoomRequest.Room); + + // Passwords are explicitly not copied between rooms. + apiRoom.HasPassword.Value = !string.IsNullOrEmpty(createRoomRequest.Room.Password.Value); + apiRoom.Password.Value = createRoomRequest.Room.Password.Value; + + AddServerSideRoom(apiRoom); + + var responseRoom = new APICreatedRoom(); + responseRoom.CopyFrom(createResponseRoom(apiRoom, false)); + + createRoomRequest.TriggerSuccess(responseRoom); + return true; + + case JoinRoomRequest joinRoomRequest: + { + var room = ServerSideRooms.Single(r => r.RoomID.Value == joinRoomRequest.Room.RoomID.Value); + + if (joinRoomRequest.Password != room.Password.Value) + { + joinRoomRequest.TriggerFailure(new InvalidOperationException("Invalid password.")); + return true; + } + + joinRoomRequest.TriggerSuccess(); + return true; + } + + case PartRoomRequest partRoomRequest: + partRoomRequest.TriggerSuccess(); + return true; + + case GetRoomsRequest getRoomsRequest: + var roomsWithoutParticipants = new List(); + + foreach (var r in ServerSideRooms) + roomsWithoutParticipants.Add(createResponseRoom(r, false)); + + getRoomsRequest.TriggerSuccess(roomsWithoutParticipants); + return true; + + case GetRoomRequest getRoomRequest: + getRoomRequest.TriggerSuccess(createResponseRoom(ServerSideRooms.Single(r => r.RoomID.Value == getRoomRequest.RoomId), true)); + return true; + + case GetBeatmapSetRequest getBeatmapSetRequest: + var onlineReq = new GetBeatmapSetRequest(getBeatmapSetRequest.ID, getBeatmapSetRequest.Type); + onlineReq.Success += res => getBeatmapSetRequest.TriggerSuccess(res); + onlineReq.Failure += e => getBeatmapSetRequest.TriggerFailure(e); + + // Get the online API from the game's dependencies. + game.Dependencies.Get().Queue(onlineReq); + return true; + + case CreateRoomScoreRequest createRoomScoreRequest: + createRoomScoreRequest.TriggerSuccess(new APIScoreToken { ID = 1 }); + return true; + + case SubmitRoomScoreRequest submitRoomScoreRequest: + submitRoomScoreRequest.TriggerSuccess(new MultiplayerScore + { + ID = currentScoreId++, + Accuracy = 1, + EndedAt = DateTimeOffset.Now, + Passed = true, + Rank = ScoreRank.S, + MaxCombo = 1000, + TotalScore = 1000000, + User = localUser, + Statistics = new Dictionary() + }); + return true; + } + + return false; + } + + /// + /// Adds a room to a local "server-side" list that's returned when a is fired. + /// + /// The room. + public void AddServerSideRoom(Room room) + { + room.RoomID.Value ??= currentRoomId++; + for (int i = 0; i < room.Playlist.Count; i++) + room.Playlist[i].ID = currentPlaylistItemId++; + + serverSideRooms.Add(room); + } + + private Room createResponseRoom(Room room, bool withParticipants) + { + var responseRoom = new Room(); + responseRoom.CopyFrom(room); + responseRoom.Password.Value = null; + if (!withParticipants) + responseRoom.RecentParticipants.Clear(); + return responseRoom; + } + } +} From 49bc3a8250aeb0d5875f0461ef07eada7c82d915 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 19 Aug 2021 06:25:47 +0300 Subject: [PATCH 1323/2442] Refactor playlists room manager to handle dummy API requests --- .../TestSceneLoungeRoomsContainer.cs | 4 +- .../TestSceneMultiplayerLoungeSubScreen.cs | 2 +- .../TestScenePlaylistsLoungeSubScreen.cs | 2 +- .../TestScenePlaylistsRoomSubScreen.cs | 13 -- .../Visual/OnlinePlay/BasicTestRoomManager.cs | 111 ------------------ .../OnlinePlayTestSceneDependencies.cs | 2 +- .../TestRequestHandlingRoomManager.cs | 76 ++++++++++++ 7 files changed, 81 insertions(+), 129 deletions(-) delete mode 100644 osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs create mode 100644 osu.Game/Tests/Visual/OnlinePlay/TestRequestHandlingRoomManager.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs index 7e822f898e..d253b7f8cf 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs @@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneLoungeRoomsContainer : OnlinePlayTestScene { - protected new BasicTestRoomManager RoomManager => (BasicTestRoomManager)base.RoomManager; + protected new TestRequestHandlingRoomManager RoomManager => (TestRequestHandlingRoomManager)base.RoomManager; private RoomsContainer container; @@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("add rooms", () => RoomManager.AddRooms(3)); AddAssert("has 3 rooms", () => container.Rooms.Count == 3); - AddStep("remove first room", () => RoomManager.Rooms.Remove(RoomManager.Rooms.FirstOrDefault())); + AddStep("remove first room", () => RoomManager.RemoveRoom(RoomManager.Rooms.FirstOrDefault())); AddAssert("has 2 rooms", () => container.Rooms.Count == 2); AddAssert("first room removed", () => container.Rooms.All(r => r.Room.RoomID.Value != 0)); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs index c66d5429d6..cc05bc24aa 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs @@ -18,7 +18,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneMultiplayerLoungeSubScreen : OnlinePlayTestScene { - protected new BasicTestRoomManager RoomManager => (BasicTestRoomManager)base.RoomManager; + protected new TestRequestHandlingRoomManager RoomManager => (TestRequestHandlingRoomManager)base.RoomManager; private LoungeSubScreen loungeScreen; diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs index aff0e7ba4b..f432a6436e 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs @@ -16,7 +16,7 @@ namespace osu.Game.Tests.Visual.Playlists { public class TestScenePlaylistsLoungeSubScreen : OnlinePlayTestScene { - protected new BasicTestRoomManager RoomManager => (BasicTestRoomManager)base.RoomManager; + protected new TestRequestHandlingRoomManager RoomManager => (TestRequestHandlingRoomManager)base.RoomManager; private LoungeSubScreen loungeScreen; diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs index 9fc29049ef..28090be1f6 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs @@ -11,7 +11,6 @@ using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; -using osu.Game.Online.API; using osu.Game.Online.Rooms; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; @@ -36,18 +35,6 @@ namespace osu.Game.Tests.Visual.Playlists { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); - - ((DummyAPIAccess)API).HandleRequest = req => - { - switch (req) - { - case CreateRoomScoreRequest createRoomScoreRequest: - createRoomScoreRequest.TriggerSuccess(new APIScoreToken { ID = 1 }); - return true; - } - - return false; - }; } [SetUpSteps] diff --git a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs deleted file mode 100644 index 55fbb9f1a6..0000000000 --- a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs +++ /dev/null @@ -1,111 +0,0 @@ -// 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.Linq; -using osu.Framework.Bindables; -using osu.Game.Beatmaps; -using osu.Game.Online.Rooms; -using osu.Game.Rulesets; -using osu.Game.Screens.OnlinePlay; -using osu.Game.Screens.OnlinePlay.Components; -using osu.Game.Users; - -namespace osu.Game.Tests.Visual.OnlinePlay -{ - /// - /// A very simple for use in online play test scenes. - /// - public class BasicTestRoomManager : IRoomManager - { - public event Action RoomsUpdated; - - public readonly BindableList Rooms = new BindableList(); - - public Action JoinRoomRequested; - - public IBindable InitialRoomsReceived { get; } = new Bindable(true); - - IBindableList IRoomManager.Rooms => Rooms; - - private int currentRoomId; - - public void CreateRoom(Room room, Action onSuccess = null, Action onError = null) - { - room.RoomID.Value ??= Rooms.Select(r => r.RoomID.Value).Where(id => id != null).Select(id => id.Value).DefaultIfEmpty().Max() + 1; - onSuccess?.Invoke(room); - - AddOrUpdateRoom(room); - } - - public void AddOrUpdateRoom(Room room) - { - var existing = Rooms.FirstOrDefault(r => r.RoomID.Value != null && r.RoomID.Value == room.RoomID.Value); - - if (existing != null) - existing.CopyFrom(room); - else - Rooms.Add(room); - - RoomsUpdated?.Invoke(); - } - - public void RemoveRoom(Room room) - { - Rooms.Remove(room); - RoomsUpdated?.Invoke(); - } - - public void ClearRooms() - { - Rooms.Clear(); - RoomsUpdated?.Invoke(); - } - - public void JoinRoom(Room room, string password, Action onSuccess = null, Action onError = null) - { - JoinRoomRequested?.Invoke(room, password); - onSuccess?.Invoke(room); - } - - public void PartRoom() - { - } - - public void AddRooms(int count, RulesetInfo ruleset = null, bool withPassword = false) - { - for (int i = 0; i < count; i++) - { - var room = new Room - { - RoomID = { Value = currentRoomId }, - Position = { Value = currentRoomId }, - Name = { Value = $"Room {currentRoomId}" }, - Host = { Value = new User { Username = "Host" } }, - EndDate = { Value = DateTimeOffset.Now + TimeSpan.FromSeconds(10) }, - Category = { Value = i % 2 == 0 ? RoomCategory.Spotlight : RoomCategory.Normal }, - Password = { Value = withPassword ? "password" : string.Empty } - }; - - if (ruleset != null) - { - room.Playlist.Add(new PlaylistItem - { - Ruleset = { Value = ruleset }, - Beatmap = - { - Value = new BeatmapInfo - { - Metadata = new BeatmapMetadata() - } - } - }); - } - - CreateRoom(room); - - currentRoomId++; - } - } - } -} diff --git a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs index e39711b7ce..defc971eef 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs @@ -71,6 +71,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay drawableComponents.Add(drawable); } - protected virtual IRoomManager CreateRoomManager() => new BasicTestRoomManager(); + protected virtual IRoomManager CreateRoomManager() => new TestRequestHandlingRoomManager(); } } diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRequestHandlingRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRequestHandlingRoomManager.cs new file mode 100644 index 0000000000..4c7fc0e18f --- /dev/null +++ b/osu.Game/Tests/Visual/OnlinePlay/TestRequestHandlingRoomManager.cs @@ -0,0 +1,76 @@ +// 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 osu.Framework.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Online.API; +using osu.Game.Online.Rooms; +using osu.Game.Rulesets; +using osu.Game.Screens.OnlinePlay.Components; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.OnlinePlay +{ + /// + /// A very simple for use in online play test scenes. + /// + public class TestRequestHandlingRoomManager : RoomManager + { + public Action JoinRoomRequested; + + private int currentRoomId; + + private readonly TestRoomRequestsHandler handler = new TestRoomRequestsHandler(); + + [BackgroundDependencyLoader] + private void load(IAPIProvider api, OsuGameBase game) + { + ((DummyAPIAccess)api).HandleRequest = request => handler.HandleRequest(request, api.LocalUser.Value, game); + } + + public override void JoinRoom(Room room, string password = null, Action onSuccess = null, Action onError = null) + { + JoinRoomRequested?.Invoke(room, password); + base.JoinRoom(room, password, onSuccess, onError); + } + + public void AddRooms(int count, RulesetInfo ruleset = null, bool withPassword = false) + { + for (int i = 0; i < count; i++) + { + var room = new Room + { + RoomID = { Value = -currentRoomId }, + Name = { Value = $@"Room {currentRoomId}" }, + Host = { Value = new User { Username = @"Host" } }, + EndDate = { Value = DateTimeOffset.Now + TimeSpan.FromSeconds(10) }, + Category = { Value = i % 2 == 0 ? RoomCategory.Spotlight : RoomCategory.Normal }, + }; + + if (withPassword) + room.Password.Value = @"password"; + + if (ruleset != null) + { + room.Playlist.Add(new PlaylistItem + { + Ruleset = { Value = ruleset }, + Beatmap = + { + Value = new BeatmapInfo + { + Metadata = new BeatmapMetadata() + } + } + }); + } + + CreateRoom(room); + + currentRoomId++; + } + } + } +} From 47ef33e7316bc93b7f001da29a3d9f8196e685d7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 19 Aug 2021 06:28:31 +0300 Subject: [PATCH 1324/2442] Fix playlists lounge sub screen test potentially failing --- .../Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs index aff0e7ba4b..509b62f69f 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs @@ -37,6 +37,7 @@ namespace osu.Game.Tests.Visual.Playlists AddStep("reset mouse", () => InputManager.ReleaseButton(MouseButton.Left)); AddStep("add rooms", () => RoomManager.AddRooms(30)); + AddUntilStep("wait for rooms", () => roomsContainer.Rooms.Count == 30); AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms[0])); @@ -53,6 +54,7 @@ namespace osu.Game.Tests.Visual.Playlists public void TestScrollSelectedIntoView() { AddStep("add rooms", () => RoomManager.AddRooms(30)); + AddUntilStep("wait for rooms", () => roomsContainer.Rooms.Count == 30); AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms[0])); From 5fc29328cf1411bea9bc4f4c093ec32b6849087d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 19 Aug 2021 06:30:22 +0300 Subject: [PATCH 1325/2442] Remove unused using Uh..... --- .../Tests/Visual/OnlinePlay/TestRequestHandlingRoomManager.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRequestHandlingRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRequestHandlingRoomManager.cs index 4c7fc0e18f..d88fd68b20 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/TestRequestHandlingRoomManager.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/TestRequestHandlingRoomManager.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Game.Beatmaps; using osu.Game.Online.API; From 072560ba3e583d5660f3df7d76999bca1264e390 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 19 Aug 2021 07:17:42 +0300 Subject: [PATCH 1326/2442] Remove leftover unused using --- osu.Game/Rulesets/Mods/Mod.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 8c2a8a8e8b..9f3b5eaf5b 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; -using JetBrains.Annotations; using Newtonsoft.Json; using osu.Framework.Bindables; using osu.Framework.Extensions.TypeExtensions; From 6d57a240acf5cd9528b2f688e4a5e4c0388eaaf6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 19 Aug 2021 07:17:22 +0300 Subject: [PATCH 1327/2442] Add animation support for the star rating display --- .../Beatmaps/Drawables/StarRatingDisplay.cs | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs index 25cde5fb82..c239fda455 100644 --- a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs +++ b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; @@ -22,6 +23,7 @@ namespace osu.Game.Beatmaps.Drawables /// public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue { + private readonly bool animated; private readonly Box background; private readonly SpriteIcon starIcon; private readonly OsuSpriteText starsText; @@ -34,6 +36,14 @@ namespace osu.Game.Beatmaps.Drawables set => current.Current = value; } + private readonly Bindable displayedStars = new BindableDouble(); + + /// + /// The currently displayed stars of this display wrapped in a bindable. + /// This bindable gets transformed on change rather than instantaneous, if animation is enabled. + /// + public IBindable DisplayedStars => displayedStars; + [Resolved] private OsuColour colours { get; set; } @@ -45,8 +55,11 @@ namespace osu.Game.Beatmaps.Drawables /// /// The already computed to display. /// The size of the star rating display. - public StarRatingDisplay(StarDifficulty starDifficulty, StarRatingDisplaySize size = StarRatingDisplaySize.Regular) + /// Whether the star rating display will perform transforms on change rather than updating instantaneously. + public StarRatingDisplay(StarDifficulty starDifficulty, StarRatingDisplaySize size = StarRatingDisplaySize.Regular, bool animated = false) { + this.animated = animated; + Current.Value = starDifficulty; AutoSizeAxes = Axes.Both; @@ -112,7 +125,7 @@ namespace osu.Game.Beatmaps.Drawables // see https://github.com/ppy/osu-framework/issues/3271. Font = OsuFont.Torus.With(size: 14.4f, weight: FontWeight.Bold), Shadow = false, - } + }, } } }, @@ -126,12 +139,22 @@ namespace osu.Game.Beatmaps.Drawables Current.BindValueChanged(c => { - starsText.Text = c.NewValue.Stars.ToString("0.00"); + if (animated) + this.TransformBindableTo(displayedStars, c.NewValue.Stars, 750, Easing.OutQuint); + else + displayedStars.Value = c.NewValue.Stars; + }); - background.Colour = colours.ForStarDifficulty(c.NewValue.Stars); + displayedStars.Value = Current.Value.Stars; - starIcon.Colour = c.NewValue.Stars >= 6.5 ? colours.Orange1 : colourProvider?.Background5 ?? Color4Extensions.FromHex("303d47"); - starsText.Colour = c.NewValue.Stars >= 6.5 ? colours.Orange1 : colourProvider?.Background5 ?? Color4.Black.Opacity(0.75f); + displayedStars.BindValueChanged(s => + { + starsText.Text = s.NewValue.ToLocalisableString("0.00"); + + background.Colour = colours.ForStarDifficulty(s.NewValue); + + starIcon.Colour = s.NewValue >= 6.5 ? colours.Orange1 : colourProvider?.Background5 ?? Color4Extensions.FromHex("303d47"); + starsText.Colour = s.NewValue >= 6.5 ? colours.Orange1 : colourProvider?.Background5 ?? Color4.Black.Opacity(0.75f); }, true); } } From 25e6317e7f1a51dc6cd15edbafaccb10d499a4d1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 19 Aug 2021 07:18:02 +0300 Subject: [PATCH 1328/2442] Use animated star display in beatmap info wedge and synchronise bar --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 83 +++++++-------------- 1 file changed, 26 insertions(+), 57 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 14c32f2348..1e1f362d71 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -21,7 +21,6 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; using osu.Framework.Logging; using osu.Game.Configuration; @@ -157,7 +156,7 @@ namespace osu.Game.Screens.Select public BeatmapSetOnlineStatusPill StatusPill { get; private set; } public FillFlowContainer MapperContainer { get; private set; } - private DifficultyColourBar difficultyColourBar; + private Container difficultyColourBar; private StarRatingDisplay starRatingDisplay; private ILocalisedBindableString titleBinding; @@ -182,7 +181,7 @@ namespace osu.Game.Screens.Select private IBindable starDifficulty; [BackgroundDependencyLoader] - private void load(LocalisationManager localisation, BeatmapDifficultyCache difficultyCache) + private void load(OsuColour colours, LocalisationManager localisation, BeatmapDifficultyCache difficultyCache) { var beatmapInfo = beatmap.BeatmapInfo; var metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); @@ -194,10 +193,26 @@ namespace osu.Game.Screens.Select Children = new Drawable[] { - difficultyColourBar = new DifficultyColourBar + difficultyColourBar = new Container { RelativeSizeAxes = Axes.Y, - Width = 20, + Width = 20f, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Width = 0.7f, + }, + new Box + { + RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.Both, + Alpha = 0.5f, + X = 0.7f, + Width = 1 - 0.7f, + } + } }, new FillFlowContainer { @@ -230,7 +245,7 @@ namespace osu.Game.Screens.Select Shear = wedged_container_shear, Children = new Drawable[] { - starRatingDisplay = new StarRatingDisplay(default) + starRatingDisplay = new StarRatingDisplay(default, animated: true) { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, @@ -292,11 +307,14 @@ namespace osu.Game.Screens.Select titleBinding.BindValueChanged(_ => setMetadata(metadata.Source)); artistBinding.BindValueChanged(_ => setMetadata(metadata.Source), true); + starRatingDisplay.DisplayedStars.BindValueChanged(s => + { + difficultyColourBar.Colour = colours.ForStarDifficulty(s.NewValue); + }, true); + starDifficulty = difficultyCache.GetBindableDifficulty(beatmapInfo, (cancellationSource = new CancellationTokenSource()).Token); starDifficulty.BindValueChanged(s => { - difficultyColourBar.Current.Value = s.NewValue ?? default; - starRatingDisplay.FadeIn(transition_duration); starRatingDisplay.Current.Value = s.NewValue ?? default; }); @@ -480,55 +498,6 @@ namespace osu.Game.Screens.Select }; } } - - public class DifficultyColourBar : Container, IHasCurrentValue - { - private readonly BindableWithCurrent current = new BindableWithCurrent(); - - public Bindable Current - { - get => current.Current; - set => current.Current = value; - } - - [Resolved] - private OsuColour colours { get; set; } - - [BackgroundDependencyLoader] - private void load() - { - const float full_opacity_ratio = 0.7f; - - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Width = full_opacity_ratio, - }, - new Box - { - RelativeSizeAxes = Axes.Both, - RelativePositionAxes = Axes.Both, - Alpha = 0.5f, - X = full_opacity_ratio, - Width = 1 - full_opacity_ratio, - } - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - Current.BindValueChanged(c => - { - this.FadeColour(colours.ForStarDifficulty(c.NewValue.Stars), transition_duration, Easing.OutQuint); - }, true); - - FinishTransforms(true); - } - } } } } From 65c0ae757bfeb80bf3fe324d80a9cb19dc6d0fcc Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 19 Aug 2021 07:18:18 +0300 Subject: [PATCH 1329/2442] Merge spectrum test case --- .../TestSceneStarRatingDisplay.cs | 28 +------------------ 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs index 052251d5a8..2806e6d347 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs @@ -3,7 +3,6 @@ using System.Linq; using NUnit.Framework; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Utils; @@ -50,32 +49,7 @@ namespace osu.Game.Tests.Visual.UserInterface { StarRatingDisplay starRating = null; - BindableDouble starRatingNumeric; - - AddStep("load display", () => - { - Child = starRating = new StarRatingDisplay(new StarDifficulty(5.55, 1)) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(3f), - }; - }); - - AddStep("transform over spectrum", () => - { - starRatingNumeric = new BindableDouble(); - starRatingNumeric.BindValueChanged(val => starRating.Current.Value = new StarDifficulty(val.NewValue, 1)); - this.TransformBindableTo(starRatingNumeric, 10, 10000, Easing.OutQuint); - }); - } - - [Test] - public void TestChangingStarRatingDisplay() - { - StarRatingDisplay starRating = null; - - AddStep("load display", () => Child = starRating = new StarRatingDisplay(new StarDifficulty(5.55, 1)) + AddStep("load display", () => Child = starRating = new StarRatingDisplay(new StarDifficulty(5.55, 1), animated: true) { Anchor = Anchor.Centre, Origin = Anchor.Centre, From 786beb975941b61a98e1e282f1ad0b16a34a3943 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Aug 2021 14:14:47 +0900 Subject: [PATCH 1330/2442] Fix missing initial state test step --- .../Visual/Multiplayer/TestSceneGameplayChatDisplay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs index 51960680d6..08aa967c61 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs @@ -85,6 +85,8 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestFocusOnTabKeyWhenExpanded() { + setLocalUserPlaying(true); + assertChatFocused(false); AddStep("press tab", () => InputManager.Key(Key.Tab)); assertChatFocused(true); From 6d00ea3375d80c953b8978a0b9505226c4506647 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Aug 2021 14:19:59 +0900 Subject: [PATCH 1331/2442] Allow toggling focus via binding --- .../Multiplayer/TestSceneGameplayChatDisplay.cs | 15 +++++++++++++++ .../Graphics/UserInterface/FocusedTextBox.cs | 2 ++ osu.Game/Input/Bindings/GlobalActionContainer.cs | 6 +++--- .../Multiplayer/GameplayChatDisplay.cs | 16 ++++++++++++---- 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs index 08aa967c61..eadfc9b279 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs @@ -107,6 +107,21 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("is not visible", () => !chatDisplay.IsPresent); } + [Test] + public void TestFocusToggleViaAction() + { + AddStep("set not expanded", () => chatDisplay.Expanded.Value = false); + AddUntilStep("is not visible", () => !chatDisplay.IsPresent); + + AddStep("press tab", () => InputManager.Key(Key.Tab)); + assertChatFocused(true); + AddUntilStep("is visible", () => chatDisplay.IsPresent); + + AddStep("press tab", () => InputManager.Key(Key.Tab)); + assertChatFocused(false); + AddUntilStep("is not visible", () => !chatDisplay.IsPresent); + } + private void assertChatFocused(bool isFocused) => AddAssert($"chat {(isFocused ? "focused" : "not focused")}", () => textBox.HasFocus == isFocused); diff --git a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs index f77a3109c9..2705caccff 100644 --- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs +++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs @@ -25,6 +25,8 @@ namespace osu.Game.Graphics.UserInterface if (allowImmediateFocus) GetContainingInputManager().ChangeFocus(this); } + public new void KillFocus() => base.KillFocus(); + public bool HoldFocus { get => allowImmediateFocus && focus; diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 66edb25d64..0176a00e9d 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -90,7 +90,7 @@ namespace osu.Game.Input.Bindings new KeyBinding(InputKey.Left, GlobalAction.SeekReplayBackward), new KeyBinding(InputKey.Right, GlobalAction.SeekReplayForward), new KeyBinding(InputKey.Control, GlobalAction.HoldForHUD), - new KeyBinding(InputKey.Tab, GlobalAction.FocusChatInput), + new KeyBinding(InputKey.Tab, GlobalAction.ToggleChatFocus), }; public IEnumerable SongSelectKeyBindings => new[] @@ -282,7 +282,7 @@ namespace osu.Game.Input.Bindings [Description("Seek replay backward")] SeekReplayBackward, - [Description("Focus chat")] - FocusChatInput + [Description("Toggle chat focus")] + ToggleChatFocus } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs index 5d494379e6..e2f135d436 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs @@ -71,11 +71,19 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { switch (action) { - case GlobalAction.FocusChatInput: - expandedFromTextboxFocus.Value = true; + case GlobalAction.ToggleChatFocus: + if (Textbox.HasFocus) + { + Schedule(() => Textbox.KillFocus()); + } + else + { + expandedFromTextboxFocus.Value = true; + + // schedule required to ensure the textbox has become present from above bindable update. + Schedule(() => Textbox.TakeFocus()); + } - // schedule required to ensure the textbox has become present from above bindable update. - Schedule(() => Textbox.TakeFocus()); return true; } From 6bfae25cda203f6bf5482281b7aa3f4568f52225 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 19 Aug 2021 08:30:27 +0300 Subject: [PATCH 1332/2442] Apply 5px vertical spacing on fill flow Regressed, was margin { bottom = 5f } from the star rating display creation method, which I've partly inlined. --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 1e1f362d71..17a26eb7ee 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -243,6 +243,7 @@ namespace osu.Game.Screens.Select Padding = new MarginPadding { Top = 14, Right = shear_width / 2 }, AutoSizeAxes = Axes.Both, Shear = wedged_container_shear, + Spacing = new Vector2(0f, 5f), Children = new Drawable[] { starRatingDisplay = new StarRatingDisplay(default, animated: true) From 6469eaa427cf038725f05e93d88065e5f4135449 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Aug 2021 15:52:51 +0900 Subject: [PATCH 1333/2442] Fix background not updated in room --- .../Lounge/Components/DrawableRoom.cs | 10 +++--- .../OnlinePlay/Match/DrawableMatchRoom.cs | 2 ++ .../OnlinePlay/Match/RoomBackgroundSprite.cs | 33 +++++++++++++++++++ 3 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 osu.Game/Screens/OnlinePlay/Match/RoomBackgroundSprite.cs diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index c5b8bffbc6..bbec9aa21d 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -61,7 +61,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components [BackgroundDependencyLoader] private void load(OverlayColourProvider colours) { - InternalChildren = new Drawable[] + InternalChildren = new[] { // This resolves internal 1px gaps due to applying the (parenting) corner radius and masking across multiple filling background sprites. new Box @@ -69,10 +69,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components RelativeSizeAxes = Axes.Both, Colour = colours.Background5, }, - new OnlinePlayBackgroundSprite + CreateBackground().With(d => { - RelativeSizeAxes = Axes.Both - }, + d.RelativeSizeAxes = Axes.Both; + }), new Container { Name = @"Room content", @@ -264,6 +264,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } + protected virtual Drawable CreateBackground() => new OnlinePlayBackgroundSprite(); + private class RoomNameText : OsuSpriteText { [Resolved(typeof(Room), nameof(Online.Rooms.Room.Name))] diff --git a/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs index b3e6292f45..0924773338 100644 --- a/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs @@ -58,5 +58,7 @@ namespace osu.Game.Screens.OnlinePlay.Match if (editButton != null) host.BindValueChanged(h => editButton.Alpha = h.NewValue?.Equals(api.LocalUser.Value) == true ? 1 : 0, true); } + + protected override Drawable CreateBackground() => new RoomBackgroundSprite(); } } diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomBackgroundSprite.cs b/osu.Game/Screens/OnlinePlay/Match/RoomBackgroundSprite.cs new file mode 100644 index 0000000000..97262dd229 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Match/RoomBackgroundSprite.cs @@ -0,0 +1,33 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Beatmaps.Drawables; + +namespace osu.Game.Screens.OnlinePlay.Match +{ + public class RoomBackgroundSprite : RoomSubScreenComposite + { + protected readonly BeatmapSetCoverType BeatmapSetCoverType; + private UpdateableBeatmapBackgroundSprite sprite; + + public RoomBackgroundSprite(BeatmapSetCoverType beatmapSetCoverType = BeatmapSetCoverType.Cover) + { + BeatmapSetCoverType = beatmapSetCoverType; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = sprite = new UpdateableBeatmapBackgroundSprite(BeatmapSetCoverType) { RelativeSizeAxes = Axes.Both }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + SelectedItem.BindValueChanged(item => sprite.Beatmap.Value = item.NewValue?.Beatmap.Value, true); + } + } +} From 1fd746524d1963f96333cec766110acc5cbc4e2f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Aug 2021 16:09:17 +0900 Subject: [PATCH 1334/2442] Remove realtime room category, fix end-date showing for realtime --- .../Visual/Multiplayer/TestSceneDrawableRoom.cs | 8 +------- osu.Game/Online/Rooms/RoomCategory.cs | 1 - .../OnlinePlay/Lounge/Components/DrawableRoom.cs | 12 ++++++++++-- .../Multiplayer/MultiplayerLoungeSubScreen.cs | 1 - 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index 489ece3271..8ca578b592 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -108,12 +108,6 @@ namespace osu.Game.Tests.Visual.Multiplayer EndDate = { Value = DateTimeOffset.Now }, }), createDrawableRoom(new Room - { - Name = { Value = "Room 4 (realtime)" }, - Status = { Value = new RoomStatusOpen() }, - Category = { Value = RoomCategory.Realtime }, - }), - createDrawableRoom(new Room { Name = { Value = "Room 4 (spotlight)" }, Status = { Value = new RoomStatusOpen() }, @@ -134,7 +128,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = { Value = "Room with password" }, Status = { Value = new RoomStatusOpen() }, - Category = { Value = RoomCategory.Realtime }, + Type = { Value = MatchType.HeadToHead }, })); AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType().Single().Alpha)); diff --git a/osu.Game/Online/Rooms/RoomCategory.cs b/osu.Game/Online/Rooms/RoomCategory.cs index bb9f1298d3..a1dcfa5fd9 100644 --- a/osu.Game/Online/Rooms/RoomCategory.cs +++ b/osu.Game/Online/Rooms/RoomCategory.cs @@ -8,6 +8,5 @@ namespace osu.Game.Online.Rooms // used for osu-web deserialization so names shouldn't be changed. Normal, Spotlight, - Realtime, } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index bbec9aa21d..106211c833 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -34,12 +34,14 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components protected Container ButtonsContainer { get; private set; } + private readonly Bindable roomType = new Bindable(); private readonly Bindable roomCategory = new Bindable(); private readonly Bindable hasPassword = new Bindable(); private RecentParticipantsList recentParticipantsList; private RoomSpecialCategoryPill specialCategoryPill; private PasswordProtectedIcon passwordIcon; + private EndDateInfo endDateInfo; public DrawableRoom(Room room) { @@ -144,10 +146,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft }, - new EndDateInfo + endDateInfo = new EndDateInfo { Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft + Origin = Anchor.CentreLeft, }, } }, @@ -238,6 +240,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components specialCategoryPill.Hide(); }, true); + roomType.BindTo(Room.Type); + roomType.BindValueChanged(t => + { + endDateInfo.Alpha = t.NewValue == MatchType.Playlists ? 1 : 0; + }, true); + hasPassword.BindTo(Room.HasPassword); hasPassword.BindValueChanged(v => passwordIcon.Alpha = v.NewValue ? 1 : 0, true); } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs index d152fc3913..6c3dfe7382 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs @@ -50,7 +50,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override Room CreateNewRoom() => new Room { Name = { Value = $"{api.LocalUser}'s awesome room" }, - Category = { Value = RoomCategory.Realtime }, Type = { Value = MatchType.HeadToHead }, }; From c31af96f1d18fb858bc4f5163439458e5dec2811 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Aug 2021 16:40:27 +0900 Subject: [PATCH 1335/2442] Pass room into RoomSettingsOverlay --- .../TestScenePlaylistsMatchSettingsOverlay.cs | 7 ++- .../Match/Components/RoomSettingsOverlay.cs | 11 +++-- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 5 ++- .../Match/MultiplayerMatchSettingsOverlay.cs | 44 +++++++++++-------- .../Multiplayer/MultiplayerMatchSubScreen.cs | 17 ++----- .../Playlists/PlaylistsRoomSettingsOverlay.cs | 29 +++++++----- .../Playlists/PlaylistsRoomSubScreen.cs | 4 +- 7 files changed, 66 insertions(+), 51 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs index 04b44efd32..48222e6b94 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs @@ -28,7 +28,7 @@ namespace osu.Game.Tests.Visual.Playlists { SelectedRoom.Value = new Room(); - Child = settings = new TestRoomSettings + Child = settings = new TestRoomSettings(SelectedRoom.Value) { RelativeSizeAxes = Axes.Both, State = { Value = Visibility.Visible } @@ -118,6 +118,11 @@ namespace osu.Game.Tests.Visual.Playlists public OsuDropdown DurationField => ((MatchSettings)Settings).DurationField; public OsuSpriteText ErrorText => ((MatchSettings)Settings).ErrorText; + + public TestRoomSettings(Room room) + : base(room) + { + } } private class TestDependencies : OnlinePlayTestSceneDependencies diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs index 80a22880d4..7a8839cdad 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs @@ -9,6 +9,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; +using osu.Game.Online.Rooms; using osuTK; using osuTK.Graphics; @@ -27,8 +28,12 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components protected abstract bool IsLoading { get; } - protected RoomSettingsOverlay() + private readonly Room room; + + protected RoomSettingsOverlay(Room room) { + this.room = room; + RelativeSizeAxes = Axes.Both; Masking = true; CornerRadius = 10; @@ -37,12 +42,12 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components [BackgroundDependencyLoader] private void load() { - Add(Settings = CreateSettings()); + Add(Settings = CreateSettings(room)); } protected abstract void SelectBeatmap(); - protected abstract OnlinePlayComposite CreateSettings(); + protected abstract OnlinePlayComposite CreateSettings(Room room); protected override void PopIn() { diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 81fae558c8..389eba0d82 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -184,7 +184,7 @@ namespace osu.Game.Screens.OnlinePlay.Match RelativeSizeAxes = Axes.Both, // Resolves 1px masking errors between the settings overlay and the room panel. Padding = new MarginPadding(-1), - Child = settingsOverlay = CreateRoomSettingsOverlay() + Child = settingsOverlay = CreateRoomSettingsOverlay(Room) } }, }, @@ -406,7 +406,8 @@ namespace osu.Game.Screens.OnlinePlay.Match /// /// Creates the room settings overlay. /// - protected abstract RoomSettingsOverlay CreateRoomSettingsOverlay(); + /// + protected abstract RoomSettingsOverlay CreateRoomSettingsOverlay(Room room); private class UserModSelectOverlay : LocalPlayerModSelectOverlay { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 3a95ac00a6..cd78a94765 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -37,15 +37,19 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match protected override bool IsLoading => ongoingOperationTracker.InProgress.Value; + public MultiplayerMatchSettingsOverlay(Room room) + : base(room) + { + } + protected override void SelectBeatmap() => settings.SelectBeatmap(); - protected override OnlinePlayComposite CreateSettings() - => settings = new MatchSettings - { - RelativeSizeAxes = Axes.Both, - RelativePositionAxes = Axes.Y, - SettingsApplied = Hide - }; + protected override OnlinePlayComposite CreateSettings(Room room) => settings = new MatchSettings(room) + { + RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.Y, + SettingsApplied = Hide + }; protected class MatchSettings : OnlinePlayComposite { @@ -73,9 +77,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match [Resolved] private MultiplayerClient client { get; set; } - [Resolved] - private Bindable currentRoom { get; set; } - [Resolved] private Bindable beatmap { get; set; } @@ -90,6 +91,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match [CanBeNull] private IDisposable applyingSettingsOperation; + private readonly Room room; + + public MatchSettings(Room room) + { + this.room = room; + } + [BackgroundDependencyLoader] private void load(OsuColour colours) { @@ -319,24 +327,24 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match client.ChangeSettings(name: NameField.Text, password: PasswordTextBox.Text, matchType: TypePicker.Current.Value).ContinueWith(t => Schedule(() => { if (t.IsCompletedSuccessfully) - onSuccess(currentRoom.Value); + onSuccess(room); else onError(t.Exception?.AsSingular().Message ?? "Error changing settings."); })); } else { - currentRoom.Value.Name.Value = NameField.Text; - currentRoom.Value.Availability.Value = AvailabilityPicker.Current.Value; - currentRoom.Value.Type.Value = TypePicker.Current.Value; - currentRoom.Value.Password.Value = PasswordTextBox.Current.Value; + room.Name.Value = NameField.Text; + room.Availability.Value = AvailabilityPicker.Current.Value; + room.Type.Value = TypePicker.Current.Value; + room.Password.Value = PasswordTextBox.Current.Value; if (int.TryParse(MaxParticipantsField.Text, out int max)) - currentRoom.Value.MaxParticipants.Value = max; + room.MaxParticipants.Value = max; else - currentRoom.Value.MaxParticipants.Value = null; + room.MaxParticipants.Value = null; - manager?.CreateRoom(currentRoom.Value, onSuccess, onError); + manager?.CreateRoom(room, onSuccess, onError); } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 3f5837cbbe..833e6b24e0 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -48,9 +48,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private OngoingOperationTracker ongoingOperationTracker { get; set; } - [Resolved] - private Bindable currentRoom { get; set; } // Todo: This should not exist. - private readonly IBindable isConnected = new Bindable(); [CanBeNull] @@ -82,16 +79,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer handleRoomLost(); }, true); - currentRoom.BindValueChanged(room => - { - if (room.NewValue == null) - { - // the room has gone away. - // this could mean something happened during the join process, or an external connection issue occurred. - // one specific scenario is where the underlying room is created, but the signalr server returns an error during the join process. this triggers a PartRoom operation (see https://github.com/ppy/osu/blob/7654df94f6f37b8382be7dfcb4f674e03bd35427/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs#L97) - handleRoomLost(); - } - }, true); + if (client.Room == null) + handleRoomLost(); } protected override Drawable CreateMainContent() => new GridContainer @@ -223,7 +212,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer OnSpectateClick = onSpectateClick }; - protected override RoomSettingsOverlay CreateRoomSettingsOverlay() => new MultiplayerMatchSettingsOverlay(); + protected override RoomSettingsOverlay CreateRoomSettingsOverlay(Room room) => new MultiplayerMatchSettingsOverlay(room); protected override void UpdateMods() { diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs index 3eea59006a..7384c60888 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Specialized; using Humanizer; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -32,15 +31,19 @@ namespace osu.Game.Screens.OnlinePlay.Playlists protected override bool IsLoading => settings.IsLoading; // should probably be replaced with an OngoingOperationTracker. + public PlaylistsRoomSettingsOverlay(Room room) + : base(room) + { + } + protected override void SelectBeatmap() => settings.SelectBeatmap(); - protected override OnlinePlayComposite CreateSettings() - => settings = new MatchSettings - { - RelativeSizeAxes = Axes.Both, - RelativePositionAxes = Axes.Y, - EditPlaylist = () => EditPlaylist?.Invoke() - }; + protected override OnlinePlayComposite CreateSettings(Room room) => settings = new MatchSettings(room) + { + RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.Y, + EditPlaylist = () => EditPlaylist?.Invoke() + }; protected class MatchSettings : OnlinePlayComposite { @@ -66,8 +69,12 @@ namespace osu.Game.Screens.OnlinePlay.Playlists [Resolved(CanBeNull = true)] private IRoomManager manager { get; set; } - [Resolved] - private Bindable currentRoom { get; set; } + private readonly Room room; + + public MatchSettings(Room room) + { + this.room = room; + } [BackgroundDependencyLoader] private void load(OsuColour colours) @@ -333,7 +340,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists Duration.Value = DurationField.Current.Value; - manager?.CreateRoom(currentRoom.Value, onSuccess, onError); + manager?.CreateRoom(room, onSuccess, onError); loadingLayer.Show(); } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 27de781d5f..cd38e658a2 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -50,7 +50,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists if (idleTracker != null) isIdle.BindTo(idleTracker.IsIdle); - AddInternal(selectionPollingComponent = new SelectionPollingComponent()); + AddInternal(selectionPollingComponent = new SelectionPollingComponent(Room)); } protected override void LoadComplete() @@ -183,7 +183,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists OnStart = StartPlay }; - protected override RoomSettingsOverlay CreateRoomSettingsOverlay() => new PlaylistsRoomSettingsOverlay + protected override RoomSettingsOverlay CreateRoomSettingsOverlay(Room room) => new PlaylistsRoomSettingsOverlay(room) { EditPlaylist = () => { From 3a1154c00e85c834789d2e4772d0efc8017c1ba3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Aug 2021 16:40:39 +0900 Subject: [PATCH 1336/2442] Pass room into SelectionPollingComponent --- .../Components/SelectionPollingComponent.cs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs index 88d9469f8c..0769d0747b 100644 --- a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs +++ b/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs @@ -3,7 +3,6 @@ using System.Threading.Tasks; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Game.Online.Rooms; namespace osu.Game.Screens.OnlinePlay.Components @@ -13,20 +12,14 @@ namespace osu.Game.Screens.OnlinePlay.Components /// public class SelectionPollingComponent : RoomPollingComponent { - [Resolved] - private Bindable selectedRoom { get; set; } - [Resolved] private IRoomManager roomManager { get; set; } - [BackgroundDependencyLoader] - private void load() + private readonly Room room; + + public SelectionPollingComponent(Room room) { - selectedRoom.BindValueChanged(_ => - { - if (IsLoaded) - PollImmediately(); - }); + this.room = room; } private GetRoomRequest pollReq; @@ -36,13 +29,13 @@ namespace osu.Game.Screens.OnlinePlay.Components if (!API.IsLoggedIn) return base.Poll(); - if (selectedRoom.Value?.RoomID.Value == null) + if (room.RoomID.Value == null) return base.Poll(); var tcs = new TaskCompletionSource(); pollReq?.Cancel(); - pollReq = new GetRoomRequest(selectedRoom.Value.RoomID.Value.Value); + pollReq = new GetRoomRequest(room.RoomID.Value.Value); pollReq.Success += result => { From 00be7f4cca50f5136a5c73d024b9eaa47cbcae4d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Aug 2021 16:49:08 +0900 Subject: [PATCH 1337/2442] Make RoomsContainer/DrawableRoom not resolve via DI --- .../Multiplayer/TestSceneDrawableRoom.cs | 11 ++++--- .../TestSceneLoungeRoomsContainer.cs | 1 + .../Lounge/Components/RoomsContainer.cs | 31 ++++++++----------- .../OnlinePlay/Lounge/DrawableLoungeRoom.cs | 15 +++++---- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 3 +- 5 files changed, 30 insertions(+), 31 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index 8ca578b592..3973dc57b2 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -24,12 +24,11 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneDrawableRoom : OsuTestScene { - [Cached] - private readonly Bindable selectedRoom = new Bindable(); - [Cached] protected readonly OverlayColourProvider ColourProvider = new OverlayColourProvider(OverlayColourScheme.Plum); + private readonly Bindable selectedRoom = new Bindable(); + [Test] public void TestMultipleStatuses() { @@ -153,7 +152,11 @@ namespace osu.Game.Tests.Visual.Multiplayer })); } - return new DrawableLoungeRoom(room) { MatchingFilter = true }; + return new DrawableLoungeRoom(room) + { + MatchingFilter = true, + SelectedRoom = { BindTarget = selectedRoom } + }; } } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs index b23638e514..66298e9b3d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs @@ -30,6 +30,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Anchor = Anchor.Centre, Origin = Anchor.Centre, Width = 0.5f, + SelectedRoom = { BindTarget = SelectedRoom } }; }); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index be5558ed0b..76cb02199b 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -23,16 +23,13 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { public class RoomsContainer : CompositeDrawable, IKeyBindingHandler { - private readonly IBindableList rooms = new BindableList(); - - private readonly FillFlowContainer roomFlow; + public readonly Bindable SelectedRoom = new Bindable(); + public readonly Bindable Filter = new Bindable(); public IReadOnlyList Rooms => roomFlow.FlowingChildren.Cast().ToArray(); - public readonly Bindable Filter = new Bindable(); - - [Resolved] - private Bindable selectedRoom { get; set; } + private readonly IBindableList rooms = new BindableList(); + private readonly FillFlowContainer roomFlow; [Resolved] private IRoomManager roomManager { get; set; } @@ -112,9 +109,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private void addRooms(IEnumerable rooms) { foreach (var room in rooms) - { - roomFlow.Add(new DrawableLoungeRoom(room)); - } + roomFlow.Add(new DrawableLoungeRoom(room) { SelectedRoom = { BindTarget = SelectedRoom } }); applyFilterCriteria(Filter?.Value); } @@ -126,8 +121,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components roomFlow.RemoveAll(d => d.Room == r); // selection may have a lease due to being in a sub screen. - if (!selectedRoom.Disabled) - selectedRoom.Value = null; + if (!SelectedRoom.Disabled) + SelectedRoom.Value = null; } } @@ -139,8 +134,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components protected override bool OnClick(ClickEvent e) { - if (!selectedRoom.Disabled) - selectedRoom.Value = null; + if (!SelectedRoom.Disabled) + SelectedRoom.Value = null; return base.OnClick(e); } @@ -202,26 +197,26 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private void selectNext(int direction) { - if (selectedRoom.Disabled) + if (SelectedRoom.Disabled) return; var visibleRooms = Rooms.AsEnumerable().Where(r => r.IsPresent); Room room; - if (selectedRoom.Value == null) + if (SelectedRoom.Value == null) room = visibleRooms.FirstOrDefault()?.Room; else { if (direction < 0) visibleRooms = visibleRooms.Reverse(); - room = visibleRooms.SkipWhile(r => r.Room != selectedRoom.Value).Skip(1).FirstOrDefault()?.Room; + room = visibleRooms.SkipWhile(r => r.Room != SelectedRoom.Value).Skip(1).FirstOrDefault()?.Room; } // we already have a valid selection only change selection if we still have a room to switch to. if (room != null) - selectedRoom.Value = room; + SelectedRoom.Value = room; } #endregion diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index 4e106b844c..7ff3298829 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -34,11 +34,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private const float transition_duration = 60; private const float selection_border_width = 4; - [Resolved(canBeNull: true)] - private LoungeSubScreen lounge { get; set; } + public readonly Bindable SelectedRoom = new Bindable(); [Resolved(canBeNull: true)] - private Bindable selectedRoom { get; set; } + private LoungeSubScreen lounge { get; set; } private Sample sampleSelect; private Sample sampleJoin; @@ -89,7 +88,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge else Alpha = 0; - selectedRoom.BindValueChanged(updateSelectedRoom, true); + SelectedRoom.BindValueChanged(updateSelectedRoom, true); } private void updateSelectedRoom(ValueChangedEvent selected) @@ -135,7 +134,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge public bool OnPressed(GlobalAction action) { - if (selectedRoom.Value != Room) + if (SelectedRoom.Value != Room) return false; switch (action) @@ -152,14 +151,14 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { } - protected override bool ShouldBeConsideredForInput(Drawable child) => selectedRoom.Value == Room || child is HoverSounds; + protected override bool ShouldBeConsideredForInput(Drawable child) => SelectedRoom.Value == Room || child is HoverSounds; protected override bool OnClick(ClickEvent e) { - if (Room != selectedRoom.Value) + if (Room != SelectedRoom.Value) { sampleSelect?.Play(); - selectedRoom.Value = Room; + SelectedRoom.Value = Room; return true; } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 8bed3d6049..677b9c0782 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -159,7 +159,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge ScrollbarOverlapsContent = false, Child = roomsContainer = new RoomsContainer { - Filter = { BindTarget = filter } + Filter = { BindTarget = filter }, + SelectedRoom = { BindTarget = selectedRoom } } }, } From 4d1897c6b42f54657ef0025df8526c1ea1ff9477 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Aug 2021 16:49:53 +0900 Subject: [PATCH 1338/2442] Remove unnecessary resolve into ListingPollingComponent --- .../Screens/OnlinePlay/Components/ListingPollingComponent.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs index 1387b5a671..daac6a66cd 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs @@ -20,9 +20,6 @@ namespace osu.Game.Screens.OnlinePlay.Components public readonly Bindable Filter = new Bindable(); - [Resolved] - private Bindable selectedRoom { get; set; } - [BackgroundDependencyLoader] private void load() { From 6b6d52c23b99fd981725d6f79ea41e4210a75735 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Aug 2021 16:53:07 +0900 Subject: [PATCH 1339/2442] Add dependency container to RoomSubScreen --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 389eba0d82..487ec9bb49 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -239,6 +239,14 @@ namespace osu.Game.Screens.OnlinePlay.Match UserMods.BindValueChanged(_ => Scheduler.AddOnce(UpdateMods)); } + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + return new CachedModelDependencyContainer(base.CreateChildDependencies(parent)) + { + Model = { Value = Room } + }; + } + public override bool OnBackButton() { if (Room.RoomID.Value == null) From da8a1692d9cbdcf8ba24dde2ac30e43cfc6ccf11 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Aug 2021 17:40:59 +0900 Subject: [PATCH 1340/2442] Add test coverage of multiplayer room/state messagepack serialisation --- ...TestMultiplayerMessagePackSerialization.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 osu.Game.Tests/Online/TestMultiplayerMessagePackSerialization.cs diff --git a/osu.Game.Tests/Online/TestMultiplayerMessagePackSerialization.cs b/osu.Game.Tests/Online/TestMultiplayerMessagePackSerialization.cs new file mode 100644 index 0000000000..5491774e26 --- /dev/null +++ b/osu.Game.Tests/Online/TestMultiplayerMessagePackSerialization.cs @@ -0,0 +1,72 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using MessagePack; +using NUnit.Framework; +using osu.Game.Online; +using osu.Game.Online.Multiplayer; +using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; + +namespace osu.Game.Tests.Online +{ + [TestFixture] + public class TestMultiplayerMessagePackSerialization + { + [Test] + public void TestSerialiseRoom() + { + var room = new MultiplayerRoom(1) + { + MatchState = new TeamVersusRoomState() + }; + + var serialized = MessagePackSerializer.Serialize(room); + + var deserialized = MessagePackSerializer.Deserialize(serialized); + + Assert.IsTrue(deserialized.MatchState is TeamVersusRoomState); + } + + [Test] + public void TestSerialiseUserStateExpected() + { + var state = new TeamVersusUserState(); + + var serialized = MessagePackSerializer.Serialize(typeof(MatchUserState), state); + var deserialized = MessagePackSerializer.Deserialize(serialized); + + Assert.IsTrue(deserialized is TeamVersusUserState); + } + + [Test] + public void TestSerialiseUnionFailsWithSingalR() + { + var state = new TeamVersusUserState(); + + // SignalR serialises using the actual type, rather than a base specification. + var serialized = MessagePackSerializer.Serialize(typeof(TeamVersusUserState), state); + + // works with explicit type specified. + MessagePackSerializer.Deserialize(serialized); + + // fails with base (union) type. + Assert.Throws(() => MessagePackSerializer.Deserialize(serialized)); + } + + [Test] + public void TestSerialiseUnionSucceedsWithWorkaround() + { + var state = new TeamVersusUserState(); + + // SignalR serialises using the actual type, rather than a base specification. + var serialized = MessagePackSerializer.Serialize(typeof(TeamVersusUserState), state, SignalRUnionWorkaroundResolver.OPTIONS); + + // works with explicit type specified. + MessagePackSerializer.Deserialize(serialized); + + // works with custom resolver. + var deserialized = MessagePackSerializer.Deserialize(serialized, SignalRUnionWorkaroundResolver.OPTIONS); + Assert.IsTrue(deserialized is TeamVersusUserState); + } + } +} From fa01e4fad2ba730ee7e0d73c47d3f929ec979b7e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Aug 2021 17:41:50 +0900 Subject: [PATCH 1341/2442] Add workaround for SignalR union serialisation --- osu.Game/Online/HubClientConnector.cs | 7 ++- .../Online/SignalRUnionWorkaroundResolver.cs | 61 +++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Online/SignalRUnionWorkaroundResolver.cs diff --git a/osu.Game/Online/HubClientConnector.cs b/osu.Game/Online/HubClientConnector.cs index d2dba8a402..e9d6960c71 100644 --- a/osu.Game/Online/HubClientConnector.cs +++ b/osu.Game/Online/HubClientConnector.cs @@ -148,7 +148,12 @@ namespace osu.Game.Online }); if (RuntimeInfo.SupportsJIT && preferMessagePack) - builder.AddMessagePackProtocol(); + { + builder.AddMessagePackProtocol(options => + { + options.SerializerOptions = SignalRUnionWorkaroundResolver.OPTIONS; + }); + } else { // eventually we will precompile resolvers for messagepack, but this isn't working currently diff --git a/osu.Game/Online/SignalRUnionWorkaroundResolver.cs b/osu.Game/Online/SignalRUnionWorkaroundResolver.cs new file mode 100644 index 0000000000..e44da044cc --- /dev/null +++ b/osu.Game/Online/SignalRUnionWorkaroundResolver.cs @@ -0,0 +1,61 @@ +// 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 MessagePack; +using MessagePack.Formatters; +using MessagePack.Resolvers; +using osu.Game.Online.Multiplayer; +using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; + +namespace osu.Game.Online +{ + /// + /// Handles SignalR being unable to comprehend [Union] types correctly by redirecting to a known base (union) type. + /// See https://github.com/dotnet/aspnetcore/issues/7298. + /// + public class SignalRUnionWorkaroundResolver : IFormatterResolver + { + public static readonly MessagePackSerializerOptions OPTIONS = + MessagePackSerializerOptions.Standard.WithResolver(new SignalRUnionWorkaroundResolver()); + + private static readonly Dictionary formatter_map = new Dictionary + { + { typeof(TeamVersusUserState), new TypeRedirectingFormatter() }, + { typeof(TeamVersusRoomState), new TypeRedirectingFormatter() }, + { typeof(ChangeTeamRequest), new TypeRedirectingFormatter() }, + + // These should not be required. The fallback should work. But something is weird with the way caching is done. + // For future adventurers, I would not advise looking into this further. It's likely not worth the effort. + { typeof(MatchUserState), new TypeRedirectingFormatter() }, + { typeof(MatchRoomState), new TypeRedirectingFormatter() }, + { typeof(MatchUserRequest), new TypeRedirectingFormatter() }, + { typeof(MatchServerEvent), new TypeRedirectingFormatter() }, + }; + + public IMessagePackFormatter GetFormatter() + { + if (formatter_map.TryGetValue(typeof(T), out var formatter)) + return (IMessagePackFormatter)formatter; + + return StandardResolver.Instance.GetFormatter(); + } + + public class TypeRedirectingFormatter : IMessagePackFormatter + { + private readonly IMessagePackFormatter baseFormatter; + + public TypeRedirectingFormatter() + { + baseFormatter = StandardResolver.Instance.GetFormatter(); + } + + public void Serialize(ref MessagePackWriter writer, TActual value, MessagePackSerializerOptions options) => + baseFormatter.Serialize(ref writer, (TBase)(object)value, StandardResolver.Options); + + public TActual Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) => + (TActual)(object)baseFormatter.Deserialize(ref reader, StandardResolver.Options); + } + } +} From f95c6f0de5b484e1502ac06b2478069dabe9f275 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Aug 2021 17:42:13 +0900 Subject: [PATCH 1342/2442] Switch multiplayer back to messagepack --- osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs index c38a648a6a..965674c2f2 100644 --- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs @@ -39,7 +39,7 @@ namespace osu.Game.Online.Multiplayer { // Importantly, we are intentionally not using MessagePack here to correctly support derived class serialization. // More information on the limitations / reasoning can be found in osu-server-spectator's initialisation code. - connector = api.GetHubConnector(nameof(OnlineMultiplayerClient), endpoint, false); + connector = api.GetHubConnector(nameof(OnlineMultiplayerClient), endpoint); if (connector != null) { From 2b5a42e06325d0f1188be1bef1c0937cb569308e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Aug 2021 17:42:26 +0900 Subject: [PATCH 1343/2442] Add missing union specification for `MatchUserRequest` --- osu.Game/Online/Multiplayer/MatchUserRequest.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Online/Multiplayer/MatchUserRequest.cs b/osu.Game/Online/Multiplayer/MatchUserRequest.cs index 15c3ad0776..8c6809e7f3 100644 --- a/osu.Game/Online/Multiplayer/MatchUserRequest.cs +++ b/osu.Game/Online/Multiplayer/MatchUserRequest.cs @@ -3,6 +3,7 @@ using System; using MessagePack; +using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; namespace osu.Game.Online.Multiplayer { @@ -11,6 +12,7 @@ namespace osu.Game.Online.Multiplayer /// [Serializable] [MessagePackObject] + [Union(0, typeof(ChangeTeamRequest))] // IMPORTANT: Add rules to SignalRUnionWorkaroundResolver for new derived types. public abstract class MatchUserRequest { } From bc025efce57932a0dd28ba0e21683bfa1b8dd85e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Aug 2021 17:42:44 +0900 Subject: [PATCH 1344/2442] Add commenting regarding workaround to avoid potential omission in the future --- osu.Game/Online/Multiplayer/MatchRoomState.cs | 6 +++--- osu.Game/Online/Multiplayer/MatchUserState.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MatchRoomState.cs b/osu.Game/Online/Multiplayer/MatchRoomState.cs index 5b662af100..edd34fb5a3 100644 --- a/osu.Game/Online/Multiplayer/MatchRoomState.cs +++ b/osu.Game/Online/Multiplayer/MatchRoomState.cs @@ -15,9 +15,9 @@ namespace osu.Game.Online.Multiplayer /// [Serializable] [MessagePackObject] - [Union(0, typeof(TeamVersusRoomState))] - // TODO: this will need to be abstract or interface when/if we get messagepack working. for now it isn't as it breaks json serialisation. - public class MatchRoomState + [Union(0, typeof(TeamVersusRoomState))] // IMPORTANT: Add rules to SignalRUnionWorkaroundResolver for new derived types. + // TODO: abstract breaks json serialisation. attention will be required for iOS support (unless we get messagepack AOT working instead). + public abstract class MatchRoomState { } } diff --git a/osu.Game/Online/Multiplayer/MatchUserState.cs b/osu.Game/Online/Multiplayer/MatchUserState.cs index f457191bb5..69245deba0 100644 --- a/osu.Game/Online/Multiplayer/MatchUserState.cs +++ b/osu.Game/Online/Multiplayer/MatchUserState.cs @@ -15,9 +15,9 @@ namespace osu.Game.Online.Multiplayer /// [Serializable] [MessagePackObject] - [Union(0, typeof(TeamVersusUserState))] - // TODO: this will need to be abstract or interface when/if we get messagepack working. for now it isn't as it breaks json serialisation. - public class MatchUserState + [Union(0, typeof(TeamVersusUserState))] // IMPORTANT: Add rules to SignalRUnionWorkaroundResolver for new derived types. + // TODO: abstract breaks json serialisation. attention will be required for iOS support (unless we get messagepack AOT working instead). + public abstract class MatchUserState { } } From 4d1582d721e0af061117f2ad49fded615be7f003 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Aug 2021 18:14:45 +0900 Subject: [PATCH 1345/2442] Play settings hide animation if visible when exiting `RoomSubScreen` --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 81fae558c8..ec56edaa4d 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -244,6 +244,7 @@ namespace osu.Game.Screens.OnlinePlay.Match if (Room.RoomID.Value == null) { // room has not been created yet; exit immediately. + settingsOverlay.Hide(); return base.OnBackButton(); } From 493f47787b5c9a2bc3366b6ea55f8dc8269f6079 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Aug 2021 18:18:42 +0900 Subject: [PATCH 1346/2442] Disable condition for the time being --- .../OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 833e6b24e0..61cb040830 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -79,8 +79,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer handleRoomLost(); }, true); - if (client.Room == null) - handleRoomLost(); + // if (client.Room == null) + // handleRoomLost(); } protected override Drawable CreateMainContent() => new GridContainer From 023f3fb70e263f6496066a0f399c978dae5e801b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Aug 2021 18:19:31 +0900 Subject: [PATCH 1347/2442] Use BackgroundScreen for multiplayer backgrounds --- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 3 + .../Screens/OnlinePlay/OnlinePlayScreen.cs | 61 ------------------- .../Screens/OnlinePlay/OnlinePlaySubScreen.cs | 9 ++- .../OnlinePlay/OnlinePlaySubScreenStack.cs | 14 +++++ 4 files changed, 25 insertions(+), 62 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 677b9c0782..7712e7d734 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -23,6 +23,7 @@ using osu.Game.Input; using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Rulesets; +using osu.Game.Screens.Backgrounds; using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Match; @@ -36,6 +37,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { public override string Title => "Lounge"; + protected override BackgroundScreen CreateBackground() => new BackgroundScreenDefault(); + protected override UserActivity InitialActivity => new UserActivity.SearchingForLobby(); protected Container Buttons { get; } = new Container diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index e5962db608..c057c814ca 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -78,32 +78,16 @@ namespace osu.Game.Screens.OnlinePlay [BackgroundDependencyLoader] private void load() { - var backgroundColour = Color4Extensions.FromHex(@"3e3a44"); - InternalChild = waves = new MultiplayerWaveContainer { RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = backgroundColour, - }, new Container { RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new BeatmapBackgroundSprite - { - RelativeSizeAxes = Axes.Both - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.9f), Color4.Black.Opacity(0.6f)) - }, screenStack = new OnlinePlaySubScreenStack { RelativeSizeAxes = Axes.Both @@ -276,51 +260,6 @@ namespace osu.Game.Screens.OnlinePlay } } - private class BeatmapBackgroundSprite : OnlinePlayBackgroundSprite - { - protected override UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new BlurredBackgroundSprite(BeatmapSetCoverType) { RelativeSizeAxes = Axes.Both }; - - public class BlurredBackgroundSprite : UpdateableBeatmapBackgroundSprite - { - public BlurredBackgroundSprite(BeatmapSetCoverType type) - : base(type) - { - } - - protected override double LoadDelay => 200; - - protected override Drawable CreateDrawable(BeatmapInfo model) => - new BufferedLoader(base.CreateDrawable(model)); - } - - // This class is an unfortunate requirement due to `LongRunningLoad` requiring direct async loading. - // It means that if the web request fetching the beatmap background takes too long, it will suddenly appear. - internal class BufferedLoader : BufferedContainer - { - private readonly Drawable drawable; - - public BufferedLoader(Drawable drawable) - { - this.drawable = drawable; - - RelativeSizeAxes = Axes.Both; - BlurSigma = new Vector2(10); - FrameBufferScale = new Vector2(0.5f); - CacheDrawnFrameBuffer = true; - } - - [BackgroundDependencyLoader] - private void load() - { - LoadComponentAsync(drawable, d => - { - Add(d); - ForceRedraw(); - }); - } - } - } - ScreenStack IHasSubScreenStack.SubScreenStack => screenStack; } } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs index e1bd889088..58531a4b1c 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs @@ -35,13 +35,16 @@ namespace osu.Game.Screens.OnlinePlay public override void OnEntering(IScreen last) { - this.FadeInFromZero(APPEAR_DURATION, Easing.OutQuint); + base.OnEntering(last); + this.FadeInFromZero(APPEAR_DURATION, Easing.OutQuint); this.MoveToX(X_SHIFT).MoveToX(0, X_MOVE_DURATION, Easing.OutQuint); } public override bool OnExiting(IScreen next) { + base.OnExiting(next); + this.FadeOut(DISAPPEAR_DURATION, Easing.OutQuint); this.MoveToX(X_SHIFT, X_MOVE_DURATION, Easing.OutQuint); @@ -50,12 +53,16 @@ namespace osu.Game.Screens.OnlinePlay public override void OnResuming(IScreen last) { + base.OnResuming(last); + this.Delay(RESUME_TRANSITION_DELAY).FadeIn(APPEAR_DURATION, Easing.OutQuint); this.MoveToX(0, X_MOVE_DURATION, Easing.OutQuint); } public override void OnSuspending(IScreen next) { + base.OnSuspending(next); + this.FadeOut(DISAPPEAR_DURATION, Easing.OutQuint); this.MoveToX(-X_SHIFT, X_MOVE_DURATION, Easing.OutQuint); } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreenStack.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreenStack.cs index 7f2a0980c1..69fa3b0916 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreenStack.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreenStack.cs @@ -1,12 +1,26 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Shapes; using osu.Framework.Screens; +using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay { public class OnlinePlaySubScreenStack : OsuScreenStack { + public OnlinePlaySubScreenStack() + { + AddInternal(new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.9f), Color4.Black.Opacity(0.6f)) + }); + } + protected override void ScreenChanged(IScreen prev, IScreen next) { base.ScreenChanged(prev, next); From 4afc808ffcf32c1930f7a1bd256b62926b6c65e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Aug 2021 18:25:40 +0900 Subject: [PATCH 1348/2442] Remove horizontal transitions for now They look bad with the current toolbar implementation. We can probably add them back once the toolbar is moved to the lounge. --- osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs index e1bd889088..49cc77a5d8 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs @@ -23,12 +23,6 @@ namespace osu.Game.Screens.OnlinePlay RelativeSizeAxes = Axes.Both; } - public const float X_SHIFT = 200; - - public const double X_MOVE_DURATION = 800; - - public const double RESUME_TRANSITION_DELAY = DISAPPEAR_DURATION / 2; - public const double APPEAR_DURATION = 800; public const double DISAPPEAR_DURATION = 500; @@ -36,28 +30,23 @@ namespace osu.Game.Screens.OnlinePlay public override void OnEntering(IScreen last) { this.FadeInFromZero(APPEAR_DURATION, Easing.OutQuint); - this.FadeInFromZero(APPEAR_DURATION, Easing.OutQuint); - this.MoveToX(X_SHIFT).MoveToX(0, X_MOVE_DURATION, Easing.OutQuint); } public override bool OnExiting(IScreen next) { this.FadeOut(DISAPPEAR_DURATION, Easing.OutQuint); - this.MoveToX(X_SHIFT, X_MOVE_DURATION, Easing.OutQuint); return false; } public override void OnResuming(IScreen last) { - this.Delay(RESUME_TRANSITION_DELAY).FadeIn(APPEAR_DURATION, Easing.OutQuint); - this.MoveToX(0, X_MOVE_DURATION, Easing.OutQuint); + this.FadeIn(APPEAR_DURATION, Easing.OutQuint); } public override void OnSuspending(IScreen next) { this.FadeOut(DISAPPEAR_DURATION, Easing.OutQuint); - this.MoveToX(-X_SHIFT, X_MOVE_DURATION, Easing.OutQuint); } public override string ToString() => Title; From 675e2e34baf1d6796e96b7911bac786352538853 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Aug 2021 19:02:05 +0900 Subject: [PATCH 1349/2442] Add padding between beatmap panel and edit button --- .../OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs index 56b87302c2..2e94e51385 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Screens; using osu.Game.Online.API; using osu.Game.Screens.OnlinePlay.Match.Components; +using osuTK; namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { @@ -35,6 +36,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, + Spacing = new Vector2(5), Children = new Drawable[] { beatmapPanelContainer = new Container From be774980440e89cbbc8451ef215138bf1a3046da Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Aug 2021 19:02:31 +0900 Subject: [PATCH 1350/2442] Move chat to right column in multiplayer match screen --- .../Multiplayer/MultiplayerMatchSubScreen.cs | 106 ++++++++---------- 1 file changed, 47 insertions(+), 59 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 3f5837cbbe..6a41403aff 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -141,79 +141,67 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer // Spacer null, // Main right column - new FillFlowContainer + new GridContainer { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new[] + RelativeSizeAxes = Axes.Both, + Content = new[] { - new FillFlowContainer + new Drawable[] { new OverlinedHeader("Beatmap") }, + new Drawable[] { new BeatmapSelectionControl { RelativeSizeAxes = Axes.X } }, + new[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] + UserModsSection = new FillFlowContainer { - new OverlinedHeader("Beatmap"), - new BeatmapSelectionControl { RelativeSizeAxes = Axes.X } - } - }, - UserModsSection = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Margin = new MarginPadding { Top = 10 }, - Children = new Drawable[] - { - new OverlinedHeader("Extra mods"), - new FillFlowContainer + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Top = 10 }, + Children = new Drawable[] { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(10, 0), - Children = new Drawable[] + new OverlinedHeader("Extra mods"), + new FillFlowContainer { - new UserModSelectButton + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10, 0), + Children = new Drawable[] { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Width = 90, - Text = "Select", - Action = ShowUserModSelect, - }, - new ModDisplay - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Current = UserMods, - Scale = new Vector2(0.8f), - }, - } + new UserModSelectButton + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Width = 90, + Text = "Select", + Action = ShowUserModSelect, + }, + new ModDisplay + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Current = UserMods, + Scale = new Vector2(0.8f), + }, + } + }, } - } - } + }, + }, + new Drawable[] { new OverlinedHeader("Chat") { Margin = new MarginPadding { Vertical = 5 }, }, }, + new Drawable[] { new MatchChatDisplay { RelativeSizeAxes = Axes.Both } } + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.AutoSize), + new Dimension(), } - } + }, } } } } }, - new Drawable[] - { - new GridContainer - { - RelativeSizeAxes = Axes.Both, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize) - }, - Content = new[] - { - new Drawable[] { new OverlinedHeader("Chat") }, - new Drawable[] { new MatchChatDisplay { RelativeSizeAxes = Axes.Both } } - } - } - } }, }; From 16aecfe934c9d893e694af1429ad0cede7c2d679 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Aug 2021 19:02:54 +0900 Subject: [PATCH 1351/2442] Start free mod selection area hidden on screen display --- .../Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 1 + osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 6a41403aff..3a012787a4 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -155,6 +155,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Margin = new MarginPadding { Top = 10 }, + Alpha = 0, Children = new Drawable[] { new OverlinedHeader("Extra mods"), diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 27de781d5f..729ddbef8c 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -120,6 +120,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, + Alpha = 0, Margin = new MarginPadding { Bottom = 10 }, Children = new Drawable[] { From 8fce5911a951dbe1cbc6884e0f596e2e5d50763b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Aug 2021 19:08:46 +0900 Subject: [PATCH 1352/2442] Adjust spacing and padding of footer buttons --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 9 +++++++-- .../Multiplayer/Match/MultiplayerMatchFooter.cs | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index ec56edaa4d..10df7cbadc 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -195,14 +195,19 @@ namespace osu.Game.Screens.OnlinePlay.Match new Container { RelativeSizeAxes = Axes.Both, - Children = new[] + Children = new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, Colour = Color4Extensions.FromHex(@"28242d") // Temporary. }, - CreateFooter() + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(5), + Child = CreateFooter() + }, } } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs index 9e8d51e64e..036e37ddfd 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs @@ -53,7 +53,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { new Dimension(), new Dimension(maxSize: spectate_button_width), - new Dimension(GridSizeMode.Absolute, 10), + new Dimension(GridSizeMode.Absolute, 5), new Dimension(maxSize: ready_button_width), new Dimension() } From 44e157447c7d005079e2ede801ee6ad6fe5a802c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Aug 2021 19:10:54 +0900 Subject: [PATCH 1353/2442] Initial rework of backgrounds --- .../Components/PlaylistItemBackground.cs | 44 ++++++++++ .../Components/RoomBackgroundScreen.cs | 82 +++++++++++++++++++ .../OnlinePlay/Lounge/LoungeSubScreen.cs | 6 +- .../Screens/OnlinePlay/OnlinePlayScreen.cs | 6 -- 4 files changed, 130 insertions(+), 8 deletions(-) create mode 100644 osu.Game/Screens/OnlinePlay/Components/PlaylistItemBackground.cs create mode 100644 osu.Game/Screens/OnlinePlay/Components/RoomBackgroundScreen.cs diff --git a/osu.Game/Screens/OnlinePlay/Components/PlaylistItemBackground.cs b/osu.Game/Screens/OnlinePlay/Components/PlaylistItemBackground.cs new file mode 100644 index 0000000000..90ad6e0f6e --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Components/PlaylistItemBackground.cs @@ -0,0 +1,44 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Graphics.Textures; +using osu.Game.Beatmaps; +using osu.Game.Graphics.Backgrounds; +using osu.Game.Online.Rooms; + +namespace osu.Game.Screens.OnlinePlay.Components +{ + public class PlaylistItemBackground : Background + { + public readonly BeatmapInfo? BeatmapInfo; + + public PlaylistItemBackground(PlaylistItem? playlistItem) + { + BeatmapInfo = playlistItem?.Beatmap.Value; + } + + [BackgroundDependencyLoader] + private void load(BeatmapManager beatmaps, LargeTextureStore textures) + { + Texture? texture = null; + + // prefer online cover where available. + if (BeatmapInfo?.BeatmapSet?.OnlineInfo?.Covers.Cover != null) + texture = textures.Get(BeatmapInfo.BeatmapSet.OnlineInfo.Covers.Cover); + + Sprite.Texture = texture ?? beatmaps.DefaultBeatmap.Background; + } + + public override bool Equals(Background? other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + + return other.GetType() == GetType() + && ((PlaylistItemBackground)other).BeatmapInfo == BeatmapInfo; + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomBackgroundScreen.cs b/osu.Game/Screens/OnlinePlay/Components/RoomBackgroundScreen.cs new file mode 100644 index 0000000000..c6e467233e --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Components/RoomBackgroundScreen.cs @@ -0,0 +1,82 @@ +// 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.Linq; +using System.Threading; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Online.Rooms; +using osuTK; + +namespace osu.Game.Screens.OnlinePlay.Components +{ + public class RoomBackgroundScreen : BackgroundScreen + { + private CancellationTokenSource? cancellationSource; + private PlaylistItemBackground? background; + + private readonly BindableList playlist = new BindableList(); + + public RoomBackgroundScreen() + { + playlist.BindCollectionChanged((_, __) => updateBackground()); + } + + private Room? room; + + public Room? Room + { + get => room; + set + { + if (room == value) + return; + + if (room != null) + playlist.UnbindFrom(room.Playlist); + + room = value; + + if (room != null) + playlist.BindTo(room.Playlist); + else + playlist.Clear(); + } + } + + private void updateBackground() + { + Schedule(() => + { + var playlistItem = playlist.FirstOrDefault(); + var beatmap = playlistItem?.Beatmap.Value; + + if (background?.BeatmapInfo?.BeatmapSet?.OnlineInfo?.Covers?.Cover == beatmap?.BeatmapSet?.OnlineInfo?.Covers?.Cover) + return; + + cancellationSource?.Cancel(); + LoadComponentAsync(new PlaylistItemBackground(playlistItem), switchBackground, (cancellationSource = new CancellationTokenSource()).Token); + }); + } + + private void switchBackground(PlaylistItemBackground newBackground) + { + float newDepth = 0; + + if (background != null) + { + newDepth = background.Depth + 1; + background.FinishTransforms(); + background.FadeOut(250); + background.Expire(); + } + + newBackground.Depth = newDepth; + newBackground.BlurTo(new Vector2(10)); + + AddInternal(background = newBackground); + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 7712e7d734..c5d9616ee3 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -23,7 +23,6 @@ using osu.Game.Input; using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Rulesets; -using osu.Game.Screens.Backgrounds; using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Match; @@ -37,7 +36,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { public override string Title => "Lounge"; - protected override BackgroundScreen CreateBackground() => new BackgroundScreenDefault(); + protected override BackgroundScreen CreateBackground() => new RoomBackgroundScreen(); protected override UserActivity InitialActivity => new UserActivity.SearchingForLobby(); @@ -180,6 +179,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge var drawable = roomsContainer.Rooms.FirstOrDefault(r => r.Room == val.NewValue); if (drawable != null) scrollContainer.ScrollIntoView(drawable); + + ApplyToBackground(b => ((RoomBackgroundScreen)b).Room = val.NewValue); }); } @@ -246,6 +247,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge public override void OnEntering(IScreen last) { base.OnEntering(last); + onReturning(); } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index c057c814ca..576598cb1e 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -6,13 +6,9 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Logging; using osu.Framework.Screens; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics.Containers; using osu.Game.Online.API; using osu.Game.Online.Rooms; @@ -21,8 +17,6 @@ using osu.Game.Screens.Menu; using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Users; -using osuTK; -using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay { From a65cd36a5f25296b5348f483208ad37458437841 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Aug 2021 19:19:46 +0900 Subject: [PATCH 1354/2442] Move some constants to `const`s --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 17a26eb7ee..297a16dd1d 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -73,17 +73,19 @@ namespace osu.Game.Screens.Select ruleset.BindValueChanged(_ => updateDisplay()); } + private const double animation_duration = 800; + protected override void PopIn() { - this.MoveToX(0, 800, Easing.OutQuint); - this.RotateTo(0, 800, Easing.OutQuint); + this.MoveToX(0, animation_duration, Easing.OutQuint); + this.RotateTo(0, animation_duration, Easing.OutQuint); this.FadeIn(transition_duration); } protected override void PopOut() { - this.MoveToX(-100, 800, Easing.In); - this.RotateTo(10, 800, Easing.In); + this.MoveToX(-100, animation_duration, Easing.In); + this.RotateTo(10, animation_duration, Easing.In); this.FadeOut(transition_duration * 2, Easing.In); } @@ -191,6 +193,8 @@ namespace osu.Game.Screens.Select titleBinding = localisation.GetLocalisedString(new RomanisableString(metadata.TitleUnicode, metadata.Title)); artistBinding = localisation.GetLocalisedString(new RomanisableString(metadata.ArtistUnicode, metadata.Artist)); + const float top_height = 0.7f; + Children = new Drawable[] { difficultyColourBar = new Container @@ -202,15 +206,15 @@ namespace osu.Game.Screens.Select new Box { RelativeSizeAxes = Axes.Both, - Width = 0.7f, + Width = top_height, }, new Box { RelativeSizeAxes = Axes.Both, RelativePositionAxes = Axes.Both, Alpha = 0.5f, - X = 0.7f, - Width = 1 - 0.7f, + X = top_height, + Width = 1 - top_height, } } }, From a6b7ca1a4c44a5f303abcf3c4c1cc59facdee905 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Aug 2021 19:55:10 +0900 Subject: [PATCH 1355/2442] Ensure all request failures are correctly handled during login --- osu.Game/Online/API/APIAccess.cs | 4 ++-- osu.Game/Online/API/APIRequest.cs | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index f7a3f4602f..96d62e95a2 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -309,7 +309,7 @@ namespace osu.Game.Online.API if (IsLoggedIn) state.Value = APIState.Online; failureCount = 0; - return true; + return req.CompletionState == APIRequestCompletionState.Completed; } catch (HttpRequestException re) { @@ -381,7 +381,7 @@ namespace osu.Game.Online.API } } - public bool IsLoggedIn => localUser.Value.Id > 1; + public bool IsLoggedIn => localUser.Value.Id > 1; // TODO: should this also be true if attempting to connect? public void Queue(APIRequest request) { diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index e117293ce6..cf17ed4b5d 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -84,7 +84,7 @@ namespace osu.Game.Online.API /// The state of this request, from an outside perspective. /// This is used to ensure correct notification events are fired. /// - private APIRequestCompletionState completionState; + public APIRequestCompletionState CompletionState { get; private set; } public void Perform(IAPIProvider api) { @@ -127,10 +127,10 @@ namespace osu.Game.Online.API { lock (completionStateLock) { - if (completionState != APIRequestCompletionState.Waiting) + if (CompletionState != APIRequestCompletionState.Waiting) return; - completionState = APIRequestCompletionState.Completed; + CompletionState = APIRequestCompletionState.Completed; } if (API == null) @@ -143,10 +143,10 @@ namespace osu.Game.Online.API { lock (completionStateLock) { - if (completionState != APIRequestCompletionState.Waiting) + if (CompletionState != APIRequestCompletionState.Waiting) return; - completionState = APIRequestCompletionState.Failed; + CompletionState = APIRequestCompletionState.Failed; } if (API == null) @@ -161,7 +161,7 @@ namespace osu.Game.Online.API { lock (completionStateLock) { - if (completionState != APIRequestCompletionState.Waiting) + if (CompletionState != APIRequestCompletionState.Waiting) return; WebRequest?.Abort(); @@ -200,7 +200,7 @@ namespace osu.Game.Online.API get { lock (completionStateLock) - return completionState == APIRequestCompletionState.Failed; + return CompletionState == APIRequestCompletionState.Failed; } } From 0effc8f5d892910964063ac096a303cf5cbb495a Mon Sep 17 00:00:00 2001 From: Xexxar Date: Thu, 19 Aug 2021 14:12:03 +0000 Subject: [PATCH 1356/2442] refactored speed skill, implemented better acc pp --- .../Difficulty/OsuPerformanceCalculator.cs | 36 +++-- .../Difficulty/Skills/Aim.cs | 2 - .../Difficulty/Skills/Speed.cs | 132 ++++++++---------- 3 files changed, 84 insertions(+), 86 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index e6ab978dfb..42f65ce4ed 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -161,6 +161,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty // Scale the speed value with accuracy and OD speedValue *= (0.95 + Math.Pow(Attributes.OverallDifficulty, 2) / 750) * Math.Pow(accuracy, (14.5 - Math.Max(Attributes.OverallDifficulty, 8)) / 2); + // Punish low OD severely prevent OD abuse on rhythmically complex songs. + speedValue *= Math.Max(0.75, (Math.Min(8, Attributes.OverallDifficulty) / 8)); // Scale the speed value with # of 50s to punish doubletapping. speedValue *= Math.Pow(0.98, countMeh < totalHits / 500.0 ? 0 : countMeh - totalHits / 500.0); @@ -169,25 +171,31 @@ namespace osu.Game.Rulesets.Osu.Difficulty private double computeAccuracyValue() { - // This percentage only considers HitCircles of any value - in this part of the calculation we focus on hitting the timing hit window - double betterAccuracyPercentage; int amountHitObjectsWithAccuracy = Attributes.HitCircleCount; - if (amountHitObjectsWithAccuracy > 0) - betterAccuracyPercentage = ((countGreat - (totalHits - amountHitObjectsWithAccuracy)) * 6 + countOk * 2 + countMeh) / (double)(amountHitObjectsWithAccuracy * 6); - else - betterAccuracyPercentage = 0; + // This section should be documented by Tr3, but effectively we're calculating the exact same way as before, but + // we calculate a variance based on the object count and # of 50s, 100s, etc. This prevents us from having cases + // where an SS on lower OD is actually worth more than a 95% on OD11, even though the OD11 requires a greater + // window of precision. - // It is possible to reach a negative accuracy with this formula. Cap it at zero - zero points - if (betterAccuracyPercentage < 0) - betterAccuracyPercentage = 0; + double p100 = (2 * (double)countOk) / amountHitObjectsWithAccuracy; // this is multiplied by two to encourage better accuracy. (scales better) + double p50 = (1 * (double)countMeh) / amountHitObjectsWithAccuracy; + double pm = (1 * (double)countMiss) / amountHitObjectsWithAccuracy; + double p300 = 1.0 - pm - p100 - p50; - // Lots of arbitrary values from testing. - // Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution - double accuracyValue = Math.Pow(1.52163, Attributes.OverallDifficulty) * Math.Pow(betterAccuracyPercentage, 24) * 2.83; + double m300 = 79.5 - 6.0 * Attributes.OverallDifficulty; + double m100 = 139.5 - 8.0 * Attributes.OverallDifficulty; + double m50 = 199.5 - 10.0 * Attributes.OverallDifficulty; + double acc = p300 + 1.0 / 3.0 * p100 + 1.0 / 6.0 * p50; + + double variance = p300 * Math.Pow(m300 / 2.0, 2.0) + + p100 * Math.Pow((m300 + m100) / 2.0, 2.0) + + p50 * Math.Pow((m100 + m50) / 2.0, 2.0) + + pm * Math.Pow(229.5 - 11 * Attributes.OverallDifficulty, 2.0); + + double accuracyValue = 2.83 * Math.Pow(1.52163, (79.5 - 2 * Math.Sqrt(variance)) / 6.0) + * Math.Pow(Math.Log(1.0 + (Math.E - 1.0) * (Math.Min(amountHitObjectsWithAccuracy, 1600) / 1000.0)), 0.5); - // Bonus for many hitcircles - it's harder to keep good accuracy up for longer - accuracyValue *= Math.Min(1.15, Math.Pow(amountHitObjectsWithAccuracy / 1000.0, 0.3)); if (mods.Any(m => m is OsuModHidden)) accuracyValue *= 1.08; diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index 776cca8657..93df6e90f8 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -74,8 +74,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills currentStrain *= strainDecay(current.DeltaTime); currentStrain += aimStrain * skillMultiplier; -// Console.WriteLine(currentStrain); - return currentStrain; } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 935e80ebc5..b29e8be1a2 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -20,10 +20,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private const double pi_over_4 = Math.PI / 4; private const double pi_over_2 = Math.PI / 2; - private const double rhythmMultiplier = 1.5; + private const double rhythmMultiplier = 2.5; private const int HistoryTimeMax = 3000; // 3 seconds of calculatingRhythmBonus max. - private double skillMultiplier => 1400; + private double skillMultiplier => 1375; private double strainDecayBase => 0.3; private double currentTapStrain = 1; @@ -57,75 +57,70 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills if (current.BaseObject is Spinner) return 0; - // {doubles, triplets, quads, quints, 6-tuplets, 7 Tuplets, greater} int previousIslandSize = -1; - double[] islandTimes = {0, 0, 0, 0, 0, 0, 0}; + double rhythmComplexitySum = 0; int islandSize = 0; bool firstDeltaSwitch = false; for (int i = Previous.Count - 1; i > 0; i--) { - double currDelta = ((OsuDifficultyHitObject)Previous[i - 1]).StrainTime; - double prevDelta = ((OsuDifficultyHitObject)Previous[i]).StrainTime; - double effectiveRatio = Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta); + double currHistoricalDecay = Math.Max(0, (HistoryTimeMax - (current.StartTime - Previous[i - 1].StartTime))) / HistoryTimeMax; // scales note 0 to 1 from history to now - effectiveRatio *= Math.Sqrt(100 / ((currDelta + prevDelta) / 2)); // scale with bpm slightly - - if (effectiveRatio > 0.5) - effectiveRatio = 0.5 + (effectiveRatio - 0.5) * 10; // extra buff for 1/3 -> 1/4 etc transitions. - - double currHistoricalDecay = Math.Max(0, (HistoryTimeMax - (current.StartTime - Previous[i - 1].StartTime))) / HistoryTimeMax; - - if (firstDeltaSwitch) + if (currHistoricalDecay != 0) { - if (isRatioEqual(1.0, prevDelta, currDelta)) + currHistoricalDecay = Math.Max(currHistoricalDecay, (Previous.Count - i) / Previous.Count); // either we're limited by time or limited by object count. + + double currDelta = ((OsuDifficultyHitObject)Previous[i - 1]).StrainTime; + double prevDelta = ((OsuDifficultyHitObject)Previous[i]).StrainTime; + double effectiveRatio = Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta); + + if (effectiveRatio > 0.5) + effectiveRatio = 0.5 + (effectiveRatio - 0.5) * 10; // large buff for 1/3 -> 1/4 type transitions. + + effectiveRatio *= Math.Sqrt(100 / ((currDelta + prevDelta) / 2)) * currHistoricalDecay; // scale with bpm slightly and with time + + if (firstDeltaSwitch) { - islandSize++; // island is still progressing, count size. + if (isRatioEqual(1.0, prevDelta, currDelta)) + { + islandSize++; // island is still progressing, count size. + } + + else + { + if (islandSize > 6) + islandSize = 6; + + if (Previous[i - 1].BaseObject is Slider) // bpm change is into slider, this is easy acc window + effectiveRatio *= 0.25; + + if (Previous[i].BaseObject is Slider) // bpm change was from a slider, this is easier typically than circle -> circle + effectiveRatio *= 0.5; + + if (previousIslandSize == islandSize) // repeated island size (ex: triplet -> triplet) + effectiveRatio *= 0.25; + + rhythmComplexitySum += effectiveRatio * currHistoricalDecay; + + previousIslandSize = islandSize; // log the last island size. + + if (prevDelta * 1.25 < currDelta) // we're slowing down, stop counting + firstDeltaSwitch = false; // if we're speeding up, this stays true and we keep counting island size. + + islandSize = 0; + } } - - else + else if (prevDelta > 1.25 * currDelta) // we want to be speeding up. { - if (islandSize > 6) - islandSize = 6; - - if (Previous[i - 1].BaseObject is Slider) // bpm change is into slider, this is easy acc window - effectiveRatio *= 0.5; - - if (Previous[i].BaseObject is Slider) // bpm change was from a slider, this is easier typically than circle -> circle - effectiveRatio *= 0.75; - - if (previousIslandSize == islandSize) // repeated island size (ex: triplet -> triplet) - effectiveRatio *= 0.5; - - islandTimes[islandSize] = islandTimes[islandSize] + effectiveRatio * currHistoricalDecay; - - previousIslandSize = islandSize; // log the last island size. - - if (prevDelta * 1.25 < currDelta) // we're slowing down, stop counting - firstDeltaSwitch = false; // if we're speeding up, this stays true and we keep counting island size. - + // Begin counting island until we change speed again. + firstDeltaSwitch = true; islandSize = 0; } } - else if (prevDelta > 1.25 * currDelta) // we want to be speeding up. - { - // Begin counting island until we change speed again. - firstDeltaSwitch = true; - islandSize = 0; - } } - double rhythmComplexitySum = 0.0; - - for (int i = 0; i < islandTimes.Length; i++) - { - rhythmComplexitySum += islandTimes[i] * ((double)(i + islandTimes.Length) / (2 * islandTimes.Length)); // sum the total amount of rhythm variance - } - -// Console.WriteLine(Math.Sqrt(4 + rhythmComplexitySum * rhythmMultiplier) / 2); - - return Math.Sqrt(4 + rhythmComplexitySum * rhythmMultiplier) / 2; + return Math.Sqrt(4 + rhythmComplexitySum * rhythmMultiplier) / 2; //produces multiplier that can be applied to strain. range [1, infinity) } private double tapStrainOf(DifficultyHitObject current, double speedBonus) @@ -133,9 +128,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills if (current.BaseObject is Spinner) return 0; - var osuCurrent = (OsuDifficultyHitObject)current; + var osuCurrObj = (OsuDifficultyHitObject)current; - return ((1 + (speedBonus - 1) * 0.75) * 0.95) / osuCurrent.StrainTime; + return speedBonus / osuCurrObj.StrainTime; } private double movementStrainOf(DifficultyHitObject current, double speedBonus) @@ -143,27 +138,24 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills if (current.BaseObject is Spinner) return 0; - var osuCurrent = (OsuDifficultyHitObject)current; + var osuCurrObj = (OsuDifficultyHitObject)current; - double distance = Math.Min(single_spacing_threshold, osuCurrent.TravelDistance + osuCurrent.JumpDistance); + double distance = Math.Min(single_spacing_threshold, osuCurrObj.TravelDistance + osuCurrObj.JumpDistance); double angleBonus = 1.0; - if (osuCurrent.Angle != null && osuCurrent.Angle.Value < angle_bonus_begin) + if (osuCurrObj.Angle != null) { - angleBonus = 1 + Math.Pow(Math.Sin(1.5 * (angle_bonus_begin - osuCurrent.Angle.Value)), 2) / 3.57; + double angle = osuCurrObj.Angle.Value; + + if (angle < pi_over_2) + angleBonus = 1.25; + else if (angle < angle_bonus_begin) + angleBonus = 1 + Math.Pow(Math.Sin(1.5 * (angle_bonus_begin - angle)), 2) / 4; - if (osuCurrent.Angle.Value < pi_over_2) - { - angleBonus = 1.28; - if (distance < 90 && osuCurrent.Angle.Value < pi_over_4) - angleBonus += (1 - angleBonus) * Math.Min((90 - distance) / 10, 1); - else if (distance < 90) - angleBonus += (1 - angleBonus) * Math.Min((90 - distance) / 10, 1) * Math.Sin((pi_over_2 - osuCurrent.Angle.Value) / pi_over_4); - } } - return ((1 + (speedBonus - 1) * 0.75) * angleBonus * speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / osuCurrent.StrainTime; + return (angleBonus * speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / osuCurrObj.StrainTime; } private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000); @@ -176,7 +168,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double deltaTime = Math.Max(max_speed_bonus, current.DeltaTime); if (deltaTime < min_speed_bonus) - speedBonus = 1 + Math.Pow((min_speed_bonus - deltaTime) / speed_balancing_factor, 2); + speedBonus = 1 + 0.75 * Math.Pow((min_speed_bonus - deltaTime) / speed_balancing_factor, 2); currentRhythm = calculateRhythmBonus(current); From 7b70d41a937b4ebd12dd9c04a56f47e0d1efd6d8 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Thu, 19 Aug 2021 14:49:44 +0000 Subject: [PATCH 1357/2442] forgot about the / 0 --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 42f65ce4ed..24de290f1f 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -173,6 +173,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty { int amountHitObjectsWithAccuracy = Attributes.HitCircleCount; + if (amountHitObjectsWithAccuracy == 0) + return 0; + // This section should be documented by Tr3, but effectively we're calculating the exact same way as before, but // we calculate a variance based on the object count and # of 50s, 100s, etc. This prevents us from having cases // where an SS on lower OD is actually worth more than a 95% on OD11, even though the OD11 requires a greater From b44e6f634d56068564186566894ecc4f53a0c888 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Thu, 19 Aug 2021 15:05:39 +0000 Subject: [PATCH 1358/2442] noticed a bug with double applying historicaldecay --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index b29e8be1a2..324546eeb9 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private const double pi_over_4 = Math.PI / 4; private const double pi_over_2 = Math.PI / 2; - private const double rhythmMultiplier = 2.5; + private const double rhythmMultiplier = 2.0; private const int HistoryTimeMax = 3000; // 3 seconds of calculatingRhythmBonus max. private double skillMultiplier => 1375; @@ -101,7 +101,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills if (previousIslandSize == islandSize) // repeated island size (ex: triplet -> triplet) effectiveRatio *= 0.25; - rhythmComplexitySum += effectiveRatio * currHistoricalDecay; + rhythmComplexitySum += effectiveRatio; previousIslandSize = islandSize; // log the last island size. From 5b2cfcc2ffeadc92c97b061b861d1754ed25c308 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Thu, 19 Aug 2021 15:27:37 +0000 Subject: [PATCH 1359/2442] adjusted low acc nerf on speed --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 24de290f1f..bb3c3a4e59 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -161,8 +161,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty // Scale the speed value with accuracy and OD speedValue *= (0.95 + Math.Pow(Attributes.OverallDifficulty, 2) / 750) * Math.Pow(accuracy, (14.5 - Math.Max(Attributes.OverallDifficulty, 8)) / 2); - // Punish low OD severely prevent OD abuse on rhythmically complex songs. - speedValue *= Math.Max(0.75, (Math.Min(8, Attributes.OverallDifficulty) / 8)); + // Punish high speed values with low OD to prevent OD abuse on rhythmically complex songs. + if (speedValue > 100 && Attributes.OverallDifficulty < 8) + speedValue = 100 + (speedValue - 100) * Math.Max(0.5, (Attributes.OverallDifficulty - 4) / 4); // Scale the speed value with # of 50s to punish doubletapping. speedValue *= Math.Pow(0.98, countMeh < totalHits / 500.0 ? 0 : countMeh - totalHits / 500.0); From bf7f0a5d30fe779c308ec0299ee7a779a4aadbd8 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Thu, 19 Aug 2021 19:02:04 +0200 Subject: [PATCH 1360/2442] Remove double whitespace --- osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs index c0460a4536..ad045c4b17 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs @@ -141,7 +141,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons { new OsuSpriteText { - Text = BeatmapsetsStrings.ShowDetailsDownloadDefault, + Text = BeatmapsetsStrings.ShowDetailsDownloadDefault, Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold) }, new OsuSpriteText From d36eb269b456ac431ebe8f6d76da85ac7222fae7 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Thu, 19 Aug 2021 20:11:18 +0000 Subject: [PATCH 1361/2442] fixed code quality issues --- .../Difficulty/OsuPerformanceCalculator.cs | 9 ++++----- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 13 +++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index bb3c3a4e59..80465efe6f 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -190,15 +190,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty double m300 = 79.5 - 6.0 * Attributes.OverallDifficulty; double m100 = 139.5 - 8.0 * Attributes.OverallDifficulty; double m50 = 199.5 - 10.0 * Attributes.OverallDifficulty; - double acc = p300 + 1.0 / 3.0 * p100 + 1.0 / 6.0 * p50; double variance = p300 * Math.Pow(m300 / 2.0, 2.0) + - p100 * Math.Pow((m300 + m100) / 2.0, 2.0) + - p50 * Math.Pow((m100 + m50) / 2.0, 2.0) + - pm * Math.Pow(229.5 - 11 * Attributes.OverallDifficulty, 2.0); + p100 * Math.Pow((m300 + m100) / 2.0, 2.0) + + p50 * Math.Pow((m100 + m50) / 2.0, 2.0) + + pm * Math.Pow(229.5 - 11 * Attributes.OverallDifficulty, 2.0); double accuracyValue = 2.83 * Math.Pow(1.52163, (79.5 - 2 * Math.Sqrt(variance)) / 6.0) - * Math.Pow(Math.Log(1.0 + (Math.E - 1.0) * (Math.Min(amountHitObjectsWithAccuracy, 1600) / 1000.0)), 0.5); + * Math.Pow(Math.Log(1.0 + (Math.E - 1.0) * (Math.Min(amountHitObjectsWithAccuracy, 1600) / 1000.0)), 0.5); if (mods.Any(m => m is OsuModHidden)) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 324546eeb9..88861d8bd5 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -20,8 +20,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private const double pi_over_4 = Math.PI / 4; private const double pi_over_2 = Math.PI / 2; - private const double rhythmMultiplier = 2.0; - private const int HistoryTimeMax = 3000; // 3 seconds of calculatingRhythmBonus max. + private const double rhythm_multiplier = 2.0; + private const int history_time_max = 3000; // 3 seconds of calculatingRhythmBonus max. private double skillMultiplier => 1375; private double strainDecayBase => 0.3; @@ -65,11 +65,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills for (int i = Previous.Count - 1; i > 0; i--) { - double currHistoricalDecay = Math.Max(0, (HistoryTimeMax - (current.StartTime - Previous[i - 1].StartTime))) / HistoryTimeMax; // scales note 0 to 1 from history to now + double currHistoricalDecay = Math.Max(0, (history_time_max - (current.StartTime - Previous[i - 1].StartTime))) / history_time_max; // scales note 0 to 1 from history to now if (currHistoricalDecay != 0) { - currHistoricalDecay = Math.Max(currHistoricalDecay, (Previous.Count - i) / Previous.Count); // either we're limited by time or limited by object count. + // below was bugged in initial version. fixed now, but will change values, will do more testing + // currHistoricalDecay = Math.Min(currHistoricalDecay, (double)(Previous.Count - i) / Previous.Count); // either we're limited by time or limited by object count. double currDelta = ((OsuDifficultyHitObject)Previous[i - 1]).StrainTime; double prevDelta = ((OsuDifficultyHitObject)Previous[i]).StrainTime; @@ -120,7 +121,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } } - return Math.Sqrt(4 + rhythmComplexitySum * rhythmMultiplier) / 2; //produces multiplier that can be applied to strain. range [1, infinity) + return Math.Sqrt(4 + rhythmComplexitySum * rhythm_multiplier) / 2; //produces multiplier that can be applied to strain. range [1, infinity) } private double tapStrainOf(DifficultyHitObject current, double speedBonus) @@ -151,7 +152,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills if (angle < pi_over_2) angleBonus = 1.25; else if (angle < angle_bonus_begin) - angleBonus = 1 + Math.Pow(Math.Sin(1.5 * (angle_bonus_begin - angle)), 2) / 4; + angleBonus = 1 + Math.Pow(Math.Sin(1.5 * (angle_bonus_begin - angle)), 2) / 4; } From 5f1948d040b054a4ec68e964166b2c61aedb2d85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 19 Aug 2021 22:31:11 +0200 Subject: [PATCH 1362/2442] Add failing test case --- .../Visual/Settings/TestSceneSettingsItem.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs index df59b9284b..d9cce69ee3 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs @@ -76,5 +76,23 @@ namespace osu.Game.Tests.Visual.Settings AddStep("restore default", () => sliderBar.Current.SetDefault()); AddUntilStep("restore button hidden", () => restoreDefaultValueButton.Alpha == 0); } + + [Test] + public void TestWarningTextVisibility() + { + SettingsNumberBox numberBox = null; + + AddStep("create settings item", () => Child = numberBox = new SettingsNumberBox()); + AddAssert("warning text not created", () => !numberBox.ChildrenOfType().Any()); + + AddStep("set warning text", () => numberBox.WarningText = "this is a warning!"); + AddAssert("warning text created", () => numberBox.ChildrenOfType().Single().Alpha == 1); + + AddStep("unset warning text", () => numberBox.WarningText = default); + AddAssert("warning text hidden", () => numberBox.ChildrenOfType().Single().Alpha == 0); + + AddStep("set warning text again", () => numberBox.WarningText = "another warning!"); + AddAssert("warning text shown again", () => numberBox.ChildrenOfType().Single().Alpha == 1); + } } } From 143b8df1b2018d1b5efb169581d199064780cdf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 19 Aug 2021 22:33:05 +0200 Subject: [PATCH 1363/2442] Fix backwards warning text presence check --- osu.Game/Overlays/Settings/SettingsItem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 6621caef4e..5282217013 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -65,7 +65,7 @@ namespace osu.Game.Overlays.Settings { set { - bool hasValue = string.IsNullOrWhiteSpace(value.ToString()); + bool hasValue = !string.IsNullOrWhiteSpace(value.ToString()); if (warningText == null) { @@ -76,7 +76,7 @@ namespace osu.Game.Overlays.Settings FlowContent.Add(warningText = new SettingsNoticeText(colours) { Margin = new MarginPadding { Bottom = 5 } }); } - warningText.Alpha = hasValue ? 0 : 1; + warningText.Alpha = hasValue ? 1 : 0; warningText.Text = value.ToString(); // TODO: Remove ToString() call after TextFlowContainer supports localisation (see https://github.com/ppy/osu-framework/issues/4636). } } From 4d9c415e730ecc488d9b4f7a9758fc208ff4968c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 20 Aug 2021 04:37:35 +0300 Subject: [PATCH 1364/2442] Remove unnecessary queue in beatmap difficulty cache tests --- .../TestSceneBeatmapDifficultyCache.cs | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs b/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs index b6ba5b748a..6856e466cf 100644 --- a/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs +++ b/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs @@ -32,7 +32,6 @@ namespace osu.Game.Tests.Beatmaps private TestBeatmapDifficultyCache difficultyCache; private IBindable starDifficultyBindable; - private Queue> starDifficultyChangesQueue; [BackgroundDependencyLoader] private void load(OsuGameBase osu) @@ -49,14 +48,10 @@ namespace osu.Game.Tests.Beatmaps Child = difficultyCache = new TestBeatmapDifficultyCache(); - starDifficultyChangesQueue = new Queue>(); starDifficultyBindable = difficultyCache.GetBindableDifficulty(importedSet.Beatmaps.First()); - starDifficultyBindable.BindValueChanged(starDifficultyChangesQueue.Enqueue); }); - AddAssert($"star difficulty -> {BASE_STARS}", () => - starDifficultyChangesQueue.Dequeue().NewValue?.Stars == BASE_STARS && - starDifficultyChangesQueue.Count == 0); + AddAssert($"star difficulty -> {BASE_STARS}", () => starDifficultyBindable.Value?.Stars == BASE_STARS); } [Test] @@ -65,19 +60,13 @@ namespace osu.Game.Tests.Beatmaps OsuModDoubleTime dt = null; AddStep("change selected mod to DT", () => SelectedMods.Value = new[] { dt = new OsuModDoubleTime { SpeedChange = { Value = 1.5 } } }); - AddAssert($"star difficulty -> {BASE_STARS + 1.5}", () => - starDifficultyChangesQueue.Dequeue().NewValue?.Stars == BASE_STARS + 1.5 && - starDifficultyChangesQueue.Count == 0); + AddAssert($"star difficulty -> {BASE_STARS + 1.5}", () => starDifficultyBindable.Value?.Stars == BASE_STARS + 1.5); AddStep("change DT speed to 1.25", () => dt.SpeedChange.Value = 1.25); - AddAssert($"star difficulty -> {BASE_STARS + 1.25}", () => - starDifficultyChangesQueue.Dequeue().NewValue?.Stars == BASE_STARS + 1.25 && - starDifficultyChangesQueue.Count == 0); + AddAssert($"star difficulty -> {BASE_STARS + 1.25}", () => starDifficultyBindable.Value?.Stars == BASE_STARS + 1.25); AddStep("change selected mod to NC", () => SelectedMods.Value = new[] { new OsuModNightcore { SpeedChange = { Value = 1.75 } } }); - AddAssert($"star difficulty -> {BASE_STARS + 1.75}", () => - starDifficultyChangesQueue.Dequeue().NewValue?.Stars == BASE_STARS + 1.75 && - starDifficultyChangesQueue.Count == 0); + AddAssert($"star difficulty -> {BASE_STARS + 1.75}", () => starDifficultyBindable.Value?.Stars == BASE_STARS + 1.75); } [Test] From d98742522b339b4c92a49406bd9df7fe052b4225 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 20 Aug 2021 04:40:03 +0300 Subject: [PATCH 1365/2442] Remove unused using directive --- osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs b/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs index 6856e466cf..bdf4fb8d0c 100644 --- a/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs +++ b/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; From 73b40a6244abacb48bbf34f537f7ac11343c0e84 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 20 Aug 2021 05:29:30 +0300 Subject: [PATCH 1366/2442] Switch to until step to account for asynchronous operations --- .../Beatmaps/TestSceneBeatmapDifficultyCache.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs b/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs index bdf4fb8d0c..da457c9e8f 100644 --- a/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs +++ b/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs @@ -50,7 +50,7 @@ namespace osu.Game.Tests.Beatmaps starDifficultyBindable = difficultyCache.GetBindableDifficulty(importedSet.Beatmaps.First()); }); - AddAssert($"star difficulty -> {BASE_STARS}", () => starDifficultyBindable.Value?.Stars == BASE_STARS); + AddUntilStep($"star difficulty -> {BASE_STARS}", () => starDifficultyBindable.Value?.Stars == BASE_STARS); } [Test] @@ -59,13 +59,13 @@ namespace osu.Game.Tests.Beatmaps OsuModDoubleTime dt = null; AddStep("change selected mod to DT", () => SelectedMods.Value = new[] { dt = new OsuModDoubleTime { SpeedChange = { Value = 1.5 } } }); - AddAssert($"star difficulty -> {BASE_STARS + 1.5}", () => starDifficultyBindable.Value?.Stars == BASE_STARS + 1.5); + AddUntilStep($"star difficulty -> {BASE_STARS + 1.5}", () => starDifficultyBindable.Value?.Stars == BASE_STARS + 1.5); AddStep("change DT speed to 1.25", () => dt.SpeedChange.Value = 1.25); - AddAssert($"star difficulty -> {BASE_STARS + 1.25}", () => starDifficultyBindable.Value?.Stars == BASE_STARS + 1.25); + AddUntilStep($"star difficulty -> {BASE_STARS + 1.25}", () => starDifficultyBindable.Value?.Stars == BASE_STARS + 1.25); AddStep("change selected mod to NC", () => SelectedMods.Value = new[] { new OsuModNightcore { SpeedChange = { Value = 1.75 } } }); - AddAssert($"star difficulty -> {BASE_STARS + 1.75}", () => starDifficultyBindable.Value?.Stars == BASE_STARS + 1.75); + AddUntilStep($"star difficulty -> {BASE_STARS + 1.75}", () => starDifficultyBindable.Value?.Stars == BASE_STARS + 1.75); } [Test] From 1e6119da0b2cab68368f370dad96591478cf7a4a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 20 Aug 2021 05:36:39 +0300 Subject: [PATCH 1367/2442] Update code inspection settings to hide "merge into pattern" again --- osu.sln.DotSettings | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index d884ea31c5..e42b30e944 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -105,8 +105,9 @@ HINT HINT WARNING + DO_NOT_SHOW + DO_NOT_SHOW WARNING - DO_NOT_SHOW WARNING WARNING WARNING From 2825e15fd4af729be343f3b029eb13be7e0f8f72 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Aug 2021 11:54:37 +0900 Subject: [PATCH 1368/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 24d07b4588..f74b5d410b 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 928620b32e..e4838dc95a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 77f9052e85..5338ad5deb 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 1e02d61b852762425553b7873ccf01eb15cb9060 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Thu, 19 Aug 2021 20:06:25 -0700 Subject: [PATCH 1369/2442] Fix nub glow color not having 0 alpha when being set --- osu.Game/Graphics/UserInterface/Nub.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/Nub.cs b/osu.Game/Graphics/UserInterface/Nub.cs index cbf3e6b3aa..80d83e17c1 100644 --- a/osu.Game/Graphics/UserInterface/Nub.cs +++ b/osu.Game/Graphics/UserInterface/Nub.cs @@ -149,7 +149,7 @@ namespace osu.Game.Graphics.UserInterface glowColour = value; var effect = EdgeEffect; - effect.Colour = value; + effect.Colour = value.Opacity(0); EdgeEffect = effect; } } From da8eba999657826a8dd8a611f5c62576749f6b44 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Aug 2021 12:11:41 +0900 Subject: [PATCH 1370/2442] Return early to avoid updating state and failure count in fail cases --- osu.Game/Online/API/APIAccess.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 96d62e95a2..af14cdc7b3 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -305,11 +305,13 @@ namespace osu.Game.Online.API { req.Perform(this); + if (req.CompletionState != APIRequestCompletionState.Completed) + return false; + // we could still be in initialisation, at which point we don't want to say we're Online yet. if (IsLoggedIn) state.Value = APIState.Online; - failureCount = 0; - return req.CompletionState == APIRequestCompletionState.Completed; + return true; } catch (HttpRequestException re) { From 284c871e39f4272d036547eceae8257bad7871c5 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Thu, 19 Aug 2021 20:33:19 -0700 Subject: [PATCH 1371/2442] Fix glow color potentially being set incorrectly when glowing Co-authored-by: Salman Ahmed --- osu.Game/Graphics/UserInterface/Nub.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/Nub.cs b/osu.Game/Graphics/UserInterface/Nub.cs index 80d83e17c1..664f32b083 100644 --- a/osu.Game/Graphics/UserInterface/Nub.cs +++ b/osu.Game/Graphics/UserInterface/Nub.cs @@ -149,7 +149,7 @@ namespace osu.Game.Graphics.UserInterface glowColour = value; var effect = EdgeEffect; - effect.Colour = value.Opacity(0); + effect.Colour = Glowing ? value : value.Opacity(0); EdgeEffect = effect; } } From 19cc4a14a36ce0ccb7d5f418e2a09c2a361887c5 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 20 Aug 2021 09:22:15 +0200 Subject: [PATCH 1372/2442] Localise top score mods statistics --- .../Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs index 582528b675..23069eccdf 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs @@ -235,7 +235,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores } private ModsInfoColumn(FillFlowContainer modsContainer) - : base("mods", modsContainer) + : base(BeatmapsetsStrings.ShowScoreboardHeadersMods, modsContainer) { this.modsContainer = modsContainer; } From 0a1c9a6c05bd33adf8f73f1d9f3c39710ed65bc6 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 20 Aug 2021 09:26:38 +0200 Subject: [PATCH 1373/2442] Move DownloadButtonStrings -> CommonStrings --- osu.Game/Localisation/CommonStrings.cs | 12 +++++++++- .../Localisation/DownloadButtonStrings.cs | 24 ------------------- .../Buttons/HeaderDownloadButton.cs | 5 ++-- 3 files changed, 13 insertions(+), 28 deletions(-) delete mode 100644 osu.Game/Localisation/DownloadButtonStrings.cs diff --git a/osu.Game/Localisation/CommonStrings.cs b/osu.Game/Localisation/CommonStrings.cs index bf488d2590..b4381fc442 100644 --- a/osu.Game/Localisation/CommonStrings.cs +++ b/osu.Game/Localisation/CommonStrings.cs @@ -29,6 +29,16 @@ namespace osu.Game.Localisation /// public static LocalisableString Height => new TranslatableString(getKey(@"height"), @"Height"); + /// + /// "Downloading..." + /// + public static LocalisableString Downloading => new TranslatableString(getKey(@"downloading"), @"Downloading..."); + + /// + /// "Importing..." + /// + public static LocalisableString Importing => new TranslatableString(getKey(@"importing"), @"Importing..."); + private static string getKey(string key) => $@"{prefix}:{key}"; } -} \ No newline at end of file +} diff --git a/osu.Game/Localisation/DownloadButtonStrings.cs b/osu.Game/Localisation/DownloadButtonStrings.cs deleted file mode 100644 index 736f309624..0000000000 --- a/osu.Game/Localisation/DownloadButtonStrings.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Localisation; - -namespace osu.Game.Localisation -{ - public static class DownloadButtonStrings - { - private const string prefix = @"osu.Game.Resources.Localisation.DownloadButton"; - - /// - /// "Downloading..." - /// - public static LocalisableString Downloading => new TranslatableString(getKey(@"downloading"), @"Downloading..."); - - /// - /// "Importing..." - /// - public static LocalisableString Importing => new TranslatableString(getKey(@"importing"), @"Importing..."); - - private static string getKey(string key) => $@"{prefix}:{key}"; - } -} diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs index ad045c4b17..e7a55079ec 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs @@ -19,7 +19,6 @@ using osu.Game.Resources.Localisation.Web; using osu.Game.Users; using osuTK; using osuTK.Graphics; -using osu.Game.Localisation; namespace osu.Game.Overlays.BeatmapSet.Buttons { @@ -115,7 +114,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons { new OsuSpriteText { - Text = DownloadButtonStrings.Downloading, + Text = Localisation.CommonStrings.Downloading, Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold) }, }; @@ -126,7 +125,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons { new OsuSpriteText { - Text = DownloadButtonStrings.Importing, + Text = Localisation.CommonStrings.Importing, Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold) }, }; From 9b4c806855b2e4ce7371983a83f13e6c7f8d65d0 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 20 Aug 2021 09:47:23 +0200 Subject: [PATCH 1374/2442] Apply review suggestions. --- osu.Game/Overlays/BeatmapSet/BasicStats.cs | 27 +++++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/BasicStats.cs b/osu.Game/Overlays/BeatmapSet/BasicStats.cs index 80dc185bb5..2dcb2f1777 100644 --- a/osu.Game/Overlays/BeatmapSet/BasicStats.cs +++ b/osu.Game/Overlays/BeatmapSet/BasicStats.cs @@ -81,10 +81,26 @@ namespace osu.Game.Overlays.BeatmapSet Direction = FillDirection.Horizontal, Children = new[] { - length = new Statistic(BeatmapStatisticsIconType.Length, BeatmapsetsStrings.ShowStatsTotalLength(string.Empty)) { Width = 0.25f }, - bpm = new Statistic(BeatmapStatisticsIconType.Bpm, BeatmapsetsStrings.ShowStatsBpm) { Width = 0.25f }, - circleCount = new Statistic(BeatmapStatisticsIconType.Circles, BeatmapsetsStrings.ShowStatsCountCircles) { Width = 0.25f }, - sliderCount = new Statistic(BeatmapStatisticsIconType.Sliders, BeatmapsetsStrings.ShowStatsCountSliders) { Width = 0.25f }, + length = new Statistic(BeatmapStatisticsIconType.Length) + { + Width = 0.25f, + TooltipText = default, + }, + bpm = new Statistic(BeatmapStatisticsIconType.Bpm) + { + Width = 0.25f, + TooltipText = BeatmapsetsStrings.ShowStatsBpm + }, + circleCount = new Statistic(BeatmapStatisticsIconType.Circles) + { + Width = 0.25f, + TooltipText = BeatmapsetsStrings.ShowStatsCountCircles + }, + sliderCount = new Statistic(BeatmapStatisticsIconType.Sliders) + { + Width = 0.25f, + TooltipText = BeatmapsetsStrings.ShowStatsCountSliders + }, }, }; } @@ -107,9 +123,8 @@ namespace osu.Game.Overlays.BeatmapSet set => this.value.Text = value; } - public Statistic(BeatmapStatisticsIconType icon, LocalisableString name) + public Statistic(BeatmapStatisticsIconType icon) { - TooltipText = name; RelativeSizeAxes = Axes.X; Height = 24f; From cff7b1e98f495b11c34d3d92850c7edfa9cd4e1a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Aug 2021 16:50:27 +0900 Subject: [PATCH 1375/2442] Ensure the correct fade level is applied over all state changes --- osu.Game/Overlays/Settings/SettingsSection.cs | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index 8d98ae484f..5c38dee356 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -102,28 +102,20 @@ namespace osu.Game.Overlays.Settings }); selectedSection = settingsPanel.CurrentSection.GetBoundCopy(); - selectedSection.BindValueChanged(selected => - { - if (selected.NewValue == this) - content.FadeIn(500, Easing.OutQuint); - else - content.FadeTo(0.25f, 500, Easing.OutQuint); - }, true); + selectedSection.BindValueChanged(_ => updateContentFade(), true); } private bool isCurrentSection => selectedSection.Value == this; protected override bool OnHover(HoverEvent e) { - if (!isCurrentSection) - content.FadeTo(0.6f, 500, Easing.OutQuint); + updateContentFade(); return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { - if (!isCurrentSection) - content.FadeTo(0.25f, 500, Easing.OutQuint); + updateContentFade(); base.OnHoverLost(e); } @@ -135,9 +127,19 @@ namespace osu.Game.Overlays.Settings return base.OnClick(e); } - protected override bool ShouldBeConsideredForInput(Drawable child) + protected override bool ShouldBeConsideredForInput(Drawable child) => + // only the current section should accept input. + // this provides the behaviour of the first click scrolling the target section to the centre of the screen. + isCurrentSection; + + private void updateContentFade() { - return isCurrentSection; + float targetFade = 1; + + if (!isCurrentSection) + targetFade = IsHovered ? 0.6f : 0.25f; + + content.FadeTo(targetFade, 500, Easing.OutQuint); } } } From c7266c74a09bcfdcdf94d95049f224d03a034397 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Aug 2021 17:00:20 +0900 Subject: [PATCH 1376/2442] Always prefer clicked section when present --- .../Graphics/Containers/SectionsContainer.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index e11d1e1300..4b368b98ad 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -23,7 +23,7 @@ namespace osu.Game.Graphics.Containers { public Bindable SelectedSection { get; } = new Bindable(); - private Drawable lastClickedSection; + private T lastClickedSection; public Drawable ExpandableHeader { @@ -145,10 +145,12 @@ namespace osu.Game.Graphics.Containers footerHeight = null; } - public void ScrollTo(Drawable section) + public void ScrollTo(Drawable target) { - lastClickedSection = section; - scrollContainer.ScrollTo(scrollContainer.GetChildPosInContent(section) - scrollContainer.DisplayableContent * scroll_y_centre - (FixedHeader?.BoundingBox.Height ?? 0)); + if (target is T section) + lastClickedSection = section; + + scrollContainer.ScrollTo(scrollContainer.GetChildPosInContent(target) - scrollContainer.DisplayableContent * scroll_y_centre - (FixedHeader?.BoundingBox.Height ?? 0)); } public void ScrollToTop() => scrollContainer.ScrollTo(0); @@ -236,10 +238,12 @@ namespace osu.Game.Graphics.Containers var presentChildren = Children.Where(c => c.IsPresent); - if (Precision.AlmostBigger(0, scrollContainer.Current)) - SelectedSection.Value = lastClickedSection as T ?? presentChildren.FirstOrDefault(); + if (lastClickedSection != null) + SelectedSection.Value = lastClickedSection; + else if (Precision.AlmostBigger(0, scrollContainer.Current)) + SelectedSection.Value = presentChildren.FirstOrDefault(); else if (Precision.AlmostBigger(scrollContainer.Current, scrollContainer.ScrollableExtent)) - SelectedSection.Value = lastClickedSection as T ?? presentChildren.LastOrDefault(); + SelectedSection.Value = presentChildren.LastOrDefault(); else { SelectedSection.Value = presentChildren From 8524937c82c6bc504de2528748d8667e005e563f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 20 Aug 2021 17:02:55 +0900 Subject: [PATCH 1377/2442] Move screenstack up a level --- osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 576598cb1e..2e1d353d47 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -77,17 +77,7 @@ namespace osu.Game.Screens.OnlinePlay RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - screenStack = new OnlinePlaySubScreenStack - { - RelativeSizeAxes = Axes.Both - } - } - }, + screenStack = new OnlinePlaySubScreenStack { RelativeSizeAxes = Axes.Both }, new Header(ScreenTitle, screenStack), RoomManager, ongoingOperationTracker From 3d96da84e6c3362fb6252cd086ec870050f72c1d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 20 Aug 2021 17:05:46 +0900 Subject: [PATCH 1378/2442] Set a default background --- .../Screens/OnlinePlay/Components/RoomBackgroundScreen.cs | 8 ++++++++ osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs | 1 - 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomBackgroundScreen.cs b/osu.Game/Screens/OnlinePlay/Components/RoomBackgroundScreen.cs index c6e467233e..efcfa35e19 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomBackgroundScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomBackgroundScreen.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Online.Rooms; @@ -20,10 +21,17 @@ namespace osu.Game.Screens.OnlinePlay.Components private readonly BindableList playlist = new BindableList(); public RoomBackgroundScreen() + : base(false) { playlist.BindCollectionChanged((_, __) => updateBackground()); } + [BackgroundDependencyLoader] + private void load() + { + switchBackground(new PlaylistItemBackground(null)); + } + private Room? room; public Room? Room diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 2e1d353d47..76815172e5 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -6,7 +6,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Graphics.Containers; From 139ff2d6e2a5a242ac2be92ba8737e81132d4f5c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Aug 2021 17:40:03 +0900 Subject: [PATCH 1379/2442] Only fade header in when hovering a section Feels less like the controls are interactive when hovering this way. --- osu.Game/Overlays/Settings/SettingsSection.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index 5c38dee356..6f167bf059 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -23,7 +23,7 @@ namespace osu.Game.Overlays.Settings private IBindable selectedSection; - private Container content; + private OsuSpriteText header; public abstract Drawable CreateIcon(); public abstract LocalisableString Header { get; } @@ -70,11 +70,12 @@ namespace osu.Game.Overlays.Settings { new Box { + Name = "separator", Colour = new Color4(0, 0, 0, 255), RelativeSizeAxes = Axes.X, Height = border_size, }, - content = new Container + new Container { Padding = new MarginPadding { @@ -85,7 +86,7 @@ namespace osu.Game.Overlays.Settings AutoSizeAxes = Axes.Y, Children = new Drawable[] { - new OsuSpriteText + header = new OsuSpriteText { Font = OsuFont.GetFont(size: header_size), Text = Header, @@ -134,12 +135,17 @@ namespace osu.Game.Overlays.Settings private void updateContentFade() { - float targetFade = 1; + float contentFade = 1; + float headerFade = 1; if (!isCurrentSection) - targetFade = IsHovered ? 0.6f : 0.25f; + { + contentFade = 0.25f; + headerFade = IsHovered ? 0.5f : 0.25f; + } - content.FadeTo(targetFade, 500, Easing.OutQuint); + header.FadeTo(headerFade, 500, Easing.OutQuint); + FlowContent.FadeTo(contentFade, 500, Easing.OutQuint); } } } From 2d19f37dc6100a605b725e8d1eeb938109c43e06 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Aug 2021 17:40:41 +0900 Subject: [PATCH 1380/2442] Add missing `new` method in `UserTrackingScrollContainer` for scrolling into view --- osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs b/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs index 17506ce0f5..0561051e35 100644 --- a/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs +++ b/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs @@ -42,6 +42,12 @@ namespace osu.Game.Graphics.Containers base.OnUserScroll(value, animated, distanceDecay); } + public new void ScrollIntoView(Drawable target, bool animated = true) + { + UserScrolling = false; + base.ScrollIntoView(target, animated); + } + public new void ScrollTo(float value, bool animated = true, double? distanceDecay = null) { UserScrolling = false; From 03e6ca5ba930830960bb5541f7b7be6fb2dc0f9a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Aug 2021 17:40:56 +0900 Subject: [PATCH 1381/2442] Adjust scroll behaviour to feel better --- .../Graphics/Containers/SectionsContainer.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 4b368b98ad..d0aa885f3e 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -112,7 +112,7 @@ namespace osu.Game.Graphics.Containers /// /// The percentage of the container to consider the centre-point for deciding the active section (and scrolling to a requested section). /// - private const float scroll_y_centre = 0.2f; + private const float scroll_y_centre = 0.1f; public SectionsContainer() { @@ -147,10 +147,22 @@ namespace osu.Game.Graphics.Containers public void ScrollTo(Drawable target) { + lastKnownScroll = null; + + float fixedHeaderSize = FixedHeader?.BoundingBox.Height ?? 0; + + // implementation similar to ScrollIntoView but a bit more nuanced. + float top = scrollContainer.GetChildPosInContent(target); + + var bottomScrollExtent = scrollContainer.ScrollableExtent - fixedHeaderSize; + + if (top > bottomScrollExtent) + scrollContainer.ScrollToEnd(); + else + scrollContainer.ScrollTo(top - fixedHeaderSize - scrollContainer.DisplayableContent * scroll_y_centre); + if (target is T section) lastClickedSection = section; - - scrollContainer.ScrollTo(scrollContainer.GetChildPosInContent(target) - scrollContainer.DisplayableContent * scroll_y_centre - (FixedHeader?.BoundingBox.Height ?? 0)); } public void ScrollToTop() => scrollContainer.ScrollTo(0); From 258ba4674cf210b66e9ca29405b4764c5989d04e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 20 Aug 2021 17:50:49 +0900 Subject: [PATCH 1382/2442] Fix background overflows --- osu.Game/Screens/BackgroundScreen.cs | 18 +++++++-------- osu.Game/Screens/BackgroundScreenStack.cs | 4 +++- .../Components/RoomBackgroundScreen.cs | 22 +++++++++++++++++++ 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/BackgroundScreen.cs b/osu.Game/Screens/BackgroundScreen.cs index a6fb94b151..a706934cce 100644 --- a/osu.Game/Screens/BackgroundScreen.cs +++ b/osu.Game/Screens/BackgroundScreen.cs @@ -11,6 +11,9 @@ namespace osu.Game.Screens { public abstract class BackgroundScreen : Screen, IEquatable { + protected const float TRANSITION_LENGTH = 500; + private const float x_movement_amount = 50; + private readonly bool animateOnEnter; public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks; @@ -27,9 +30,6 @@ namespace osu.Game.Screens return other?.GetType() == GetType(); } - private const float transition_length = 500; - private const float x_movement_amount = 50; - protected override bool OnKeyDown(KeyDownEvent e) { // we don't want to handle escape key. @@ -55,8 +55,8 @@ namespace osu.Game.Screens this.FadeOut(); this.MoveToX(x_movement_amount); - this.FadeIn(transition_length, Easing.InOutQuart); - this.MoveToX(0, transition_length, Easing.InOutQuart); + this.FadeIn(TRANSITION_LENGTH, Easing.InOutQuart); + this.MoveToX(0, TRANSITION_LENGTH, Easing.InOutQuart); } base.OnEntering(last); @@ -64,7 +64,7 @@ namespace osu.Game.Screens public override void OnSuspending(IScreen next) { - this.MoveToX(-x_movement_amount, transition_length, Easing.InOutQuart); + this.MoveToX(-x_movement_amount, TRANSITION_LENGTH, Easing.InOutQuart); base.OnSuspending(next); } @@ -72,8 +72,8 @@ namespace osu.Game.Screens { if (IsLoaded) { - this.FadeOut(transition_length, Easing.OutExpo); - this.MoveToX(x_movement_amount, transition_length, Easing.OutExpo); + this.FadeOut(TRANSITION_LENGTH, Easing.OutExpo); + this.MoveToX(x_movement_amount, TRANSITION_LENGTH, Easing.OutExpo); } return base.OnExiting(next); @@ -82,7 +82,7 @@ namespace osu.Game.Screens public override void OnResuming(IScreen last) { if (IsLoaded) - this.MoveToX(0, transition_length, Easing.OutExpo); + this.MoveToX(0, TRANSITION_LENGTH, Easing.OutExpo); base.OnResuming(last); } } diff --git a/osu.Game/Screens/BackgroundScreenStack.cs b/osu.Game/Screens/BackgroundScreenStack.cs index 9c0c5da0fb..17894d2474 100644 --- a/osu.Game/Screens/BackgroundScreenStack.cs +++ b/osu.Game/Screens/BackgroundScreenStack.cs @@ -10,10 +10,12 @@ namespace osu.Game.Screens { public class BackgroundScreenStack : ScreenStack { + public const float BACKGROUND_SCALE = 1.06f; + public BackgroundScreenStack() : base(false) { - Scale = new Vector2(1.06f); + Scale = new Vector2(BACKGROUND_SCALE); RelativeSizeAxes = Axes.Both; Anchor = Anchor.Centre; Origin = Anchor.Centre; diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomBackgroundScreen.cs b/osu.Game/Screens/OnlinePlay/Components/RoomBackgroundScreen.cs index efcfa35e19..393cf49bb7 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomBackgroundScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomBackgroundScreen.cs @@ -8,6 +8,7 @@ using System.Threading; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Screens; using osu.Game.Online.Rooms; using osuTK; @@ -86,5 +87,26 @@ namespace osu.Game.Screens.OnlinePlay.Components AddInternal(background = newBackground); } + + protected override void Update() + { + base.Update(); + + // This is a static screen, so override the scale set in base.Update(), but also the scale set by the screen stack. + Scale = new Vector2(1f / BackgroundScreenStack.BACKGROUND_SCALE); + } + + public override void OnSuspending(IScreen next) + { + base.OnSuspending(next); + this.MoveToX(0, TRANSITION_LENGTH); + } + + public override bool OnExiting(IScreen next) + { + var result = base.OnExiting(next); + this.MoveToX(0); + return result; + } } } From 591ba8cb099a0ab4d9488fea7877ea4304c6179e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Aug 2021 17:56:35 +0900 Subject: [PATCH 1383/2442] Ensure the final scroll target is used when checking for whether too far down --- osu.Game/Graphics/Containers/SectionsContainer.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index d0aa885f3e..76492cab55 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -154,12 +154,13 @@ namespace osu.Game.Graphics.Containers // implementation similar to ScrollIntoView but a bit more nuanced. float top = scrollContainer.GetChildPosInContent(target); - var bottomScrollExtent = scrollContainer.ScrollableExtent - fixedHeaderSize; + float bottomScrollExtent = scrollContainer.ScrollableExtent - fixedHeaderSize; + float scrollTarget = top - fixedHeaderSize - scrollContainer.DisplayableContent * scroll_y_centre; - if (top > bottomScrollExtent) + if (scrollTarget > bottomScrollExtent) scrollContainer.ScrollToEnd(); else - scrollContainer.ScrollTo(top - fixedHeaderSize - scrollContainer.DisplayableContent * scroll_y_centre); + scrollContainer.ScrollTo(scrollTarget); if (target is T section) lastClickedSection = section; From c22c6f3a49057544546594d2d4d34c9a255c2f02 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 20 Aug 2021 18:14:12 +0900 Subject: [PATCH 1384/2442] Initial room background implementation --- ...creen.cs => OnlinePlayBackgroundScreen.cs} | 35 +++++---------- .../Lounge/LoungeBackgroundScreen.cs | 45 +++++++++++++++++++ .../OnlinePlay/Lounge/LoungeSubScreen.cs | 4 +- .../OnlinePlay/Match/RoomBackgroundScreen.cs | 19 ++++++++ .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 5 +++ 5 files changed, 83 insertions(+), 25 deletions(-) rename osu.Game/Screens/OnlinePlay/Components/{RoomBackgroundScreen.cs => OnlinePlayBackgroundScreen.cs} (74%) create mode 100644 osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs create mode 100644 osu.Game/Screens/OnlinePlay/Match/RoomBackgroundScreen.cs diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomBackgroundScreen.cs b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs similarity index 74% rename from osu.Game/Screens/OnlinePlay/Components/RoomBackgroundScreen.cs rename to osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs index 393cf49bb7..5077979bf0 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomBackgroundScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs @@ -1,57 +1,47 @@ // 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.Linq; using System.Threading; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Game.Online.Rooms; using osuTK; +#nullable enable + namespace osu.Game.Screens.OnlinePlay.Components { - public class RoomBackgroundScreen : BackgroundScreen + public abstract class OnlinePlayBackgroundScreen : BackgroundScreen { private CancellationTokenSource? cancellationSource; private PlaylistItemBackground? background; - private readonly BindableList playlist = new BindableList(); - - public RoomBackgroundScreen() + protected OnlinePlayBackgroundScreen() : base(false) { - playlist.BindCollectionChanged((_, __) => updateBackground()); } [BackgroundDependencyLoader] private void load() { - switchBackground(new PlaylistItemBackground(null)); + switchBackground(new PlaylistItemBackground(playlistItem)); } - private Room? room; + private PlaylistItem? playlistItem; - public Room? Room + protected PlaylistItem? PlaylistItem { - get => room; + get => playlistItem; set { - if (room == value) + if (playlistItem == value) return; - if (room != null) - playlist.UnbindFrom(room.Playlist); + playlistItem = value; - room = value; - - if (room != null) - playlist.BindTo(room.Playlist); - else - playlist.Clear(); + if (LoadState > LoadState.Ready) + updateBackground(); } } @@ -59,7 +49,6 @@ namespace osu.Game.Screens.OnlinePlay.Components { Schedule(() => { - var playlistItem = playlist.FirstOrDefault(); var beatmap = playlistItem?.Beatmap.Value; if (background?.BeatmapInfo?.BeatmapSet?.OnlineInfo?.Covers?.Cover == beatmap?.BeatmapSet?.OnlineInfo?.Covers?.Cover) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs new file mode 100644 index 0000000000..7d38e220d3 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs @@ -0,0 +1,45 @@ +// 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.Linq; +using osu.Framework.Bindables; +using osu.Game.Online.Rooms; +using osu.Game.Screens.OnlinePlay.Components; +using PlaylistItem = osu.Game.Online.Rooms.PlaylistItem; + +namespace osu.Game.Screens.OnlinePlay.Lounge +{ + public class LoungeBackgroundScreen : OnlinePlayBackgroundScreen + { + private readonly BindableList playlist = new BindableList(); + + public LoungeBackgroundScreen() + { + playlist.BindCollectionChanged((_, __) => PlaylistItem = playlist.FirstOrDefault()); + } + + private Room? selectedRoom; + + public Room? SelectedRoom + { + get => selectedRoom; + set + { + if (selectedRoom == value) + return; + + if (selectedRoom != null) + playlist.UnbindFrom(selectedRoom.Playlist); + + selectedRoom = value; + + if (selectedRoom != null) + playlist.BindTo(selectedRoom.Playlist); + else + playlist.Clear(); + } + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 34beba923f..90e5a62d7e 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -36,7 +36,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { public override string Title => "Lounge"; - protected override BackgroundScreen CreateBackground() => new RoomBackgroundScreen(); + protected override BackgroundScreen CreateBackground() => new LoungeBackgroundScreen(); protected override UserActivity InitialActivity => new UserActivity.SearchingForLobby(); @@ -169,7 +169,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge if (drawable != null) scrollContainer.ScrollIntoView(drawable); - ApplyToBackground(b => ((RoomBackgroundScreen)b).Room = val.NewValue); + ApplyToBackground(b => ((LoungeBackgroundScreen)b).SelectedRoom = val.NewValue); }); } diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomBackgroundScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomBackgroundScreen.cs new file mode 100644 index 0000000000..cacc3864c2 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Match/RoomBackgroundScreen.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osu.Game.Online.Rooms; +using osu.Game.Screens.OnlinePlay.Components; + +namespace osu.Game.Screens.OnlinePlay.Match +{ + public class RoomBackgroundScreen : OnlinePlayBackgroundScreen + { + public readonly Bindable SelectedItem = new Bindable(); + + public RoomBackgroundScreen() + { + SelectedItem.BindValueChanged(item => PlaylistItem = item.NewValue); + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index d6a23ff708..8097e91c2e 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -29,6 +29,11 @@ namespace osu.Game.Screens.OnlinePlay.Match [Cached(typeof(IBindable))] protected readonly Bindable SelectedItem = new Bindable(); + protected override BackgroundScreen CreateBackground() => new RoomBackgroundScreen + { + SelectedItem = { BindTarget = SelectedItem } + }; + public override bool DisallowExternalBeatmapRulesetChanges => true; /// From d304e283e4a24506923f48097b84494086ef38d6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 20 Aug 2021 18:14:59 +0900 Subject: [PATCH 1385/2442] Don't deselect online room when joined --- osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index 4e106b844c..70342680e2 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -172,8 +172,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge sampleJoin?.Play(); lounge?.Join(Room, null); - - return base.OnClick(e); + return true; } public class PasswordEntryPopover : OsuPopover From cbee379f62cdc49b8227067fd952f5bd57bbe172 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Fri, 20 Aug 2021 12:30:49 +0300 Subject: [PATCH 1386/2442] Test scrolled to section top is visible --- .../UserInterface/TestSceneSectionsContainer.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs index 5c2e6e457d..2312c57af2 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs @@ -6,6 +6,7 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Shapes; +using osu.Framework.Testing; using osu.Game.Graphics.Containers; using osuTK.Graphics; @@ -61,10 +62,12 @@ namespace osu.Game.Tests.Visual.UserInterface )); AddStep("scroll up", () => triggerUserScroll(1)); AddStep("scroll down", () => triggerUserScroll(-1)); + AddStep("scroll up a bit", () => triggerUserScroll(0.1f)); + AddStep("scroll down a bit", () => triggerUserScroll(-0.1f)); } [Test] - public void TestCorrectSectionSelected() + public void TestCorrectSelectionAndVisibleTop() { const int sections_count = 11; float[] alternating = { 0.07f, 0.33f, 0.16f, 0.33f }; @@ -79,6 +82,12 @@ namespace osu.Game.Tests.Visual.UserInterface { AddStep($"scroll to section {scrollIndex + 1}", () => container.ScrollTo(container.Children[scrollIndex])); AddUntilStep("correct section selected", () => container.SelectedSection.Value == container.Children[scrollIndex]); + AddUntilStep("section top is visible", () => + { + float scrollPosition = container.ChildrenOfType().First().Current; + float sectionTop = container.Children[scrollIndex].BoundingBox.Top; + return scrollPosition < sectionTop; + }); } for (int i = 1; i < sections_count; i++) From 77149044a5954278d9deef2b014c5fe267033915 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Aug 2021 19:33:47 +0900 Subject: [PATCH 1387/2442] Allow intro screen to retrieve beatmap even if rulesets is not loaded --- osu.Game/Beatmaps/BeatmapManager.cs | 36 +++++++++++++++++++++++++++- osu.Game/Beatmaps/BeatmapStore.cs | 7 ++++++ osu.Game/Screens/Menu/IntroScreen.cs | 3 ++- 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 4a78ceb299..241649062e 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -365,6 +365,10 @@ namespace osu.Game.Beatmaps queryable = beatmaps.BeatmapSetsOverview; break; + case IncludedDetails.AllButRuleset: + queryable = beatmaps.BeatmapSetsWithoutRuleset; + break; + case IncludedDetails.AllButFiles: queryable = beatmaps.BeatmapSetsWithoutFiles; break; @@ -384,8 +388,33 @@ namespace osu.Game.Beatmaps /// Perform a lookup query on available s. /// /// The query. + /// The level of detail to include in the returned objects. /// Results from the provided query. - public IEnumerable QueryBeatmapSets(Expression> query) => beatmaps.ConsumableItems.AsNoTracking().Where(query); + public IEnumerable QueryBeatmapSets(Expression> query, IncludedDetails includes = IncludedDetails.All) + { + IQueryable queryable; + + switch (includes) + { + case IncludedDetails.Minimal: + queryable = beatmaps.BeatmapSetsOverview; + break; + + case IncludedDetails.AllButRuleset: + queryable = beatmaps.BeatmapSetsWithoutRuleset; + break; + + case IncludedDetails.AllButFiles: + queryable = beatmaps.BeatmapSetsWithoutFiles; + break; + + default: + queryable = beatmaps.ConsumableItems; + break; + } + + return queryable.AsNoTracking().Where(query); + } /// /// Perform a lookup query on available s. @@ -554,6 +583,11 @@ namespace osu.Game.Beatmaps /// AllButFiles, + /// + /// Include everything except ruleset. Used for cases where we aren't sure the ruleset is present but still want to consume the beatmap. + /// + AllButRuleset, + /// /// Include everything. /// diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index 642bafd2ac..e3214b7c03 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -92,6 +92,13 @@ namespace osu.Game.Beatmaps .Include(s => s.Beatmaps) .AsNoTracking(); + public IQueryable BeatmapSetsWithoutRuleset => ContextFactory.Get().BeatmapSetInfo + .Include(s => s.Metadata) + .Include(s => s.Files).ThenInclude(f => f.FileInfo) + .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty) + .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata) + .AsNoTracking(); + public IQueryable BeatmapSetsWithoutFiles => ContextFactory.Get().BeatmapSetInfo .Include(s => s.Metadata) .Include(s => s.Beatmaps).ThenInclude(s => s.Ruleset) diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index 71f3b60026..07a94fb97e 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -109,7 +110,7 @@ namespace osu.Game.Screens.Menu bool loadThemedIntro() { - setInfo = beatmaps.QueryBeatmapSet(b => b.Hash == BeatmapHash); + setInfo = beatmaps.QueryBeatmapSets(b => b.Hash == BeatmapHash, IncludedDetails.AllButRuleset).FirstOrDefault(); if (setInfo == null) return false; From b9ff94485db48af93f12ea0fbd879737d76a7bd0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Aug 2021 19:45:52 +0900 Subject: [PATCH 1388/2442] Revert usage of `OsuGameTestScene` for `TestSceneOsuGame` Turns out we likely don't want this, as it means the testing user (using a visual test browser) will not have access to their beatmaps. Can revisit at a future date if the temporary files are still an issue. --- .../TestSceneOsuGame.cs | 23 +++++++++++- .../TestSceneOsuGame.cs | 23 +++++++++++- .../TestSceneOsuGame.cs | 23 +++++++++++- .../TestSceneOsuGame.cs | 23 +++++++++++- .../Visual/Navigation/TestSceneOsuGame.cs | 37 ++++++++++++++++++- 5 files changed, 123 insertions(+), 6 deletions(-) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/TestSceneOsuGame.cs b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/TestSceneOsuGame.cs index fb63f69f72..9c512a01ea 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/TestSceneOsuGame.cs +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/TestSceneOsuGame.cs @@ -1,11 +1,32 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Platform; using osu.Game.Tests.Visual; +using osuTK.Graphics; namespace osu.Game.Rulesets.EmptyFreeform.Tests { - public class TestSceneOsuGame : OsuGameTestScene + public class TestSceneOsuGame : OsuTestScene { + [BackgroundDependencyLoader] + private void load(GameHost host, OsuGameBase gameBase) + { + OsuGame game = new OsuGame(); + game.SetHost(host); + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + game + }; + } } } diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs index a99a400afa..270d906b01 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs @@ -1,11 +1,32 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Platform; using osu.Game.Tests.Visual; +using osuTK.Graphics; namespace osu.Game.Rulesets.Pippidon.Tests { - public class TestSceneOsuGame : OsuGameTestScene + public class TestSceneOsuGame : OsuTestScene { + [BackgroundDependencyLoader] + private void load(GameHost host, OsuGameBase gameBase) + { + OsuGame game = new OsuGame(); + game.SetHost(host); + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + game + }; + } } } diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/TestSceneOsuGame.cs b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/TestSceneOsuGame.cs index debdc14b57..aed6abb6bf 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/TestSceneOsuGame.cs +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/TestSceneOsuGame.cs @@ -1,11 +1,32 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Platform; using osu.Game.Tests.Visual; +using osuTK.Graphics; namespace osu.Game.Rulesets.EmptyScrolling.Tests { - public class TestSceneOsuGame : OsuGameTestScene + public class TestSceneOsuGame : OsuTestScene { + [BackgroundDependencyLoader] + private void load(GameHost host, OsuGameBase gameBase) + { + OsuGame game = new OsuGame(); + game.SetHost(host); + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + game + }; + } } } diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs index a99a400afa..270d906b01 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs @@ -1,11 +1,32 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Platform; using osu.Game.Tests.Visual; +using osuTK.Graphics; namespace osu.Game.Rulesets.Pippidon.Tests { - public class TestSceneOsuGame : OsuGameTestScene + public class TestSceneOsuGame : OsuTestScene { + [BackgroundDependencyLoader] + private void load(GameHost host, OsuGameBase gameBase) + { + OsuGame game = new OsuGame(); + game.SetHost(host); + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + game + }; + } } } diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs index d3f1a852d1..48e68b03fb 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs @@ -6,7 +6,11 @@ using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; +using osu.Framework.Platform; +using osu.Framework.Testing; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Configuration; @@ -24,11 +28,12 @@ using osu.Game.Scoring; using osu.Game.Screens.Menu; using osu.Game.Skinning; using osu.Game.Utils; +using osuTK.Graphics; namespace osu.Game.Tests.Visual.Navigation { [TestFixture] - public class TestSceneOsuGame : OsuGameTestScene + public class TestSceneOsuGame : OsuTestScene { private IReadOnlyList requiredGameDependencies => new[] { @@ -79,9 +84,37 @@ namespace osu.Game.Tests.Visual.Navigation typeof(PreviewTrackManager), }; + private OsuGame game; + [Resolved] private OsuGameBase gameBase { get; set; } + [Resolved] + private GameHost host { get; set; } + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("create game", () => + { + game = new OsuGame(); + game.SetHost(host); + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + game + }; + }); + + AddUntilStep("wait for load", () => game.IsLoaded); + } + + [Test] public void TestNullRulesetHandled() { @@ -117,7 +150,7 @@ namespace osu.Game.Tests.Visual.Navigation { foreach (var type in requiredGameDependencies) { - if (Game.Dependencies.Get(type) == null) + if (game.Dependencies.Get(type) == null) throw new InvalidOperationException($"{type} has not been cached"); } From b190020c4b78d0dab21a33d22c9f7637a3124fbb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 20 Aug 2021 21:02:25 +0900 Subject: [PATCH 1389/2442] Block lounge background screen from exiting --- .../Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs index 7d38e220d3..743fb1fbbf 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs @@ -5,6 +5,7 @@ using System.Linq; using osu.Framework.Bindables; +using osu.Framework.Screens; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Components; using PlaylistItem = osu.Game.Online.Rooms.PlaylistItem; @@ -41,5 +42,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge playlist.Clear(); } } + + public override bool OnExiting(IScreen next) + { + // This screen never exits. + return true; + } } } From 9458cd5a31476d3d1955fc5d595080f91f3a7e0c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 20 Aug 2021 21:07:51 +0900 Subject: [PATCH 1390/2442] Make DrawableMatchRoom background load instantly --- .../OnlinePlay/Match/DrawableMatchRoom.cs | 13 +++++++- .../OnlinePlay/Match/RoomBackgroundSprite.cs | 33 ------------------- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 3 +- 3 files changed, 14 insertions(+), 35 deletions(-) delete mode 100644 osu.Game/Screens/OnlinePlay/Match/RoomBackgroundSprite.cs diff --git a/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs index 0924773338..e83403850f 100644 --- a/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs @@ -6,6 +6,7 @@ using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Game.Beatmaps.Drawables; using osu.Game.Online.API; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Lounge.Components; @@ -17,6 +18,7 @@ namespace osu.Game.Screens.OnlinePlay.Match { public class DrawableMatchRoom : DrawableRoom { + public readonly IBindable SelectedItem = new Bindable(); public Action OnEdit; [Resolved] @@ -28,6 +30,8 @@ namespace osu.Game.Screens.OnlinePlay.Match [CanBeNull] private Drawable editButton; + private BackgroundSprite background; + public DrawableMatchRoom(Room room, bool allowEdit = true) : base(room) { @@ -57,8 +61,15 @@ namespace osu.Game.Screens.OnlinePlay.Match if (editButton != null) host.BindValueChanged(h => editButton.Alpha = h.NewValue?.Equals(api.LocalUser.Value) == true ? 1 : 0, true); + + SelectedItem.BindValueChanged(item => background.Beatmap.Value = item.NewValue?.Beatmap.Value, true); } - protected override Drawable CreateBackground() => new RoomBackgroundSprite(); + protected override Drawable CreateBackground() => background = new BackgroundSprite(); + + private class BackgroundSprite : UpdateableBeatmapBackgroundSprite + { + protected override double LoadDelay => 0; + } } } diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomBackgroundSprite.cs b/osu.Game/Screens/OnlinePlay/Match/RoomBackgroundSprite.cs deleted file mode 100644 index 97262dd229..0000000000 --- a/osu.Game/Screens/OnlinePlay/Match/RoomBackgroundSprite.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Beatmaps.Drawables; - -namespace osu.Game.Screens.OnlinePlay.Match -{ - public class RoomBackgroundSprite : RoomSubScreenComposite - { - protected readonly BeatmapSetCoverType BeatmapSetCoverType; - private UpdateableBeatmapBackgroundSprite sprite; - - public RoomBackgroundSprite(BeatmapSetCoverType beatmapSetCoverType = BeatmapSetCoverType.Cover) - { - BeatmapSetCoverType = beatmapSetCoverType; - } - - [BackgroundDependencyLoader] - private void load() - { - InternalChild = sprite = new UpdateableBeatmapBackgroundSprite(BeatmapSetCoverType) { RelativeSizeAxes = Axes.Both }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - SelectedItem.BindValueChanged(item => sprite.Beatmap.Value = item.NewValue?.Beatmap.Value, true); - } - } -} diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 8097e91c2e..214540fe10 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -139,7 +139,8 @@ namespace osu.Game.Screens.OnlinePlay.Match { new DrawableMatchRoom(Room, allowEdit) { - OnEdit = () => settingsOverlay.Show() + OnEdit = () => settingsOverlay.Show(), + SelectedItem = { BindTarget = SelectedItem } } }, null, From b1a732b9b7928d73d76db540e2feffece095aae2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 20 Aug 2021 21:28:48 +0900 Subject: [PATCH 1391/2442] Remove selectedRoom from OnlinePlayScreen --- osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 4 +--- osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs | 11 ----------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 90e5a62d7e..e2a4f3dfdf 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -49,9 +49,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge protected ListingPollingComponent ListingPollingComponent { get; private set; } - [Resolved] - private Bindable selectedRoom { get; set; } - [Resolved] private MusicController music { get; set; } @@ -68,6 +65,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private LeasedBindable selectionLease; private readonly Bindable filter = new Bindable(new FilterCriteria()); + private readonly Bindable selectedRoom = new Bindable(); private readonly IBindable operationInProgress = new Bindable(); private readonly IBindable isIdle = new BindableBool(); private LoadingLayer loadingLayer; diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 76815172e5..fc20b21b60 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -10,7 +10,6 @@ using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Graphics.Containers; using osu.Game.Online.API; -using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Screens.Menu; using osu.Game.Screens.OnlinePlay.Components; @@ -38,9 +37,6 @@ namespace osu.Game.Screens.OnlinePlay [Cached(Type = typeof(IRoomManager))] protected RoomManager RoomManager { get; private set; } - [Cached] - private readonly Bindable selectedRoom = new Bindable(); - [Cached] private readonly OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker(); @@ -106,13 +102,6 @@ namespace osu.Game.Screens.OnlinePlay apiState.BindValueChanged(onlineStateChanged, true); } - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) - { - var dependencies = new CachedModelDependencyContainer(base.CreateChildDependencies(parent)); - dependencies.Model.BindTo(selectedRoom); - return dependencies; - } - private void forcefullyExit() { // This is temporary since we don't currently have a way to force screens to be exited From 5c8ca32ea4b94387d082b8a994d261cb2a62c717 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 20 Aug 2021 21:33:21 +0900 Subject: [PATCH 1392/2442] Simplify lounge implementation --- .../Lounge/LoungeBackgroundScreen.cs | 27 +++++++------------ .../OnlinePlay/Lounge/LoungeSubScreen.cs | 4 +-- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 5 +--- 3 files changed, 11 insertions(+), 25 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs index 743fb1fbbf..6c00ca2e81 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs @@ -14,33 +14,24 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { public class LoungeBackgroundScreen : OnlinePlayBackgroundScreen { + public readonly Bindable SelectedRoom = new Bindable(); private readonly BindableList playlist = new BindableList(); public LoungeBackgroundScreen() { + SelectedRoom.BindValueChanged(onSelectedRoomChanged); playlist.BindCollectionChanged((_, __) => PlaylistItem = playlist.FirstOrDefault()); } - private Room? selectedRoom; - - public Room? SelectedRoom + private void onSelectedRoomChanged(ValueChangedEvent room) { - get => selectedRoom; - set - { - if (selectedRoom == value) - return; + if (room.OldValue != null) + playlist.UnbindFrom(room.OldValue.Playlist); - if (selectedRoom != null) - playlist.UnbindFrom(selectedRoom.Playlist); - - selectedRoom = value; - - if (selectedRoom != null) - playlist.BindTo(selectedRoom.Playlist); - else - playlist.Clear(); - } + if (room.NewValue != null) + playlist.BindTo(room.NewValue.Playlist); + else + playlist.Clear(); } public override bool OnExiting(IScreen next) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index e2a4f3dfdf..034deba54a 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -36,7 +36,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { public override string Title => "Lounge"; - protected override BackgroundScreen CreateBackground() => new LoungeBackgroundScreen(); + protected override BackgroundScreen CreateBackground() => new LoungeBackgroundScreen { SelectedRoom = { BindTarget = selectedRoom } }; protected override UserActivity InitialActivity => new UserActivity.SearchingForLobby(); @@ -166,8 +166,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge var drawable = roomsContainer.Rooms.FirstOrDefault(r => r.Room == val.NewValue); if (drawable != null) scrollContainer.ScrollIntoView(drawable); - - ApplyToBackground(b => ((LoungeBackgroundScreen)b).SelectedRoom = val.NewValue); }); } diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 214540fe10..36cd8c4df3 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -29,10 +29,7 @@ namespace osu.Game.Screens.OnlinePlay.Match [Cached(typeof(IBindable))] protected readonly Bindable SelectedItem = new Bindable(); - protected override BackgroundScreen CreateBackground() => new RoomBackgroundScreen - { - SelectedItem = { BindTarget = SelectedItem } - }; + protected override BackgroundScreen CreateBackground() => new RoomBackgroundScreen { SelectedItem = { BindTarget = SelectedItem } }; public override bool DisallowExternalBeatmapRulesetChanges => true; From 5192ee3b5713efe0e167d789d59b897cf894ed99 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 20 Aug 2021 21:40:35 +0900 Subject: [PATCH 1393/2442] Fix initial display in room background --- osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 5 ++++- osu.Game/Screens/OnlinePlay/Match/RoomBackgroundScreen.cs | 3 ++- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 5 ++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 034deba54a..ee51b37849 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -36,7 +36,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { public override string Title => "Lounge"; - protected override BackgroundScreen CreateBackground() => new LoungeBackgroundScreen { SelectedRoom = { BindTarget = selectedRoom } }; + protected override BackgroundScreen CreateBackground() => new LoungeBackgroundScreen + { + SelectedRoom = { BindTarget = selectedRoom } + }; protected override UserActivity InitialActivity => new UserActivity.SearchingForLobby(); diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomBackgroundScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomBackgroundScreen.cs index cacc3864c2..2e5f25370f 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomBackgroundScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomBackgroundScreen.cs @@ -11,8 +11,9 @@ namespace osu.Game.Screens.OnlinePlay.Match { public readonly Bindable SelectedItem = new Bindable(); - public RoomBackgroundScreen() + public RoomBackgroundScreen(PlaylistItem initialPlaylistItem) { + PlaylistItem = initialPlaylistItem; SelectedItem.BindValueChanged(item => PlaylistItem = item.NewValue); } } diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 36cd8c4df3..8ab3f77888 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -29,7 +29,10 @@ namespace osu.Game.Screens.OnlinePlay.Match [Cached(typeof(IBindable))] protected readonly Bindable SelectedItem = new Bindable(); - protected override BackgroundScreen CreateBackground() => new RoomBackgroundScreen { SelectedItem = { BindTarget = SelectedItem } }; + protected override BackgroundScreen CreateBackground() => new RoomBackgroundScreen(Room.Playlist.FirstOrDefault()) + { + SelectedItem = { BindTarget = SelectedItem } + }; public override bool DisallowExternalBeatmapRulesetChanges => true; From f85d3665d82f1edc9444f6bddd5fa5ffa8c36482 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 20 Aug 2021 21:45:24 +0900 Subject: [PATCH 1394/2442] Cleanups --- osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 1 - osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 2 +- .../OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 3 --- osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs | 3 --- 4 files changed, 1 insertion(+), 8 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index ee51b37849..7e898db584 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -235,7 +235,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge public override void OnEntering(IScreen last) { base.OnEntering(last); - onReturning(); } diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 8ab3f77888..c05022a903 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -426,7 +426,7 @@ namespace osu.Game.Screens.OnlinePlay.Match /// /// Creates the room settings overlay. /// - /// + /// The room to change the settings of. protected abstract RoomSettingsOverlay CreateRoomSettingsOverlay(Room room); private class UserModSelectOverlay : LocalPlayerModSelectOverlay diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 1cb3ce0b12..06e60903aa 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -78,9 +78,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer if (!connected.NewValue) handleRoomLost(); }, true); - - // if (client.Room == null) - // handleRoomLost(); } protected override Drawable CreateMainContent() => new GridContainer diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs index 371824577e..3411c4afb1 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs @@ -36,9 +36,7 @@ namespace osu.Game.Screens.OnlinePlay public override bool OnExiting(IScreen next) { base.OnExiting(next); - this.FadeOut(DISAPPEAR_DURATION, Easing.OutQuint); - return false; } @@ -51,7 +49,6 @@ namespace osu.Game.Screens.OnlinePlay public override void OnSuspending(IScreen next) { base.OnSuspending(next); - this.FadeOut(DISAPPEAR_DURATION, Easing.OutQuint); } From 5e234fb7468942ef724c616f893cf7a50ba0853b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Aug 2021 22:07:13 +0900 Subject: [PATCH 1395/2442] Add try catch to avoid test failures on windows --- osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs index c9f5774735..5e14af5c27 100644 --- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -313,9 +313,13 @@ namespace osu.Game.Tests.NonVisual { base.Dispose(isDisposing); - // the storage may have changed from the initial location. - // this handles cleanup of the initial location. - InitialStorage.DeleteDirectory(string.Empty); + try + { + // the storage may have changed from the initial location. + // this handles cleanup of the initial location. + InitialStorage.DeleteDirectory(string.Empty); + } + catch { } } } } From e13b516f31e28c69c74a5bcf83c04d09787291d5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Aug 2021 22:26:36 +0900 Subject: [PATCH 1396/2442] Fix excess blank lines --- osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs index 48e68b03fb..b8232837b5 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs @@ -114,7 +114,6 @@ namespace osu.Game.Tests.Visual.Navigation AddUntilStep("wait for load", () => game.IsLoaded); } - [Test] public void TestNullRulesetHandled() { From d3dba296d672afa4d5c4d7c97154fe68117935a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 20 Aug 2021 23:47:35 +0900 Subject: [PATCH 1397/2442] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index f74b5d410b..8fd761691c 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index e4838dc95a..0abd9adf77 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 5338ad5deb..d03985a0c2 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From a46ae855aa513b53f5a9e70bb9f98ee96af98cc1 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Fri, 20 Aug 2021 15:40:34 +0000 Subject: [PATCH 1398/2442] implemented object count limitation for rhythm build up --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 88861d8bd5..e350a113f7 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -69,8 +69,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills if (currHistoricalDecay != 0) { - // below was bugged in initial version. fixed now, but will change values, will do more testing - // currHistoricalDecay = Math.Min(currHistoricalDecay, (double)(Previous.Count - i) / Previous.Count); // either we're limited by time or limited by object count. + currHistoricalDecay = Math.Min(currHistoricalDecay, (double)(Previous.Count - i) / Previous.Count); // either we're limited by time or limited by object count. double currDelta = ((OsuDifficultyHitObject)Previous[i - 1]).StrainTime; double prevDelta = ((OsuDifficultyHitObject)Previous[i]).StrainTime; From 8745fe9e34f38132487356b0ea4fff71dab3b167 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Fri, 20 Aug 2021 22:32:04 +0300 Subject: [PATCH 1399/2442] Change editor timestamp regex to not match non-editor ones --- osu.Game/Online/Chat/MessageFormatter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index ae9199c428..d72104816f 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -43,7 +43,7 @@ namespace osu.Game.Online.Chat RegexOptions.IgnoreCase); // 00:00:000 (1,2,3) - test - private static readonly Regex time_regex = new Regex(@"\d\d:\d\d:\d\d\d? [^-]*"); + private static readonly Regex time_regex = new Regex(@"\d\d:\d\d:\d\d\d? \((\d,?)+\)"); // #osu private static readonly Regex channel_regex = new Regex(@"(#[a-zA-Z]+[a-zA-Z0-9]+)"); From 058d2d2a49eaa4eb929b59c900f0b11b26f2d73a Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Fri, 20 Aug 2021 23:01:06 +0300 Subject: [PATCH 1400/2442] Use nekodex's regex from osu-web --- osu.Game/Online/Chat/MessageFormatter.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index d72104816f..0e4ea694aa 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -43,7 +43,8 @@ namespace osu.Game.Online.Chat RegexOptions.IgnoreCase); // 00:00:000 (1,2,3) - test - private static readonly Regex time_regex = new Regex(@"\d\d:\d\d:\d\d\d? \((\d,?)+\)"); + // regex from https://github.com/ppy/osu-web/blob/651a9bac2b60d031edd7e33b8073a469bf11edaa/resources/assets/coffee/_classes/beatmap-discussion-helper.coffee#L10 + private static readonly Regex time_regex = new Regex(@"\b(((\d{2,}):([0-5]\d)[:.](\d{3}))(\s\((?:\d+[,|])*\d+\))?)"); // #osu private static readonly Regex channel_regex = new Regex(@"(#[a-zA-Z]+[a-zA-Z0-9]+)"); From 20f193c1c2eb6f85ab522884946d97423dbf0f60 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 21 Aug 2021 04:15:54 +0300 Subject: [PATCH 1401/2442] Fix screen offsetting not handling scaled game instances By using `Content` instead, now the logic will get the X of the settings overlay at the `Content` space, which can be scaled in the `ScalingMode.Everything` mode. And in the case of `ScalingMode.ExcludeOverlays`, a subcontainer somewhere inside `Content` that's holding the screen stack would be scaled, but `Content` won't be affected which is what we want in that case. --- osu.Game/OsuGame.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 0db63df69b..b9a649fda2 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1019,9 +1019,9 @@ namespace osu.Game var horizontalOffset = 0f; if (Settings.IsLoaded && Settings.IsPresent) - horizontalOffset += ToLocalSpace(Settings.ScreenSpaceDrawQuad.TopRight).X * SIDE_OVERLAY_OFFSET_RATIO; + horizontalOffset += Content.ToLocalSpace(Settings.ScreenSpaceDrawQuad.TopRight).X * SIDE_OVERLAY_OFFSET_RATIO; if (Notifications.IsLoaded && Notifications.IsPresent) - horizontalOffset += (ToLocalSpace(Notifications.ScreenSpaceDrawQuad.TopLeft).X - DrawWidth) * SIDE_OVERLAY_OFFSET_RATIO; + horizontalOffset += (Content.ToLocalSpace(Notifications.ScreenSpaceDrawQuad.TopLeft).X - Content.DrawWidth) * SIDE_OVERLAY_OFFSET_RATIO; ScreenOffsetContainer.X = horizontalOffset; From 318f830cd22152eaf712cbe5f5abd7458f8c4463 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 21 Aug 2021 04:18:03 +0300 Subject: [PATCH 1402/2442] Expand test coverage for different scaling modes Intentionally not using `[Values]` as the scale modes can be applied to the running game instance directly, rather than recreating it all over again. The same could be said for the notification overlay but not sure, seems like something that should be considered at an `OsuGameTestScene` level instead (whether the same game instance can be reused for further testing). --- .../Visual/Menus/TestSceneSideOverlays.cs | 47 +++++++++++++++---- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs b/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs index 5230e026bc..e34ec6c46a 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs @@ -1,8 +1,12 @@ // 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.Linq; using NUnit.Framework; using osu.Framework.Testing; +using osu.Framework.Utils; +using osu.Game.Configuration; using osu.Game.Overlays; namespace osu.Game.Tests.Visual.Menus @@ -21,21 +25,48 @@ namespace osu.Game.Tests.Visual.Menus [Test] public void TestScreenOffsettingOnSettingsOverlay() { - AddStep("open settings", () => Game.Settings.Show()); - AddUntilStep("right screen offset applied", () => Game.ScreenOffsetContainer.X == SettingsPanel.WIDTH * TestOsuGame.SIDE_OVERLAY_OFFSET_RATIO); + foreach (var scalingMode in Enum.GetValues(typeof(ScalingMode)).Cast()) + { + AddStep($"set scaling mode to {scalingMode}", () => + { + Game.LocalConfig.SetValue(OsuSetting.Scaling, scalingMode); - AddStep("hide settings", () => Game.Settings.Hide()); - AddUntilStep("screen offset removed", () => Game.ScreenOffsetContainer.X == 0f); + if (scalingMode != ScalingMode.Off) + { + Game.LocalConfig.SetValue(OsuSetting.ScalingSizeX, 0.5f); + Game.LocalConfig.SetValue(OsuSetting.ScalingSizeY, 0.5f); + } + }); + + AddStep("open settings", () => Game.Settings.Show()); + AddUntilStep("right screen offset applied", () => Precision.AlmostEquals(Game.ScreenOffsetContainer.X, SettingsPanel.WIDTH * TestOsuGame.SIDE_OVERLAY_OFFSET_RATIO)); + + AddStep("hide settings", () => Game.Settings.Hide()); + AddUntilStep("screen offset removed", () => Game.ScreenOffsetContainer.X == 0f); + } } [Test] public void TestScreenOffsettingOnNotificationOverlay() { - AddStep("open notifications", () => Game.Notifications.Show()); - AddUntilStep("right screen offset applied", () => Game.ScreenOffsetContainer.X == -NotificationOverlay.WIDTH * TestOsuGame.SIDE_OVERLAY_OFFSET_RATIO); + foreach (var scalingMode in Enum.GetValues(typeof(ScalingMode)).Cast()) + { + if (scalingMode != ScalingMode.Off) + { + AddStep($"set scaling mode to {scalingMode}", () => + { + Game.LocalConfig.SetValue(OsuSetting.Scaling, scalingMode); + Game.LocalConfig.SetValue(OsuSetting.ScalingSizeX, 0.5f); + Game.LocalConfig.SetValue(OsuSetting.ScalingSizeY, 0.5f); + }); + } - AddStep("hide notifications", () => Game.Notifications.Hide()); - AddUntilStep("screen offset removed", () => Game.ScreenOffsetContainer.X == 0f); + AddStep("open notifications", () => Game.Notifications.Show()); + AddUntilStep("right screen offset applied", () => Precision.AlmostEquals(Game.ScreenOffsetContainer.X, -NotificationOverlay.WIDTH * TestOsuGame.SIDE_OVERLAY_OFFSET_RATIO)); + + AddStep("hide notifications", () => Game.Notifications.Hide()); + AddUntilStep("screen offset removed", () => Game.ScreenOffsetContainer.X == 0f); + } } } } From 36352d1de4338014c6a154ebef4157888bd9ba54 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 21 Aug 2021 14:34:35 +0900 Subject: [PATCH 1403/2442] Improve highlighted chat username shadow effect --- osu.Game/Overlays/Chat/ChatLine.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index 01cfe9a55b..6f4f568eb2 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -110,15 +110,15 @@ namespace osu.Game.Overlays.Chat EdgeEffect = new EdgeEffectParameters { Roundness = 1, - Offset = new Vector2(0, 3), - Radius = 3, + Radius = 1, Colour = Color4.Black.Opacity(0.3f), + Offset = new Vector2(0, 1), Type = EdgeEffectType.Shadow, }, Child = new Container { AutoSizeAxes = Axes.Both, - Y = 3, + Y = 0, Masking = true, CornerRadius = 4, Children = new Drawable[] From 15d443f6b7e8e00142ec5757a8973baaf887415a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 21 Aug 2021 14:44:54 +0900 Subject: [PATCH 1404/2442] Use the UI mouse cursor when hovering gameplay chat in an interactive state --- osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs index e2f135d436..9fe41842f3 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Input.Bindings; using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.Play; @@ -38,6 +39,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer Textbox.FocusLost = () => expandedFromTextboxFocus.Value = false; } + protected override bool OnHover(HoverEvent e) => true; // use UI mouse cursor. + protected override void LoadComplete() { base.LoadComplete(); From ae47c5cdb3396533d324a76bee5a917e98ec9099 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 21 Aug 2021 15:08:42 +0900 Subject: [PATCH 1405/2442] Fix bottom area of a settings section not being clickable --- osu.Game/Overlays/Settings/SettingsSection.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index 6f167bf059..2a6f3f5ed7 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -47,7 +47,6 @@ namespace osu.Game.Overlays.Settings protected SettingsSection() { - Margin = new MarginPadding { Top = margin }; AutoSizeAxes = Axes.Y; RelativeSizeAxes = Axes.X; @@ -80,7 +79,7 @@ namespace osu.Game.Overlays.Settings Padding = new MarginPadding { Top = margin + border_size, - Bottom = 10, + Bottom = margin + 10, }, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, From 1729d43cecba9ca9d33203e5f09713693fd4473b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 21 Aug 2021 15:18:03 +0300 Subject: [PATCH 1406/2442] Add explanatory comment --- osu.Game/OsuGame.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b9a649fda2..b827b687a5 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1018,6 +1018,8 @@ namespace osu.Game var horizontalOffset = 0f; + // Calculate the horizontal offset using Content, as it gets nested inside a ScalingMode.Everything container + // which should apply to overlays, but not get affected by modes like ScalingMode.ExcludeOverlays which shouldn't. if (Settings.IsLoaded && Settings.IsPresent) horizontalOffset += Content.ToLocalSpace(Settings.ScreenSpaceDrawQuad.TopRight).X * SIDE_OVERLAY_OFFSET_RATIO; if (Notifications.IsLoaded && Notifications.IsPresent) From 9a6ff2995103596aa944aed5b746805619f9385e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 21 Aug 2021 15:39:57 +0300 Subject: [PATCH 1407/2442] Reword comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/OsuGame.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b827b687a5..4d952c39c6 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1018,8 +1018,9 @@ namespace osu.Game var horizontalOffset = 0f; - // Calculate the horizontal offset using Content, as it gets nested inside a ScalingMode.Everything container - // which should apply to overlays, but not get affected by modes like ScalingMode.ExcludeOverlays which shouldn't. + // Content.ToLocalSpace() is used instead of this.ToLocalSpace() to correctly calculate the offset with scaling modes active. + // Content is a child of a scaling container with ScalingMode.Everything set, while the game itself is never scaled. + // this avoids a visible jump in the positioning of the screen offset container. if (Settings.IsLoaded && Settings.IsPresent) horizontalOffset += Content.ToLocalSpace(Settings.ScreenSpaceDrawQuad.TopRight).X * SIDE_OVERLAY_OFFSET_RATIO; if (Notifications.IsLoaded && Notifications.IsPresent) From 479401e533682635868cfcedbcb2c7b15179103f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 21 Aug 2021 15:11:08 +0200 Subject: [PATCH 1408/2442] Add option to set own computation function in test --- .../Beatmaps/TestSceneBeatmapDifficultyCache.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs b/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs index da457c9e8f..7bbe647302 100644 --- a/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs +++ b/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs @@ -58,6 +58,12 @@ namespace osu.Game.Tests.Beatmaps { OsuModDoubleTime dt = null; + AddStep("set computation function", () => difficultyCache.ComputeDifficulty = lookup => + { + var modRateAdjust = (ModRateAdjust)lookup.OrderedMods.SingleOrDefault(mod => mod is ModRateAdjust); + return new StarDifficulty(BASE_STARS + modRateAdjust?.SpeedChange.Value ?? 0, 0); + }); + AddStep("change selected mod to DT", () => SelectedMods.Value = new[] { dt = new OsuModDoubleTime { SpeedChange = { Value = 1.5 } } }); AddUntilStep($"star difficulty -> {BASE_STARS + 1.5}", () => starDifficultyBindable.Value?.Stars == BASE_STARS + 1.5); @@ -133,13 +139,11 @@ namespace osu.Game.Tests.Beatmaps private class TestBeatmapDifficultyCache : BeatmapDifficultyCache { + public Func ComputeDifficulty { get; set; } + protected override Task ComputeValueAsync(DifficultyCacheLookup lookup, CancellationToken token = default) { - var rateAdjust = lookup.OrderedMods.OfType().SingleOrDefault(); - if (rateAdjust != null) - return Task.FromResult(new StarDifficulty(BASE_STARS + rateAdjust.SpeedChange.Value, 0)); - - return Task.FromResult(new StarDifficulty(BASE_STARS, 0)); + return Task.FromResult(ComputeDifficulty?.Invoke(lookup) ?? new StarDifficulty(BASE_STARS, 0)); } } } From f642546d6a385be54181138ce868a3df80d28950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 21 Aug 2021 15:15:13 +0200 Subject: [PATCH 1409/2442] Add failing test case --- .../TestSceneBeatmapDifficultyCache.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs b/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs index 7bbe647302..2b4dc48fe0 100644 --- a/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs +++ b/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs @@ -74,6 +74,29 @@ namespace osu.Game.Tests.Beatmaps AddUntilStep($"star difficulty -> {BASE_STARS + 1.75}", () => starDifficultyBindable.Value?.Stars == BASE_STARS + 1.75); } + [Test] + public void TestStarDifficultyAdjustHashCodeConflict() + { + OsuModDifficultyAdjust difficultyAdjust = null; + + AddStep("set computation function", () => difficultyCache.ComputeDifficulty = lookup => + { + var modDifficultyAdjust = (ModDifficultyAdjust)lookup.OrderedMods.SingleOrDefault(mod => mod is ModDifficultyAdjust); + return new StarDifficulty(BASE_STARS * (modDifficultyAdjust?.OverallDifficulty.Value ?? 1), 0); + }); + + AddStep("change selected mod to DA", () => SelectedMods.Value = new[] { difficultyAdjust = new OsuModDifficultyAdjust() }); + AddUntilStep($"star difficulty -> {BASE_STARS}", () => starDifficultyBindable.Value?.Stars == BASE_STARS); + + AddStep("change DA difficulty to 0.5", () => difficultyAdjust.OverallDifficulty.Value = 0.5f); + AddUntilStep($"star difficulty -> {BASE_STARS * 0.5f}", () => starDifficultyBindable.Value?.Stars == BASE_STARS / 2); + + // hash code of 0 (the value) conflicts with the hash code of null (the initial/default value). + // it's important that the mod reference and its underlying bindable references stay the same to demonstrate this failure. + AddStep("change DA difficulty to 0", () => difficultyAdjust.OverallDifficulty.Value = 0); + AddUntilStep($"star difficulty -> 0", () => starDifficultyBindable.Value?.Stars == 0); + } + [Test] public void TestKeyEqualsWithDifferentModInstances() { From 995338029c78d000323dd6db66303042e8bdd153 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 21 Aug 2021 15:39:02 +0200 Subject: [PATCH 1410/2442] Fix difficulty cache lookups sharing underlying mod instances `DifficultyCacheLookup`s were storing raw `Mod` instances into their `OrderedMods` field. This could cause the cache lookups to wrongly succeed in cases of mods with settings. The particular case that triggered this fix was Difficulty Adjust. Because the difficulty cache is backed by a dictionary, there are two stages to the lookup; first `GetHashCode()` is used to find the appropriate hash bucket to look in, and then items from that hash bucket are compared against the key being searched for via the implementation of `Equals()`. As it turns out, the first hashing step ended up being the saving grace in most cases, as the hash computation included the values of the mod settings. But the Difficulty Adjust failure case was triggered by the quirk that `GetHashCode(0) == GetHashCode(null) == 0`. In such a case, the `Equals()` fallback was used. But as it turns out, because the `Mod` instance stored to lookups was not cloned and therefore potentially externally mutable, it could be polluted after being stored to the dictionary, and therefore breaking the equality check. Even though all of the setting values were compared, the hash bucket didn't match the actual contents of the lookup anymore (because they were mutated externally, e.g. by the user changing the mod setting values in the mod settings overlay). To resolve, clone out the mod structure before creating all difficulty lookups. --- osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index 5025e4ad4b..0aa6a6dd0b 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -310,7 +310,7 @@ namespace osu.Game.Beatmaps Beatmap = beatmap; // In the case that the user hasn't given us a ruleset, use the beatmap's default ruleset. Ruleset = ruleset ?? Beatmap.Ruleset; - OrderedMods = mods?.OrderBy(m => m.Acronym).ToArray() ?? Array.Empty(); + OrderedMods = mods?.OrderBy(m => m.Acronym).Select(mod => mod.DeepClone()).ToArray() ?? Array.Empty(); } public bool Equals(DifficultyCacheLookup other) From 43b3845970fefeb2998a3103b7caf565583feede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 21 Aug 2021 16:35:40 +0200 Subject: [PATCH 1411/2442] Remove redundant string interpolation --- osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs b/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs index 2b4dc48fe0..dcfeea5db8 100644 --- a/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs +++ b/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs @@ -94,7 +94,7 @@ namespace osu.Game.Tests.Beatmaps // hash code of 0 (the value) conflicts with the hash code of null (the initial/default value). // it's important that the mod reference and its underlying bindable references stay the same to demonstrate this failure. AddStep("change DA difficulty to 0", () => difficultyAdjust.OverallDifficulty.Value = 0); - AddUntilStep($"star difficulty -> 0", () => starDifficultyBindable.Value?.Stars == 0); + AddUntilStep("star difficulty -> 0", () => starDifficultyBindable.Value?.Stars == 0); } [Test] From 812c85f3defa233be7e038e07930e5e22551d142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 21 Aug 2021 16:43:38 +0200 Subject: [PATCH 1412/2442] Clean up code style issues --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 3 +-- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 80465efe6f..3ed8012a83 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -197,8 +197,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty pm * Math.Pow(229.5 - 11 * Attributes.OverallDifficulty, 2.0); double accuracyValue = 2.83 * Math.Pow(1.52163, (79.5 - 2 * Math.Sqrt(variance)) / 6.0) - * Math.Pow(Math.Log(1.0 + (Math.E - 1.0) * (Math.Min(amountHitObjectsWithAccuracy, 1600) / 1000.0)), 0.5); - + * Math.Pow(Math.Log(1.0 + (Math.E - 1.0) * (Math.Min(amountHitObjectsWithAccuracy, 1600) / 1000.0)), 0.5); if (mods.Any(m => m is OsuModHidden)) accuracyValue *= 1.08; diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index e350a113f7..7e3d069abf 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -86,7 +86,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { islandSize++; // island is still progressing, count size. } - else { if (islandSize > 6) @@ -152,7 +151,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills angleBonus = 1.25; else if (angle < angle_bonus_begin) angleBonus = 1 + Math.Pow(Math.Sin(1.5 * (angle_bonus_begin - angle)), 2) / 4; - } return (angleBonus * speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / osuCurrObj.StrainTime; From aedfdce87296abec5f9f572408de333bf0a9e8ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 21 Aug 2021 16:45:41 +0200 Subject: [PATCH 1413/2442] Update values in calculator test --- .../OsuDifficultyCalculatorTest.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index 8d8387378e..265a7ccd21 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -15,13 +15,13 @@ namespace osu.Game.Rulesets.Osu.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; - [TestCase(6.7568168283591499d, "diffcalc-test")] - [TestCase(1.0348244046058293d, "zero-length-sliders")] + [TestCase(6.6915334809485199d, "diffcalc-test")] + [TestCase(1.0366129190339499d, "zero-length-sliders")] public void Test(double expected, string name) => base.Test(expected, name); - [TestCase(8.4783236764532557d, "diffcalc-test")] - [TestCase(1.2708532136987165d, "zero-length-sliders")] + [TestCase(8.3966904501824473d, "diffcalc-test")] + [TestCase(1.2732783892964523d, "zero-length-sliders")] public void TestClockRateAdjusted(double expected, string name) => Test(expected, name, new OsuModDoubleTime()); From 270f497af83d472ead4266137a42fbb16af72f20 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Sat, 21 Aug 2021 17:23:17 +0000 Subject: [PATCH 1414/2442] penalized 1/1->1/2->1/4 rhythm transitions --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index e350a113f7..58bc5680e2 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private const double pi_over_4 = Math.PI / 4; private const double pi_over_2 = Math.PI / 2; - private const double rhythm_multiplier = 2.0; + private const double rhythm_multiplier = 2.5; private const int history_time_max = 3000; // 3 seconds of calculatingRhythmBonus max. private double skillMultiplier => 1375; @@ -58,12 +58,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills return 0; int previousIslandSize = -1; + int prevPreviousIslandSize = -1; double rhythmComplexitySum = 0; int islandSize = 0; bool firstDeltaSwitch = false; - for (int i = Previous.Count - 1; i > 0; i--) + for (int i = Previous.Count - 2; i > 0; i--) { double currHistoricalDecay = Math.Max(0, (history_time_max - (current.StartTime - Previous[i - 1].StartTime))) / history_time_max; // scales note 0 to 1 from history to now @@ -73,6 +74,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double currDelta = ((OsuDifficultyHitObject)Previous[i - 1]).StrainTime; double prevDelta = ((OsuDifficultyHitObject)Previous[i]).StrainTime; + double prevPrevDelta = ((OsuDifficultyHitObject)Previous[i + 1]).StrainTime; double effectiveRatio = Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta); if (effectiveRatio > 0.5) @@ -98,12 +100,16 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills if (Previous[i].BaseObject is Slider) // bpm change was from a slider, this is easier typically than circle -> circle effectiveRatio *= 0.5; - if (previousIslandSize == islandSize) // repeated island size (ex: triplet -> triplet) + if (prevPreviousIslandSize % 2 == islandSize % 2) // repeated island size (ex: triplet -> triplet) effectiveRatio *= 0.25; + if (prevPrevDelta > prevDelta + 10 && prevDelta > currDelta + 10) // previous increase happened a note ago, 1/1->1/2-1/4, dont want to buff this. + effectiveRatio *= 0.125; + rhythmComplexitySum += effectiveRatio; previousIslandSize = islandSize; // log the last island size. + prevPreviousIslandSize = previousIslandSize; // yep if (prevDelta * 1.25 < currDelta) // we're slowing down, stop counting firstDeltaSwitch = false; // if we're speeding up, this stays true and we keep counting island size. From 0402f85eb017e869409fa10a292bcef581afc3ee Mon Sep 17 00:00:00 2001 From: Xexxar Date: Sat, 21 Aug 2021 17:29:17 +0000 Subject: [PATCH 1415/2442] left residual code from testing prevprevdeltas --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 059694ad17..21477a09da 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -58,7 +58,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills return 0; int previousIslandSize = -1; - int prevPreviousIslandSize = -1; double rhythmComplexitySum = 0; int islandSize = 0; @@ -99,7 +98,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills if (Previous[i].BaseObject is Slider) // bpm change was from a slider, this is easier typically than circle -> circle effectiveRatio *= 0.5; - if (prevPreviousIslandSize % 2 == islandSize % 2) // repeated island size (ex: triplet -> triplet) + if (previousIslandSize == islandSize) // repeated island size (ex: triplet -> triplet) effectiveRatio *= 0.25; if (prevPrevDelta > prevDelta + 10 && prevDelta > currDelta + 10) // previous increase happened a note ago, 1/1->1/2-1/4, dont want to buff this. @@ -108,7 +107,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills rhythmComplexitySum += effectiveRatio; previousIslandSize = islandSize; // log the last island size. - prevPreviousIslandSize = previousIslandSize; // yep if (prevDelta * 1.25 < currDelta) // we're slowing down, stop counting firstDeltaSwitch = false; // if we're speeding up, this stays true and we keep counting island size. From aaffc05b820b434ca74c9ffc8a2af81f8c8d5d0b Mon Sep 17 00:00:00 2001 From: Xexxar Date: Sat, 21 Aug 2021 17:56:45 +0000 Subject: [PATCH 1416/2442] removed accuracy penalty for misses --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 3ed8012a83..62f84b22ec 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -185,7 +185,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty double p100 = (2 * (double)countOk) / amountHitObjectsWithAccuracy; // this is multiplied by two to encourage better accuracy. (scales better) double p50 = (1 * (double)countMeh) / amountHitObjectsWithAccuracy; double pm = (1 * (double)countMiss) / amountHitObjectsWithAccuracy; - double p300 = 1.0 - pm - p100 - p50; + double p300 = Math.Max(0, 1.0 - pm - p100 - p50); double m300 = 79.5 - 6.0 * Attributes.OverallDifficulty; double m100 = 139.5 - 8.0 * Attributes.OverallDifficulty; @@ -193,8 +193,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty double variance = p300 * Math.Pow(m300 / 2.0, 2.0) + p100 * Math.Pow((m300 + m100) / 2.0, 2.0) + - p50 * Math.Pow((m100 + m50) / 2.0, 2.0) + - pm * Math.Pow(229.5 - 11 * Attributes.OverallDifficulty, 2.0); + p50 * Math.Pow((m100 + m50) / 2.0, 2.0); double accuracyValue = 2.83 * Math.Pow(1.52163, (79.5 - 2 * Math.Sqrt(variance)) / 6.0) * Math.Pow(Math.Log(1.0 + (Math.E - 1.0) * (Math.Min(amountHitObjectsWithAccuracy, 1600) / 1000.0)), 0.5); From 2877b438242e7d2a9789a00db0ae882825697076 Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Sun, 22 Aug 2021 09:54:07 +0800 Subject: [PATCH 1417/2442] split multiplayer and playlist activity --- .../Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs | 3 +++ osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs | 3 +++ osu.Game/Screens/Play/RoomSubmittingPlayer.cs | 3 --- osu.Game/Users/UserActivity.cs | 8 ++++++++ 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index ca1a3710ab..02c8e55caa 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -17,6 +17,7 @@ using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Ranking; +using osu.Game.Users; using osuTK; namespace osu.Game.Screens.OnlinePlay.Multiplayer @@ -28,6 +29,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer // Disallow fails in multiplayer for now. protected override bool CheckModsAllowFailure() => false; + protected override UserActivity InitialActivity => new UserActivity.InMultiplayerGame(Beatmap.Value.BeatmapInfo, Ruleset.Value); + [Resolved] private MultiplayerClient client { get; set; } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs index 567ea6b988..246bdbc204 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs @@ -13,6 +13,7 @@ using osu.Game.Rulesets; using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; +using osu.Game.Users; namespace osu.Game.Screens.OnlinePlay.Playlists { @@ -20,6 +21,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { public Action Exited; + protected override UserActivity InitialActivity => new UserActivity.InPlaylistGame(Beatmap.Value.BeatmapInfo, Ruleset.Value); + public PlaylistsPlayer(PlaylistItem playlistItem, PlayerConfiguration configuration = null) : base(playlistItem, configuration) { diff --git a/osu.Game/Screens/Play/RoomSubmittingPlayer.cs b/osu.Game/Screens/Play/RoomSubmittingPlayer.cs index f751fb747c..7ba12f5db6 100644 --- a/osu.Game/Screens/Play/RoomSubmittingPlayer.cs +++ b/osu.Game/Screens/Play/RoomSubmittingPlayer.cs @@ -6,7 +6,6 @@ using osu.Framework.Bindables; using osu.Game.Online.API; using osu.Game.Online.Rooms; using osu.Game.Scoring; -using osu.Game.Users; namespace osu.Game.Screens.Play { @@ -20,8 +19,6 @@ namespace osu.Game.Screens.Play protected readonly PlaylistItem PlaylistItem; - protected override UserActivity InitialActivity => new UserActivity.InMultiplayerGame(Beatmap.Value.BeatmapInfo, Ruleset.Value); - protected RoomSubmittingPlayer(PlaylistItem playlistItem, PlayerConfiguration configuration = null) : base(configuration) { diff --git a/osu.Game/Users/UserActivity.cs b/osu.Game/Users/UserActivity.cs index 88b91a3b42..75aa4866ff 100644 --- a/osu.Game/Users/UserActivity.cs +++ b/osu.Game/Users/UserActivity.cs @@ -50,6 +50,14 @@ namespace osu.Game.Users public override string Status => $@"{base.Status} with others"; } + public class InPlaylistGame : InGame + { + public InPlaylistGame(BeatmapInfo beatmap, RulesetInfo ruleset) + : base(beatmap, ruleset) + { + } + } + public class InSoloGame : InGame { public InSoloGame(BeatmapInfo info, RulesetInfo ruleset) From 3d402d9e788c1406eb997344887dc7ff4eb36da0 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sun, 22 Aug 2021 10:13:34 +0800 Subject: [PATCH 1418/2442] List incompatible mods in tooltip of mod button --- osu.Game/Overlays/Mods/ModButton.cs | 6 +- osu.Game/Overlays/Mods/ModButtonTooltip.cs | 145 +++++++++++++++++++++ 2 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Overlays/Mods/ModButtonTooltip.cs diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index 572ff0d1aa..88cc706766 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -23,7 +23,7 @@ namespace osu.Game.Overlays.Mods /// /// Represents a clickable button which can cycle through one of more mods. /// - public class ModButton : ModButtonEmpty, IHasTooltip + public class ModButton : ModButtonEmpty, IHasCustomTooltip { private ModIcon foregroundIcon; private ModIcon backgroundIcon; @@ -308,5 +308,9 @@ namespace osu.Game.Overlays.Mods Mod = mod; } + + public ITooltip GetCustomTooltip() => new ModButtonTooltip(); + + public object TooltipContent => SelectedMod ?? Mods[0]; } } diff --git a/osu.Game/Overlays/Mods/ModButtonTooltip.cs b/osu.Game/Overlays/Mods/ModButtonTooltip.cs new file mode 100644 index 0000000000..2a3160e779 --- /dev/null +++ b/osu.Game/Overlays/Mods/ModButtonTooltip.cs @@ -0,0 +1,145 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; +using osu.Game.Screens.Play.HUD; +using osuTK; + +namespace osu.Game.Overlays.Mods +{ + public class ModButtonTooltip : VisibilityContainer, ITooltip + { + private readonly OsuSpriteText descriptionText; + private readonly Box background; + private readonly OsuSpriteText incompatibleText; + + private readonly Bindable> incompatibleMods = new Bindable>(); + + [Resolved] + private Bindable ruleset { get; set; } + + public ModButtonTooltip() + { + AutoSizeAxes = Axes.Both; + Masking = true; + CornerRadius = 5; + + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Padding = new MarginPadding { Left = 10, Right = 10, Top = 5, Bottom = 5 }, + Children = new Drawable[] + { + descriptionText = new OsuSpriteText + { + Font = OsuFont.GetFont(weight: FontWeight.Regular), + Margin = new MarginPadding { Bottom = 5 } + }, + incompatibleText = new OsuSpriteText + { + Font = OsuFont.GetFont(weight: FontWeight.Regular), + Text = "Incompatible with:" + }, + new ModDisplay + { + Current = incompatibleMods, + ExpansionMode = ExpansionMode.AlwaysExpanded, + Scale = new Vector2(0.7f) + } + } + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + background.Colour = colours.Gray3; + descriptionText.Colour = colours.BlueLighter; + incompatibleText.Colour = colours.BlueLight; + } + + protected override void PopIn() => this.FadeIn(200, Easing.OutQuint); + protected override void PopOut() => this.FadeOut(200, Easing.OutQuint); + + private Drawable getModItem(Mod mod) + { + return new FillFlowContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new ModIcon(mod, false) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + Scale = new Vector2(0.4f) + }, + new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding { Left = 5 }, + Font = OsuFont.GetFont(weight: FontWeight.Regular), + Text = mod.Name, + }, + } + }; + } + + private string lastMod; + + public bool SetContent(object content) + { + if (!(content is Mod mod)) + return false; + + if (mod.Acronym == lastMod) return true; + + lastMod = mod.Acronym; + + descriptionText.Text = mod.Description; + + var incompatibleTypes = mod.IncompatibleMods; + + var allMods = ruleset.Value.CreateInstance().GetAllMods(); + + incompatibleMods.Value = allMods.Where(m => incompatibleTypes.Any(t => t.IsInstanceOfType(m))).ToList(); + + if (!incompatibleMods.Value.Any()) + { + incompatibleText.Text = "Compatible with all mods"; + return true; + } + + incompatibleText.Text = "Incompatible with:"; + + return true; + } + + public void Move(Vector2 pos) => Position = pos; + } +} From ef6faf04be40955911b288d2b9b57b8cf21c7a10 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sun, 22 Aug 2021 10:22:18 +0800 Subject: [PATCH 1419/2442] Use FirstOrDefault in TooltipContent --- osu.Game/Overlays/Mods/ModButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index 88cc706766..9ad867c58a 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -311,6 +311,6 @@ namespace osu.Game.Overlays.Mods public ITooltip GetCustomTooltip() => new ModButtonTooltip(); - public object TooltipContent => SelectedMod ?? Mods[0]; + public object TooltipContent => SelectedMod ?? Mods.FirstOrDefault(); } } From e213562b2a908b30a19b17e6182a08d9aa74c653 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sun, 22 Aug 2021 11:01:17 +0800 Subject: [PATCH 1420/2442] Add a red tint on mods incompatible with the current selection --- osu.Game/Overlays/Mods/ModButton.cs | 38 +++++++++++++++++++++- osu.Game/Overlays/Mods/ModButtonTooltip.cs | 2 +- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index 9ad867c58a..359d1a7981 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -11,12 +11,16 @@ using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; using System; +using System.Collections.Generic; using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics.Cursor; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; +using osu.Game.Utils; namespace osu.Game.Overlays.Mods { @@ -43,6 +47,9 @@ namespace osu.Game.Overlays.Mods // A selected index of -1 means not selected. private int selectedIndex = -1; + [Resolved] + private Bindable> globalSelectedMods { get; set; } + /// /// Change the selected mod index of this button. /// @@ -236,7 +243,28 @@ namespace osu.Game.Overlays.Mods backgroundIcon.Mod = foregroundIcon.Mod; foregroundIcon.Mod = mod; text.Text = mod.Name; - Colour = mod.HasImplementation ? Color4.White : Color4.Gray; + updateColour(mod); + } + + private Colour4 lightRed; + private Colour4 darkRed; + + private void updateColour(Mod mod) + { + var baseColour = mod.HasImplementation ? Color4.White : Color4.Gray; + + if (globalSelectedMods != null) + { + if (!globalSelectedMods.Value.Contains(mod)) + { + if (!ModUtils.CheckCompatibleSet(globalSelectedMods.Value.Concat(new[] { mod }))) + { + baseColour = mod.HasImplementation ? lightRed : darkRed; + } + } + } + + Colour = baseColour; } private void createIcons() @@ -309,6 +337,14 @@ namespace osu.Game.Overlays.Mods Mod = mod; } + [BackgroundDependencyLoader] + private void load(OsuColour colour) + { + lightRed = colour.RedLight; + darkRed = colour.RedDark; + globalSelectedMods.BindValueChanged(_ => updateColour(SelectedMod ?? Mods.FirstOrDefault()), true); + } + public ITooltip GetCustomTooltip() => new ModButtonTooltip(); public object TooltipContent => SelectedMod ?? Mods.FirstOrDefault(); diff --git a/osu.Game/Overlays/Mods/ModButtonTooltip.cs b/osu.Game/Overlays/Mods/ModButtonTooltip.cs index 2a3160e779..3383ad2d99 100644 --- a/osu.Game/Overlays/Mods/ModButtonTooltip.cs +++ b/osu.Game/Overlays/Mods/ModButtonTooltip.cs @@ -127,7 +127,7 @@ namespace osu.Game.Overlays.Mods var allMods = ruleset.Value.CreateInstance().GetAllMods(); - incompatibleMods.Value = allMods.Where(m => incompatibleTypes.Any(t => t.IsInstanceOfType(m))).ToList(); + incompatibleMods.Value = allMods.Where(m => m.GetType() != mod.GetType() && incompatibleTypes.Any(t => t.IsInstanceOfType(m))).ToList(); if (!incompatibleMods.Value.Any()) { From 0bbddd297c9bc0ea1201005d761cf7b3f78ead0b Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sun, 22 Aug 2021 11:05:53 +0800 Subject: [PATCH 1421/2442] Remove unused code --- osu.Game/Overlays/Mods/ModButtonTooltip.cs | 30 ---------------------- 1 file changed, 30 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModButtonTooltip.cs b/osu.Game/Overlays/Mods/ModButtonTooltip.cs index 3383ad2d99..1b889281f0 100644 --- a/osu.Game/Overlays/Mods/ModButtonTooltip.cs +++ b/osu.Game/Overlays/Mods/ModButtonTooltip.cs @@ -13,7 +13,6 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.UI; using osu.Game.Screens.Play.HUD; using osuTK; @@ -81,35 +80,6 @@ namespace osu.Game.Overlays.Mods protected override void PopIn() => this.FadeIn(200, Easing.OutQuint); protected override void PopOut() => this.FadeOut(200, Easing.OutQuint); - private Drawable getModItem(Mod mod) - { - return new FillFlowContainer - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Children = new Drawable[] - { - new ModIcon(mod, false) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - Scale = new Vector2(0.4f) - }, - new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Margin = new MarginPadding { Left = 5 }, - Font = OsuFont.GetFont(weight: FontWeight.Regular), - Text = mod.Name, - }, - } - }; - } - private string lastMod; public bool SetContent(object content) From 9cd0a182f6b0267521b78f94e5852f4bc567ce35 Mon Sep 17 00:00:00 2001 From: Michael Malloy Date: Sun, 22 Aug 2021 00:26:35 -0500 Subject: [PATCH 1422/2442] Add null check for Android ruleset loading --- osu.Game/Rulesets/RulesetStore.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 1f12f3dfeb..4da3fc2b4f 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -30,7 +30,13 @@ namespace osu.Game.Rulesets // On android in release configuration assemblies are loaded from the apk directly into memory. // We cannot read assemblies from cwd, so should check loaded assemblies instead. loadFromAppDomain(); - loadFromDisk(); + + // This null check prevents Android from attempting to load the rulesets from disk. + // RuntimeInfo.StartupDirectory returns null on Android, and returns a path on other platforms. + if (RuntimeInfo.StartupDirectory != null) + { + loadFromDisk(); + } // the event handler contains code for resolving dependency on the game assembly for rulesets located outside the base game directory. // It needs to be attached to the assembly lookup event before the actual call to loadUserRulesets() else rulesets located out of the base game directory will fail From 956112eb103da73a89cc2895693cb050eff512b1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 22 Aug 2021 12:40:41 +0300 Subject: [PATCH 1423/2442] Reword comment and remove brackets --- osu.Game/Rulesets/RulesetStore.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 4da3fc2b4f..a9e04a02b5 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -31,12 +31,11 @@ namespace osu.Game.Rulesets // We cannot read assemblies from cwd, so should check loaded assemblies instead. loadFromAppDomain(); - // This null check prevents Android from attempting to load the rulesets from disk. - // RuntimeInfo.StartupDirectory returns null on Android, and returns a path on other platforms. + // This null check prevents Android from attempting to load the rulesets from disk, + // as the underlying path "AppContext.BaseDirectory", despite being non-nullable, it returns null on android. + // See https://github.com/xamarin/xamarin-android/issues/3489. if (RuntimeInfo.StartupDirectory != null) - { loadFromDisk(); - } // the event handler contains code for resolving dependency on the game assembly for rulesets located outside the base game directory. // It needs to be attached to the assembly lookup event before the actual call to loadUserRulesets() else rulesets located out of the base game directory will fail From 81e3c9d40f7aa6bc8d439a4bf45f0c605c9a38e6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 22 Aug 2021 19:13:21 +0900 Subject: [PATCH 1424/2442] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 8fd761691c..cae8a946a3 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 0abd9adf77..bac99a098d 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index d03985a0c2..f3ecc90bea 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From d164529be8cb0ab5eda29a454e96abf2174ca934 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 22 Aug 2021 15:29:24 +0300 Subject: [PATCH 1425/2442] Fix ruleset selector not updating to the first ruleset item until after `LoadComplete()` This fixes the whole issue behind `Ruleset.Value` being null, by updating `Current` on BDL rather than waiting for the base logic which executes at `LoadComplete`. This seems like something that should happen at the base `TabControl` class itself, by switching `Current` right after the first added tab item, rather than doing it on `LoadComplete`, but I'm not sure about changing framework logic outright, so fixing this locally until it occurs on other places. --- osu.Game/Rulesets/RulesetSelector.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Rulesets/RulesetSelector.cs b/osu.Game/Rulesets/RulesetSelector.cs index 8e6ec556d2..e4d8f89a22 100644 --- a/osu.Game/Rulesets/RulesetSelector.cs +++ b/osu.Game/Rulesets/RulesetSelector.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using osu.Framework.Graphics.UserInterface; using osu.Framework.Allocation; @@ -18,6 +19,12 @@ namespace osu.Game.Rulesets { foreach (var r in Rulesets.AvailableRulesets) AddItem(r); + + // This is supposed to be an implicit process in the base class, but the problem is that it happens in LoadComplete. + // That can become an issue with overlays that require access to the initial ruleset value + // before the ruleset selectors reached a LoadComplete state. + // (e.g. displaying RankingsOverlay for the first time). + Current.Value = Items.First(); } } } From 45b8bd175c6ae9806f13250658383be901658f2e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 22 Aug 2021 15:29:47 +0300 Subject: [PATCH 1426/2442] Decouple rankings overlay's ruleset bindable from game-wide bindable --- osu.Game/Overlays/RankingsOverlay.cs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/RankingsOverlay.cs b/osu.Game/Overlays/RankingsOverlay.cs index a093969115..65c9c4d953 100644 --- a/osu.Game/Overlays/RankingsOverlay.cs +++ b/osu.Game/Overlays/RankingsOverlay.cs @@ -15,6 +15,7 @@ namespace osu.Game.Overlays { public class RankingsOverlay : TabbableOnlineOverlay { + protected Bindable Ruleset => Header.Ruleset; protected Bindable Country => Header.Country; private APIRequest lastRequest; @@ -22,9 +23,6 @@ namespace osu.Game.Overlays [Resolved] private IAPIProvider api { get; set; } - [Resolved] - private Bindable ruleset { get; set; } - public RankingsOverlay() : base(OverlayColourScheme.Green) { @@ -34,8 +32,6 @@ namespace osu.Game.Overlays { base.LoadComplete(); - Header.Ruleset.BindTo(ruleset); - Country.BindValueChanged(_ => { // if a country is requested, force performance scope. @@ -45,7 +41,7 @@ namespace osu.Game.Overlays Scheduler.AddOnce(triggerTabChanged); }); - ruleset.BindValueChanged(_ => + Ruleset.BindValueChanged(_ => { if (Header.Current.Value == RankingsScope.Spotlights) return; @@ -85,7 +81,7 @@ namespace osu.Game.Overlays { LoadDisplay(new SpotlightsLayout { - Ruleset = { BindTarget = ruleset } + Ruleset = { BindTarget = Ruleset } }); return; } @@ -110,13 +106,13 @@ namespace osu.Game.Overlays switch (Header.Current.Value) { case RankingsScope.Performance: - return new GetUserRankingsRequest(ruleset.Value, country: Country.Value?.FlagName); + return new GetUserRankingsRequest(Ruleset.Value, country: Country.Value?.FlagName); case RankingsScope.Country: - return new GetCountryRankingsRequest(ruleset.Value); + return new GetCountryRankingsRequest(Ruleset.Value); case RankingsScope.Score: - return new GetUserRankingsRequest(ruleset.Value, UserRankingsType.Score); + return new GetUserRankingsRequest(Ruleset.Value, UserRankingsType.Score); } return null; From 9816af688eb78a9181164761e170be53b9194586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 22 Aug 2021 14:26:53 +0200 Subject: [PATCH 1427/2442] Add basic design settings to setup screen --- osu.Game/Screens/Edit/Setup/DesignSection.cs | 58 ++++++++++++++++++++ osu.Game/Screens/Edit/Setup/SetupScreen.cs | 3 +- 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Screens/Edit/Setup/DesignSection.cs diff --git a/osu.Game/Screens/Edit/Setup/DesignSection.cs b/osu.Game/Screens/Edit/Setup/DesignSection.cs new file mode 100644 index 0000000000..380b29b04b --- /dev/null +++ b/osu.Game/Screens/Edit/Setup/DesignSection.cs @@ -0,0 +1,58 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Localisation; +using osu.Game.Graphics.UserInterfaceV2; + +namespace osu.Game.Screens.Edit.Setup +{ + internal class DesignSection : SetupSection + { + private LabelledSwitchButton widescreenSupport; + private LabelledSwitchButton epilepsyWarning; + private LabelledSwitchButton letterboxDuringBreaks; + + public override LocalisableString Title => "Design"; + + [BackgroundDependencyLoader] + private void load() + { + Children = new[] + { + widescreenSupport = new LabelledSwitchButton + { + Label = "Widescreen support", + Current = { Value = Beatmap.BeatmapInfo.WidescreenStoryboard } + }, + epilepsyWarning = new LabelledSwitchButton + { + Label = "Epilepsy warning", + Description = "Setting this is recommended if the storyboard contains scenes with rapidly flashing colours.", + Current = { Value = Beatmap.BeatmapInfo.EpilepsyWarning } + }, + letterboxDuringBreaks = new LabelledSwitchButton + { + Label = "Letterbox during breaks", + Current = { Value = Beatmap.BeatmapInfo.LetterboxInBreaks } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + widescreenSupport.Current.BindValueChanged(_ => updateBeatmap()); + epilepsyWarning.Current.BindValueChanged(_ => updateBeatmap()); + letterboxDuringBreaks.Current.BindValueChanged(_ => updateBeatmap()); + } + + private void updateBeatmap() + { + Beatmap.BeatmapInfo.WidescreenStoryboard = widescreenSupport.Current.Value; + Beatmap.BeatmapInfo.EpilepsyWarning = epilepsyWarning.Current.Value; + Beatmap.BeatmapInfo.LetterboxInBreaks = letterboxDuringBreaks.Current.Value; + } + } +} diff --git a/osu.Game/Screens/Edit/Setup/SetupScreen.cs b/osu.Game/Screens/Edit/Setup/SetupScreen.cs index 5bbec2574f..72bf3ad67e 100644 --- a/osu.Game/Screens/Edit/Setup/SetupScreen.cs +++ b/osu.Game/Screens/Edit/Setup/SetupScreen.cs @@ -34,7 +34,8 @@ namespace osu.Game.Screens.Edit.Setup new ResourcesSection(), new MetadataSection(), new DifficultySection(), - new ColoursSection() + new ColoursSection(), + new DesignSection(), } }, }); From d602dc9d908ddcfc9470b9bda7c6c7303416bdf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 22 Aug 2021 14:28:16 +0200 Subject: [PATCH 1428/2442] Enable epilepsy warning setting persistence in encoder --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index f14f6ec10c..524b556ddc 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -93,8 +93,8 @@ namespace osu.Game.Beatmaps.Formats // writer.WriteLine(@"OverlayPosition: " + b.OverlayPosition); // if (!string.IsNullOrEmpty(b.SkinPreference)) // writer.WriteLine(@"SkinPreference:" + b.SkinPreference); - // if (b.EpilepsyWarning) - // writer.WriteLine(@"EpilepsyWarning: 1"); + if (beatmap.BeatmapInfo.EpilepsyWarning) + writer.WriteLine(@"EpilepsyWarning: 1"); // if (b.CountdownOffset > 0) // writer.WriteLine(@"CountdownOffset: " + b.CountdownOffset.ToString()); if (beatmap.BeatmapInfo.RulesetID == 3) From e4a8f72167e2239a0254ded972dc2b0f27b7e000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 22 Aug 2021 19:01:26 +0200 Subject: [PATCH 1429/2442] Add failing test case --- .../SongSelect/TestSceneBeatmapInfoWedge.cs | 26 +++++++++++++++++++ osu.Game/Screens/Select/BeatmapInfoWedge.cs | 3 +++ 2 files changed, 29 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index 2b38c4f936..9fa0eab548 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Globalization; using System.Linq; using JetBrains.Annotations; using NUnit.Framework; @@ -11,6 +12,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets; using osu.Game.Rulesets.Catch; @@ -19,6 +21,7 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Taiko; using osu.Game.Screens.Select; using osuTK; @@ -141,6 +144,29 @@ namespace osu.Game.Tests.Visual.SongSelect selectBeatmap(createLongMetadata()); } + [Test] + public void TestBPMUpdates() + { + const float bpm = 120; + IBeatmap beatmap = createTestBeatmap(new OsuRuleset().RulesetInfo); + beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 60 * 1000 / bpm }); + + OsuModDoubleTime doubleTime = null; + + selectBeatmap(beatmap); + checkDisplayedBPM(bpm); + + AddStep("select DT", () => SelectedMods.Value = new[] { doubleTime = new OsuModDoubleTime() }); + checkDisplayedBPM(bpm * 1.5f); + + AddStep("change DT rate", () => doubleTime.SpeedChange.Value = 2); + checkDisplayedBPM(bpm * 2); + + void checkDisplayedBPM(float target) => + AddUntilStep($"displayed bpm is {target}", () => this.ChildrenOfType().Any( + label => label.Statistic.Name == "BPM" && label.Statistic.Content == target.ToString(CultureInfo.InvariantCulture))); + } + private void selectBeatmap([CanBeNull] IBeatmap b) { Container containerBefore = null; diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 297a16dd1d..1769381a58 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -449,8 +449,11 @@ namespace osu.Game.Screens.Select { public LocalisableString TooltipText { get; } + internal BeatmapStatistic Statistic { get; } + public InfoLabel(BeatmapStatistic statistic) { + Statistic = statistic; TooltipText = statistic.Name; AutoSizeAxes = Axes.Both; From 9538a32b5ec0fbe7ea7fdf0a2012743b84fe877a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 22 Aug 2021 19:05:44 +0200 Subject: [PATCH 1430/2442] Explicitly update beatmap info wedge on mod change This used to already be the case prior to b419ea7, but in a very roundabout way. Changes to the value of the star difficulty bindable - including indirect changes via the set of active mods changing - would trigger the wedge display to regenerate and load asynchronously. b419ea7 accidentally broke this by moving down the bindable retrieval to a lower level, at which point `WedgeInfoText` would only receive the set of mods selected at the time at which a given beatmap was selected, and not receive any further updates, breaking the BPM display updating in real time (as `WedgeInfoText` could not be aware that rate-changing mods were even in effect). To resolve, explicitly reload the wedge's contents on mod changes. --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 1769381a58..d40e21cd5e 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -71,6 +71,7 @@ namespace osu.Game.Screens.Select private void load() { ruleset.BindValueChanged(_ => updateDisplay()); + mods.BindValueChanged(_ => updateDisplay()); } private const double animation_duration = 800; From 2e80e2be5146f0f7d54c476d6c9530b2f7abf96d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 22 Aug 2021 21:47:37 +0200 Subject: [PATCH 1431/2442] Reword epilepsy warning description text --- osu.Game/Screens/Edit/Setup/DesignSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Setup/DesignSection.cs b/osu.Game/Screens/Edit/Setup/DesignSection.cs index 380b29b04b..ce6b4fa034 100644 --- a/osu.Game/Screens/Edit/Setup/DesignSection.cs +++ b/osu.Game/Screens/Edit/Setup/DesignSection.cs @@ -28,7 +28,7 @@ namespace osu.Game.Screens.Edit.Setup epilepsyWarning = new LabelledSwitchButton { Label = "Epilepsy warning", - Description = "Setting this is recommended if the storyboard contains scenes with rapidly flashing colours.", + Description = "Recommended if the storyboard contains scenes with rapidly flashing colours.", Current = { Value = Beatmap.BeatmapInfo.EpilepsyWarning } }, letterboxDuringBreaks = new LabelledSwitchButton From 4f3a5fbad5245ef37b73a6123b136f94eb61f128 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 23 Aug 2021 14:29:15 +0900 Subject: [PATCH 1432/2442] Fix test failure --- .../TestScenePlaylistsLoungeSubScreen.cs | 20 ++++++++++++------- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 15 +++++++------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs index a4bcb4baae..dafa8300f6 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs @@ -3,10 +3,11 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Bindables; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Graphics.Containers; -using osu.Game.Screens.OnlinePlay.Lounge; +using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Playlists; using osu.Game.Tests.Visual.OnlinePlay; @@ -18,13 +19,13 @@ namespace osu.Game.Tests.Visual.Playlists { protected new TestRequestHandlingRoomManager RoomManager => (TestRequestHandlingRoomManager)base.RoomManager; - private LoungeSubScreen loungeScreen; + private TestLoungeSubScreen loungeScreen; public override void SetUpSteps() { base.SetUpSteps(); - AddStep("push screen", () => LoadScreen(loungeScreen = new PlaylistsLoungeSubScreen())); + AddStep("push screen", () => LoadScreen(loungeScreen = new TestLoungeSubScreen())); AddUntilStep("wait for present", () => loungeScreen.IsCurrentScreen()); } @@ -69,21 +70,26 @@ namespace osu.Game.Tests.Visual.Playlists { AddStep("add rooms", () => RoomManager.AddRooms(1)); - AddAssert("selected room is not disabled", () => !OnlinePlayDependencies.SelectedRoom.Disabled); + AddAssert("selected room is not disabled", () => !loungeScreen.SelectedRoom.Disabled); AddStep("select room", () => roomsContainer.Rooms[0].TriggerClick()); - AddAssert("selected room is non-null", () => OnlinePlayDependencies.SelectedRoom.Value != null); + AddAssert("selected room is non-null", () => loungeScreen.SelectedRoom.Value != null); AddStep("enter room", () => roomsContainer.Rooms[0].TriggerClick()); AddUntilStep("wait for match load", () => Stack.CurrentScreen is PlaylistsRoomSubScreen); - AddAssert("selected room is non-null", () => OnlinePlayDependencies.SelectedRoom.Value != null); - AddAssert("selected room is disabled", () => OnlinePlayDependencies.SelectedRoom.Disabled); + AddAssert("selected room is non-null", () => loungeScreen.SelectedRoom.Value != null); + AddAssert("selected room is disabled", () => loungeScreen.SelectedRoom.Disabled); } private bool checkRoomVisible(DrawableRoom room) => loungeScreen.ChildrenOfType().First().ScreenSpaceDrawQuad .Contains(room.ScreenSpaceDrawQuad.Centre); + + private class TestLoungeSubScreen : PlaylistsLoungeSubScreen + { + public new Bindable SelectedRoom => base.SelectedRoom; + } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 7e898db584..cf3d76a3fb 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -38,7 +38,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge protected override BackgroundScreen CreateBackground() => new LoungeBackgroundScreen { - SelectedRoom = { BindTarget = selectedRoom } + SelectedRoom = { BindTarget = SelectedRoom } }; protected override UserActivity InitialActivity => new UserActivity.SearchingForLobby(); @@ -52,6 +52,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge protected ListingPollingComponent ListingPollingComponent { get; private set; } + protected readonly Bindable SelectedRoom = new Bindable(); + [Resolved] private MusicController music { get; set; } @@ -68,7 +70,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private LeasedBindable selectionLease; private readonly Bindable filter = new Bindable(new FilterCriteria()); - private readonly Bindable selectedRoom = new Bindable(); private readonly IBindable operationInProgress = new Bindable(); private readonly IBindable isIdle = new BindableBool(); private LoadingLayer loadingLayer; @@ -105,7 +106,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge Child = roomsContainer = new RoomsContainer { Filter = { BindTarget = filter }, - SelectedRoom = { BindTarget = selectedRoom } + SelectedRoom = { BindTarget = SelectedRoom } } }, }, @@ -164,7 +165,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge }; // scroll selected room into view on selection. - selectedRoom.BindValueChanged(val => + SelectedRoom.BindValueChanged(val => { var drawable = roomsContainer.Rooms.FirstOrDefault(r => r.Room == val.NewValue); if (drawable != null) @@ -247,8 +248,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge selectionLease.Return(); selectionLease = null; - if (selectedRoom.Value?.RoomID.Value == null) - selectedRoom.Value = new Room(); + if (SelectedRoom.Value?.RoomID.Value == null) + SelectedRoom.Value = new Room(); music?.EnsurePlayingSomething(); @@ -321,7 +322,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge protected virtual void OpenNewRoom(Room room) { - selectionLease = selectedRoom.BeginLease(false); + selectionLease = SelectedRoom.BeginLease(false); Debug.Assert(selectionLease != null); selectionLease.Value = room; From 58fb0c042bb060f7ba0769c22ccc75a2ad105aaf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 23 Aug 2021 14:30:34 +0900 Subject: [PATCH 1433/2442] Remove background scale altogether I'm not sure why this is required. Seems like something which was meant to exist to handle parallax, but that is already handled elsewhere now. --- osu.Game/Screens/BackgroundScreenStack.cs | 6 ------ .../OnlinePlay/Components/OnlinePlayBackgroundScreen.cs | 8 -------- 2 files changed, 14 deletions(-) diff --git a/osu.Game/Screens/BackgroundScreenStack.cs b/osu.Game/Screens/BackgroundScreenStack.cs index 17894d2474..294f23d2ac 100644 --- a/osu.Game/Screens/BackgroundScreenStack.cs +++ b/osu.Game/Screens/BackgroundScreenStack.cs @@ -4,25 +4,19 @@ using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Screens; -using osuTK; namespace osu.Game.Screens { public class BackgroundScreenStack : ScreenStack { - public const float BACKGROUND_SCALE = 1.06f; - public BackgroundScreenStack() : base(false) { - Scale = new Vector2(BACKGROUND_SCALE); RelativeSizeAxes = Axes.Both; Anchor = Anchor.Centre; Origin = Anchor.Centre; } - //public float ParallaxAmount { set => parallax.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT * value; } - public void Push(BackgroundScreen screen) { if (screen == null) diff --git a/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs index 5077979bf0..a282f1dacf 100644 --- a/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs @@ -77,14 +77,6 @@ namespace osu.Game.Screens.OnlinePlay.Components AddInternal(background = newBackground); } - protected override void Update() - { - base.Update(); - - // This is a static screen, so override the scale set in base.Update(), but also the scale set by the screen stack. - Scale = new Vector2(1f / BackgroundScreenStack.BACKGROUND_SCALE); - } - public override void OnSuspending(IScreen next) { base.OnSuspending(next); From 2ba88923b624d012c69c99d40b834beab47e407e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 23 Aug 2021 08:41:14 +0300 Subject: [PATCH 1434/2442] Select user preferred ruleset on overlay ruleset selectors initially --- osu.Game/Overlays/OverlayRulesetSelector.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/Overlays/OverlayRulesetSelector.cs b/osu.Game/Overlays/OverlayRulesetSelector.cs index 8c44157f78..74b8d0d3be 100644 --- a/osu.Game/Overlays/OverlayRulesetSelector.cs +++ b/osu.Game/Overlays/OverlayRulesetSelector.cs @@ -1,9 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; +using osu.Game.Online.API; using osu.Game.Rulesets; using osuTK; @@ -16,6 +18,15 @@ namespace osu.Game.Overlays AutoSizeAxes = Axes.Both; } + [BackgroundDependencyLoader] + private void load(RulesetStore store, IAPIProvider api) + { + var preferredRuleset = store.GetRuleset(api.LocalUser.Value.PlayMode); + + if (preferredRuleset != null) + Current.Value = preferredRuleset; + } + protected override TabItem CreateTabItem(RulesetInfo value) => new OverlayRulesetTabItem(value); protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer From 99bb3032a54c5a65d10bf2be009c75f3edeefc00 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 23 Aug 2021 15:26:39 +0900 Subject: [PATCH 1435/2442] Move gradient to be part of the background for now --- .../Components/OnlinePlayBackgroundScreen.cs | 10 ++++++++++ .../Screens/OnlinePlay/OnlinePlaySubScreenStack.cs | 14 -------------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs index a282f1dacf..6ce5f6a6db 100644 --- a/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs @@ -3,10 +3,14 @@ using System.Threading; using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Shapes; using osu.Framework.Screens; using osu.Game.Online.Rooms; using osuTK; +using osuTK.Graphics; #nullable enable @@ -20,6 +24,12 @@ namespace osu.Game.Screens.OnlinePlay.Components protected OnlinePlayBackgroundScreen() : base(false) { + AddInternal(new Box + { + RelativeSizeAxes = Axes.Both, + Depth = float.MinValue, + Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.9f), Color4.Black.Opacity(0.6f)) + }); } [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreenStack.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreenStack.cs index 69fa3b0916..7f2a0980c1 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreenStack.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreenStack.cs @@ -1,26 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Shapes; using osu.Framework.Screens; -using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay { public class OnlinePlaySubScreenStack : OsuScreenStack { - public OnlinePlaySubScreenStack() - { - AddInternal(new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.9f), Color4.Black.Opacity(0.6f)) - }); - } - protected override void ScreenChanged(IScreen prev, IScreen next) { base.ScreenChanged(prev, next); From 1d89d757af12ca677aac1892a02256d9618b13d1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 23 Aug 2021 09:56:00 +0300 Subject: [PATCH 1436/2442] Fix beatmap ruleset selector selecting initial ruleset --- .../BeatmapSet/BeatmapRulesetSelector.cs | 3 +++ osu.Game/Overlays/OverlayRulesetSelector.cs | 9 ++++++--- osu.Game/Rulesets/RulesetSelector.cs | 20 ++++++++++++++----- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs b/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs index 005d21726b..c46a3966ee 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs @@ -6,11 +6,14 @@ using osu.Framework.Graphics.UserInterface; using osu.Game.Beatmaps; using osu.Game.Rulesets; using System.Linq; +using osu.Framework.Allocation; namespace osu.Game.Overlays.BeatmapSet { public class BeatmapRulesetSelector : OverlayRulesetSelector { + protected override bool SelectInitialRuleset => false; + private readonly Bindable beatmapSet = new Bindable(); public BeatmapSetInfo BeatmapSet diff --git a/osu.Game/Overlays/OverlayRulesetSelector.cs b/osu.Game/Overlays/OverlayRulesetSelector.cs index 74b8d0d3be..60b16abd48 100644 --- a/osu.Game/Overlays/OverlayRulesetSelector.cs +++ b/osu.Game/Overlays/OverlayRulesetSelector.cs @@ -21,10 +21,13 @@ namespace osu.Game.Overlays [BackgroundDependencyLoader] private void load(RulesetStore store, IAPIProvider api) { - var preferredRuleset = store.GetRuleset(api.LocalUser.Value.PlayMode); + if (SelectInitialRuleset) + { + var preferredRuleset = store.GetRuleset(api.LocalUser.Value.PlayMode); - if (preferredRuleset != null) - Current.Value = preferredRuleset; + if (preferredRuleset != null) + Current.Value = preferredRuleset; + } } protected override TabItem CreateTabItem(RulesetInfo value) => new OverlayRulesetTabItem(value); diff --git a/osu.Game/Rulesets/RulesetSelector.cs b/osu.Game/Rulesets/RulesetSelector.cs index e4d8f89a22..e5d39fa144 100644 --- a/osu.Game/Rulesets/RulesetSelector.cs +++ b/osu.Game/Rulesets/RulesetSelector.cs @@ -14,17 +14,27 @@ namespace osu.Game.Rulesets protected override Dropdown CreateDropdown() => null; + protected virtual bool SelectInitialRuleset => true; + + protected RulesetSelector() + { + SelectFirstTabByDefault = false; + } + [BackgroundDependencyLoader] private void load() { foreach (var r in Rulesets.AvailableRulesets) AddItem(r); - // This is supposed to be an implicit process in the base class, but the problem is that it happens in LoadComplete. - // That can become an issue with overlays that require access to the initial ruleset value - // before the ruleset selectors reached a LoadComplete state. - // (e.g. displaying RankingsOverlay for the first time). - Current.Value = Items.First(); + if (SelectInitialRuleset) + { + // This is supposed to be an implicit process in the base class, but the problem is that it happens in LoadComplete. + // That can become an issue with overlays that require access to the initial ruleset value + // before the ruleset selectors reached a LoadComplete state. + // (e.g. displaying RankingsOverlay for the first time). + Current.Value = Items.First(); + } } } } From f2d51200a5b53a66251aca60fc1d98a749ba94c3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 23 Aug 2021 16:55:42 +0900 Subject: [PATCH 1437/2442] Update android mime types in line with new specifications --- osu.Android/OsuGameActivity.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index 063e02d349..0bcbfc4baf 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -20,8 +20,21 @@ namespace osu.Android [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false, LaunchMode = LaunchMode.SingleInstance, Exported = true)] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")] - [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-archive")] - [IntentFilter(new[] { Intent.ActionSend, Intent.ActionSendMultiple }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[] { "application/zip", "application/octet-stream", "application/download", "application/x-zip", "application/x-zip-compressed", "application/x-osu-archive" })] + [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-beatmap-archive")] + [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-skin-archive")] + [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-replay")] + [IntentFilter(new[] { Intent.ActionSend, Intent.ActionSendMultiple }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[] + { + "application/zip", + "application/octet-stream", + "application/download", + "application/x-zip", + "application/x-zip-compressed", + // newer official mime types (see https://osu.ppy.sh/wiki/en/osu%21_File_Formats). + "application/x-osu-beatmap-archive", + "application/x-osu-skin-archive", + "application/x-osu-replay", + })] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryBrowsable, Intent.CategoryDefault }, DataSchemes = new[] { "osu", "osump" })] public class OsuGameActivity : AndroidGameActivity { @@ -66,12 +79,14 @@ namespace osu.Android case Intent.ActionSendMultiple: { var uris = new List(); + for (int i = 0; i < intent.ClipData?.ItemCount; i++) { var content = intent.ClipData?.GetItemAt(i); if (content != null) uris.Add(content.Uri); } + handleImportFromUris(uris.ToArray()); break; } From cb7c2f713be7cfeb6e667df1404a843773ae83ca Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 23 Aug 2021 11:09:26 +0300 Subject: [PATCH 1438/2442] Store default ruleset value for ability to revert --- osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs | 1 - osu.Game/Overlays/OverlayRulesetSelector.cs | 2 +- osu.Game/Rulesets/RulesetSelector.cs | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs b/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs index c46a3966ee..3e707fc091 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs @@ -6,7 +6,6 @@ using osu.Framework.Graphics.UserInterface; using osu.Game.Beatmaps; using osu.Game.Rulesets; using System.Linq; -using osu.Framework.Allocation; namespace osu.Game.Overlays.BeatmapSet { diff --git a/osu.Game/Overlays/OverlayRulesetSelector.cs b/osu.Game/Overlays/OverlayRulesetSelector.cs index 60b16abd48..3f562e933d 100644 --- a/osu.Game/Overlays/OverlayRulesetSelector.cs +++ b/osu.Game/Overlays/OverlayRulesetSelector.cs @@ -26,7 +26,7 @@ namespace osu.Game.Overlays var preferredRuleset = store.GetRuleset(api.LocalUser.Value.PlayMode); if (preferredRuleset != null) - Current.Value = preferredRuleset; + Current.Value = Current.Default = preferredRuleset; } } diff --git a/osu.Game/Rulesets/RulesetSelector.cs b/osu.Game/Rulesets/RulesetSelector.cs index e5d39fa144..998948140e 100644 --- a/osu.Game/Rulesets/RulesetSelector.cs +++ b/osu.Game/Rulesets/RulesetSelector.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets // That can become an issue with overlays that require access to the initial ruleset value // before the ruleset selectors reached a LoadComplete state. // (e.g. displaying RankingsOverlay for the first time). - Current.Value = Items.First(); + Current.Value = Current.Default = Items.First(); } } } From 9fa39cd34e867be4d1258b52f427cfb5eb0cbb6b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 23 Aug 2021 11:11:06 +0300 Subject: [PATCH 1439/2442] Revert ruleset when not applied filters (includes scope change) --- osu.Game/Overlays/RankingsOverlay.cs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/osu.Game/Overlays/RankingsOverlay.cs b/osu.Game/Overlays/RankingsOverlay.cs index 65c9c4d953..9ddcaa589e 100644 --- a/osu.Game/Overlays/RankingsOverlay.cs +++ b/osu.Game/Overlays/RankingsOverlay.cs @@ -18,6 +18,8 @@ namespace osu.Game.Overlays protected Bindable Ruleset => Header.Ruleset; protected Bindable Country => Header.Country; + private bool requiresRulesetRevert; + private APIRequest lastRequest; [Resolved] @@ -50,6 +52,25 @@ namespace osu.Game.Overlays }); } + protected override void PopIn() + { + base.PopIn(); + + if (requiresRulesetRevert) + { + Ruleset.SetDefault(); + requiresRulesetRevert = false; + } + } + + protected override void PopOutComplete() + { + base.PopOutComplete(); + + if (Header.Current.Value == default && Country.Value == null) + requiresRulesetRevert = true; + } + protected override void OnTabChanged(RankingsScope tab) { // country filtering is only valid for performance scope. From 391c4e529c4fa847385c5052e6788b1968348ef6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 23 Aug 2021 11:11:15 +0300 Subject: [PATCH 1440/2442] Add test coverage for all added features --- .../Visual/Online/TestSceneRankingsOverlay.cs | 42 +++++++++++++++++++ .../TestSceneOverlayRulesetSelector.cs | 22 ++++++++++ 2 files changed, 64 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs index aff510dd95..74b8cbdfe5 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs @@ -1,12 +1,16 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using osu.Framework.Allocation; using osu.Game.Overlays; using NUnit.Framework; using osu.Game.Users; using osu.Framework.Bindables; +using osu.Framework.Testing; using osu.Game.Overlays.Rankings; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Taiko; namespace osu.Game.Tests.Visual.Online { @@ -35,6 +39,44 @@ namespace osu.Game.Tests.Visual.Online AddStep("Show", rankingsOverlay.Show); } + [Test] + public void TestRulesetResetsOnNoFilters() + { + AddStep("Set different ruleset", () => rankingsOverlay.ChildrenOfType().Single().SwitchTab(1)); + AddAssert("Ruleset not default", () => !rankingsOverlay.Header.Ruleset.IsDefault); + AddStep("Hide overlay", () => rankingsOverlay.Hide()); + AddUntilStep("Wait for hide", () => !rankingsOverlay.IsPresent); + + AddStep("Show overlay again", () => rankingsOverlay.Show()); + AddAssert("Ruleset reverted", () => rankingsOverlay.Header.Ruleset.IsDefault); + } + + [Test] + public void TestRulesetDoesntResetOnCountryFilter() + { + AddStep("Set different ruleset", () => rankingsOverlay.ChildrenOfType().Single().SwitchTab(1)); + AddAssert("Ruleset not default", () => !rankingsOverlay.Header.Ruleset.IsDefault); + AddStep("Set country filter", () => countryBindable.Value = us_country); + AddStep("Hide overlay", () => rankingsOverlay.Hide()); + AddUntilStep("Wait for hide", () => !rankingsOverlay.IsPresent); + + AddStep("Show overlay again", () => rankingsOverlay.Show()); + AddAssert("Ruleset not reverted", () => !rankingsOverlay.Header.Ruleset.IsDefault); + } + + [Test] + public void TestRulesetDoesntResetOnScopeChange() + { + AddStep("Set ruleset to osu!taiko", () => rankingsOverlay.Header.Ruleset.Value = new TaikoRuleset().RulesetInfo); + AddAssert("Ruleset not default", () => !rankingsOverlay.Header.Ruleset.IsDefault); + AddStep("Set different scope", () => scope.Value = RankingsScope.Score); + AddStep("Hide overlay", () => rankingsOverlay.Hide()); + AddUntilStep("Wait for hide", () => !rankingsOverlay.IsPresent); + + AddStep("Show overlay again", () => rankingsOverlay.Show()); + AddAssert("Ruleset not reverted", () => !rankingsOverlay.Header.Ruleset.IsDefault); + } + [Test] public void TestFlagScopeDependency() { diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayRulesetSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayRulesetSelector.cs index f4fa41a3b7..ceec8b2e58 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayRulesetSelector.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayRulesetSelector.cs @@ -70,5 +70,27 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("Select catch", () => ruleset.Value = new CatchRuleset().RulesetInfo); AddAssert("Check catch selected", () => selector.Current.Value.Equals(new CatchRuleset().RulesetInfo)); } + + [Test] + public void TestUserPreferredRuleset() + { + OverlayRulesetSelector localSelector = null; + + AddStep("Set osu! preferred ruleset", () => API.LocalUser.Value.PlayMode = OsuRuleset.SHORT_NAME); + AddStep("load overlay ruleset selector", () => Child = localSelector = new OverlayRulesetSelector()); + AddAssert("Check osu! selected", () => localSelector.Current.Value.Equals(new OsuRuleset().RulesetInfo)); + + AddStep("Set osu!taiko preferred ruleset", () => API.LocalUser.Value.PlayMode = TaikoRuleset.SHORT_NAME); + AddStep("load overlay ruleset selector", () => Child = localSelector = new OverlayRulesetSelector()); + AddAssert("Check osu!taiko selected", () => localSelector.Current.Value.Equals(new TaikoRuleset().RulesetInfo)); + + AddStep("Set osu!catch preferred ruleset", () => API.LocalUser.Value.PlayMode = CatchRuleset.SHORT_NAME); + AddStep("load overlay ruleset selector", () => Child = localSelector = new OverlayRulesetSelector()); + AddAssert("Check osu!catch selected", () => localSelector.Current.Value.Equals(new CatchRuleset().RulesetInfo)); + + AddStep("Set osu!mania preferred ruleset", () => API.LocalUser.Value.PlayMode = ManiaRuleset.SHORT_NAME); + AddStep("load overlay ruleset selector", () => Child = localSelector = new OverlayRulesetSelector()); + AddAssert("Check osu!mania selected", () => localSelector.Current.Value.Equals(new ManiaRuleset().RulesetInfo)); + } } } From 1de84e1c9806beb54eaae23b415cc2279c8a38be Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 23 Aug 2021 17:34:19 +0900 Subject: [PATCH 1441/2442] Change description to include mention of video Co-authored-by: Joseph Madamba --- osu.Game/Screens/Edit/Setup/DesignSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Setup/DesignSection.cs b/osu.Game/Screens/Edit/Setup/DesignSection.cs index ce6b4fa034..3f54ba9c9b 100644 --- a/osu.Game/Screens/Edit/Setup/DesignSection.cs +++ b/osu.Game/Screens/Edit/Setup/DesignSection.cs @@ -28,7 +28,7 @@ namespace osu.Game.Screens.Edit.Setup epilepsyWarning = new LabelledSwitchButton { Label = "Epilepsy warning", - Description = "Recommended if the storyboard contains scenes with rapidly flashing colours.", + Description = "Recommended if the storyboard or video contain scenes with rapidly flashing colours.", Current = { Value = Beatmap.BeatmapInfo.EpilepsyWarning } }, letterboxDuringBreaks = new LabelledSwitchButton From d17f7b5c8bab129ac3d1e219177b63e5dfe6b248 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 23 Aug 2021 17:40:41 +0900 Subject: [PATCH 1442/2442] Side `WidescreenStoryboard` to on by default for new beatmaps --- osu.Game/Beatmaps/BeatmapManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 241649062e..27aa874dc9 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -128,6 +128,7 @@ namespace osu.Game.Beatmaps BaseDifficulty = new BeatmapDifficulty(), Ruleset = ruleset, Metadata = metadata, + WidescreenStoryboard = true, } } }; From 2f6b95da39129c6bfcfa74b34a69ec9611bcf43c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 23 Aug 2021 17:41:03 +0900 Subject: [PATCH 1443/2442] Add descriptions for the remaining settings --- osu.Game/Screens/Edit/Setup/DesignSection.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Edit/Setup/DesignSection.cs b/osu.Game/Screens/Edit/Setup/DesignSection.cs index 3f54ba9c9b..68aaf3dd76 100644 --- a/osu.Game/Screens/Edit/Setup/DesignSection.cs +++ b/osu.Game/Screens/Edit/Setup/DesignSection.cs @@ -23,6 +23,7 @@ namespace osu.Game.Screens.Edit.Setup widescreenSupport = new LabelledSwitchButton { Label = "Widescreen support", + Description = "Allows storyboards to use the full screen space, rather than be confined to a 4:3 area.", Current = { Value = Beatmap.BeatmapInfo.WidescreenStoryboard } }, epilepsyWarning = new LabelledSwitchButton @@ -34,6 +35,7 @@ namespace osu.Game.Screens.Edit.Setup letterboxDuringBreaks = new LabelledSwitchButton { Label = "Letterbox during breaks", + Description = "Adds horizontal letterboxing to give a cinematic look during breaks.", Current = { Value = Beatmap.BeatmapInfo.LetterboxInBreaks } } }; From 79aea1fb7834a4513f7153bc8e6f9b8359bfe518 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 23 Aug 2021 11:44:50 +0300 Subject: [PATCH 1444/2442] Fix test overlay ruleset selectors not surrounded with overlay colour providers --- .../UserInterface/TestSceneOverlayRulesetSelector.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayRulesetSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayRulesetSelector.cs index ceec8b2e58..de55914fa8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayRulesetSelector.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayRulesetSelector.cs @@ -77,19 +77,19 @@ namespace osu.Game.Tests.Visual.UserInterface OverlayRulesetSelector localSelector = null; AddStep("Set osu! preferred ruleset", () => API.LocalUser.Value.PlayMode = OsuRuleset.SHORT_NAME); - AddStep("load overlay ruleset selector", () => Child = localSelector = new OverlayRulesetSelector()); + AddStep("load overlay ruleset selector", () => Child = new ColourProvidedContainer(OverlayColourScheme.Red, localSelector = new OverlayRulesetSelector())); AddAssert("Check osu! selected", () => localSelector.Current.Value.Equals(new OsuRuleset().RulesetInfo)); AddStep("Set osu!taiko preferred ruleset", () => API.LocalUser.Value.PlayMode = TaikoRuleset.SHORT_NAME); - AddStep("load overlay ruleset selector", () => Child = localSelector = new OverlayRulesetSelector()); + AddStep("load overlay ruleset selector", () => Child = new ColourProvidedContainer(OverlayColourScheme.Red, localSelector = new OverlayRulesetSelector())); AddAssert("Check osu!taiko selected", () => localSelector.Current.Value.Equals(new TaikoRuleset().RulesetInfo)); AddStep("Set osu!catch preferred ruleset", () => API.LocalUser.Value.PlayMode = CatchRuleset.SHORT_NAME); - AddStep("load overlay ruleset selector", () => Child = localSelector = new OverlayRulesetSelector()); + AddStep("load overlay ruleset selector", () => Child = new ColourProvidedContainer(OverlayColourScheme.Red, localSelector = new OverlayRulesetSelector())); AddAssert("Check osu!catch selected", () => localSelector.Current.Value.Equals(new CatchRuleset().RulesetInfo)); AddStep("Set osu!mania preferred ruleset", () => API.LocalUser.Value.PlayMode = ManiaRuleset.SHORT_NAME); - AddStep("load overlay ruleset selector", () => Child = localSelector = new OverlayRulesetSelector()); + AddStep("load overlay ruleset selector", () => Child = new ColourProvidedContainer(OverlayColourScheme.Red, localSelector = new OverlayRulesetSelector())); AddAssert("Check osu!mania selected", () => localSelector.Current.Value.Equals(new ManiaRuleset().RulesetInfo)); } } From c2c9a93e2c721b16947e5fa797e41a84a2584845 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 23 Aug 2021 18:12:35 +0900 Subject: [PATCH 1445/2442] Show editor file choosers in a popover rather than inline Supersedes and closes #14370. Closes #14367. --- .../Edit/Setup/FileChooserLabelledTextBox.cs | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs b/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs index 69c27702f8..e68e581942 100644 --- a/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs +++ b/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs @@ -8,22 +8,27 @@ using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Game.Database; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; +using osuTK; namespace osu.Game.Screens.Edit.Setup { /// /// A labelled textbox which reveals an inline file chooser when clicked. /// - internal class FileChooserLabelledTextBox : LabelledTextBox, ICanAcceptFiles + internal class FileChooserLabelledTextBox : LabelledTextBox, ICanAcceptFiles, IHasPopover { private readonly string[] handledExtensions; + public IEnumerable HandledExtensions => handledExtensions; /// @@ -51,23 +56,9 @@ namespace osu.Game.Screens.Edit.Setup Origin = Anchor.Centre, RelativeSizeAxes = Axes.X, CornerRadius = CORNER_RADIUS, - OnFocused = DisplayFileChooser + OnFocused = this.ShowPopover }; - public void DisplayFileChooser() - { - OsuFileSelector fileSelector; - - Target.Child = fileSelector = new OsuFileSelector(currentFile.Value?.DirectoryName, handledExtensions) - { - RelativeSizeAxes = Axes.X, - Height = 400, - CurrentFile = { BindTarget = currentFile } - }; - - sectionsContainer.ScrollTo(fileSelector); - } - protected override void LoadComplete() { base.LoadComplete(); @@ -111,5 +102,23 @@ namespace osu.Game.Screens.Edit.Setup GetContainingInputManager().TriggerFocusContention(this); } } + + public Popover GetPopover() => new FileChooserPopover(handledExtensions, currentFile); + + private class FileChooserPopover : OsuPopover + { + public FileChooserPopover(string[] handledExtensions, Bindable currentFile) + { + Child = new Container + { + Size = new Vector2(600, 400), + Child = new OsuFileSelector(currentFile.Value?.DirectoryName, handledExtensions) + { + RelativeSizeAxes = Axes.Both, + CurrentFile = { BindTarget = currentFile } + }, + }; + } + } } } From eeeaefbd7d0c87baeb65c0b08fa1c3364ea88c5c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 23 Aug 2021 12:38:24 +0300 Subject: [PATCH 1446/2442] Revert "Store default ruleset value for ability to revert" This reverts commit cb7c2f713be7cfeb6e667df1404a843773ae83ca. --- osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs | 1 + osu.Game/Overlays/OverlayRulesetSelector.cs | 2 +- osu.Game/Rulesets/RulesetSelector.cs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs b/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs index 3e707fc091..c46a3966ee 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics.UserInterface; using osu.Game.Beatmaps; using osu.Game.Rulesets; using System.Linq; +using osu.Framework.Allocation; namespace osu.Game.Overlays.BeatmapSet { diff --git a/osu.Game/Overlays/OverlayRulesetSelector.cs b/osu.Game/Overlays/OverlayRulesetSelector.cs index 3f562e933d..60b16abd48 100644 --- a/osu.Game/Overlays/OverlayRulesetSelector.cs +++ b/osu.Game/Overlays/OverlayRulesetSelector.cs @@ -26,7 +26,7 @@ namespace osu.Game.Overlays var preferredRuleset = store.GetRuleset(api.LocalUser.Value.PlayMode); if (preferredRuleset != null) - Current.Value = Current.Default = preferredRuleset; + Current.Value = preferredRuleset; } } diff --git a/osu.Game/Rulesets/RulesetSelector.cs b/osu.Game/Rulesets/RulesetSelector.cs index 998948140e..e5d39fa144 100644 --- a/osu.Game/Rulesets/RulesetSelector.cs +++ b/osu.Game/Rulesets/RulesetSelector.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets // That can become an issue with overlays that require access to the initial ruleset value // before the ruleset selectors reached a LoadComplete state. // (e.g. displaying RankingsOverlay for the first time). - Current.Value = Current.Default = Items.First(); + Current.Value = Items.First(); } } } From 257934a14415a014d8c5bfd1703064965f70886e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 23 Aug 2021 12:38:30 +0300 Subject: [PATCH 1447/2442] Revert "Revert ruleset when not applied filters (includes scope change)" This reverts commit 9fa39cd34e867be4d1258b52f427cfb5eb0cbb6b. --- osu.Game/Overlays/RankingsOverlay.cs | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/osu.Game/Overlays/RankingsOverlay.cs b/osu.Game/Overlays/RankingsOverlay.cs index 9ddcaa589e..65c9c4d953 100644 --- a/osu.Game/Overlays/RankingsOverlay.cs +++ b/osu.Game/Overlays/RankingsOverlay.cs @@ -18,8 +18,6 @@ namespace osu.Game.Overlays protected Bindable Ruleset => Header.Ruleset; protected Bindable Country => Header.Country; - private bool requiresRulesetRevert; - private APIRequest lastRequest; [Resolved] @@ -52,25 +50,6 @@ namespace osu.Game.Overlays }); } - protected override void PopIn() - { - base.PopIn(); - - if (requiresRulesetRevert) - { - Ruleset.SetDefault(); - requiresRulesetRevert = false; - } - } - - protected override void PopOutComplete() - { - base.PopOutComplete(); - - if (Header.Current.Value == default && Country.Value == null) - requiresRulesetRevert = true; - } - protected override void OnTabChanged(RankingsScope tab) { // country filtering is only valid for performance scope. From 6931721864bfba25469f2647caf5b1e1b8b6476b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 23 Aug 2021 12:39:27 +0300 Subject: [PATCH 1448/2442] Revert test coverage --- .../Visual/Online/TestSceneRankingsOverlay.cs | 42 ------------------- 1 file changed, 42 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs index 74b8cbdfe5..aff510dd95 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs @@ -1,16 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; using osu.Framework.Allocation; using osu.Game.Overlays; using NUnit.Framework; using osu.Game.Users; using osu.Framework.Bindables; -using osu.Framework.Testing; using osu.Game.Overlays.Rankings; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Taiko; namespace osu.Game.Tests.Visual.Online { @@ -39,44 +35,6 @@ namespace osu.Game.Tests.Visual.Online AddStep("Show", rankingsOverlay.Show); } - [Test] - public void TestRulesetResetsOnNoFilters() - { - AddStep("Set different ruleset", () => rankingsOverlay.ChildrenOfType().Single().SwitchTab(1)); - AddAssert("Ruleset not default", () => !rankingsOverlay.Header.Ruleset.IsDefault); - AddStep("Hide overlay", () => rankingsOverlay.Hide()); - AddUntilStep("Wait for hide", () => !rankingsOverlay.IsPresent); - - AddStep("Show overlay again", () => rankingsOverlay.Show()); - AddAssert("Ruleset reverted", () => rankingsOverlay.Header.Ruleset.IsDefault); - } - - [Test] - public void TestRulesetDoesntResetOnCountryFilter() - { - AddStep("Set different ruleset", () => rankingsOverlay.ChildrenOfType().Single().SwitchTab(1)); - AddAssert("Ruleset not default", () => !rankingsOverlay.Header.Ruleset.IsDefault); - AddStep("Set country filter", () => countryBindable.Value = us_country); - AddStep("Hide overlay", () => rankingsOverlay.Hide()); - AddUntilStep("Wait for hide", () => !rankingsOverlay.IsPresent); - - AddStep("Show overlay again", () => rankingsOverlay.Show()); - AddAssert("Ruleset not reverted", () => !rankingsOverlay.Header.Ruleset.IsDefault); - } - - [Test] - public void TestRulesetDoesntResetOnScopeChange() - { - AddStep("Set ruleset to osu!taiko", () => rankingsOverlay.Header.Ruleset.Value = new TaikoRuleset().RulesetInfo); - AddAssert("Ruleset not default", () => !rankingsOverlay.Header.Ruleset.IsDefault); - AddStep("Set different scope", () => scope.Value = RankingsScope.Score); - AddStep("Hide overlay", () => rankingsOverlay.Hide()); - AddUntilStep("Wait for hide", () => !rankingsOverlay.IsPresent); - - AddStep("Show overlay again", () => rankingsOverlay.Show()); - AddAssert("Ruleset not reverted", () => !rankingsOverlay.Header.Ruleset.IsDefault); - } - [Test] public void TestFlagScopeDependency() { From f4b69ceb8ae23ba438fd3230738b3b6e5469e0a1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 23 Aug 2021 12:40:20 +0300 Subject: [PATCH 1449/2442] Remove unused using embedded in reverted changes --- osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs b/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs index c46a3966ee..3e707fc091 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs @@ -6,7 +6,6 @@ using osu.Framework.Graphics.UserInterface; using osu.Game.Beatmaps; using osu.Game.Rulesets; using System.Linq; -using osu.Framework.Allocation; namespace osu.Game.Overlays.BeatmapSet { From d3958eb3fbc518f7fc2e273e19da90924b22e897 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 23 Aug 2021 13:23:57 +0300 Subject: [PATCH 1450/2442] Revert initial ruleset value logic --- .../BeatmapSet/BeatmapRulesetSelector.cs | 2 -- osu.Game/Overlays/OverlayRulesetSelector.cs | 14 -------------- osu.Game/Rulesets/RulesetSelector.cs | 17 ----------------- 3 files changed, 33 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs b/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs index 3e707fc091..005d21726b 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs @@ -11,8 +11,6 @@ namespace osu.Game.Overlays.BeatmapSet { public class BeatmapRulesetSelector : OverlayRulesetSelector { - protected override bool SelectInitialRuleset => false; - private readonly Bindable beatmapSet = new Bindable(); public BeatmapSetInfo BeatmapSet diff --git a/osu.Game/Overlays/OverlayRulesetSelector.cs b/osu.Game/Overlays/OverlayRulesetSelector.cs index 60b16abd48..8c44157f78 100644 --- a/osu.Game/Overlays/OverlayRulesetSelector.cs +++ b/osu.Game/Overlays/OverlayRulesetSelector.cs @@ -1,11 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; -using osu.Game.Online.API; using osu.Game.Rulesets; using osuTK; @@ -18,18 +16,6 @@ namespace osu.Game.Overlays AutoSizeAxes = Axes.Both; } - [BackgroundDependencyLoader] - private void load(RulesetStore store, IAPIProvider api) - { - if (SelectInitialRuleset) - { - var preferredRuleset = store.GetRuleset(api.LocalUser.Value.PlayMode); - - if (preferredRuleset != null) - Current.Value = preferredRuleset; - } - } - protected override TabItem CreateTabItem(RulesetInfo value) => new OverlayRulesetTabItem(value); protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer diff --git a/osu.Game/Rulesets/RulesetSelector.cs b/osu.Game/Rulesets/RulesetSelector.cs index e5d39fa144..8e6ec556d2 100644 --- a/osu.Game/Rulesets/RulesetSelector.cs +++ b/osu.Game/Rulesets/RulesetSelector.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.Linq; using osu.Framework.Graphics.UserInterface; using osu.Framework.Allocation; @@ -14,27 +13,11 @@ namespace osu.Game.Rulesets protected override Dropdown CreateDropdown() => null; - protected virtual bool SelectInitialRuleset => true; - - protected RulesetSelector() - { - SelectFirstTabByDefault = false; - } - [BackgroundDependencyLoader] private void load() { foreach (var r in Rulesets.AvailableRulesets) AddItem(r); - - if (SelectInitialRuleset) - { - // This is supposed to be an implicit process in the base class, but the problem is that it happens in LoadComplete. - // That can become an issue with overlays that require access to the initial ruleset value - // before the ruleset selectors reached a LoadComplete state. - // (e.g. displaying RankingsOverlay for the first time). - Current.Value = Items.First(); - } } } } From f8a7e0bdb6bd21af2e83c483e1bff07a33da2a27 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 23 Aug 2021 13:50:39 +0300 Subject: [PATCH 1451/2442] Update rankings overlay ruleset bindable with parent on initial display --- osu.Game/Overlays/RankingsOverlay.cs | 32 ++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/RankingsOverlay.cs b/osu.Game/Overlays/RankingsOverlay.cs index 65c9c4d953..b8bdef925e 100644 --- a/osu.Game/Overlays/RankingsOverlay.cs +++ b/osu.Game/Overlays/RankingsOverlay.cs @@ -15,7 +15,6 @@ namespace osu.Game.Overlays { public class RankingsOverlay : TabbableOnlineOverlay { - protected Bindable Ruleset => Header.Ruleset; protected Bindable Country => Header.Country; private APIRequest lastRequest; @@ -23,6 +22,12 @@ namespace osu.Game.Overlays [Resolved] private IAPIProvider api { get; set; } + [Resolved] + private IBindable parentRuleset { get; set; } + + [Cached] + private readonly Bindable ruleset = new Bindable(); + public RankingsOverlay() : base(OverlayColourScheme.Green) { @@ -32,6 +37,8 @@ namespace osu.Game.Overlays { base.LoadComplete(); + Header.Ruleset.BindTo(ruleset); + Country.BindValueChanged(_ => { // if a country is requested, force performance scope. @@ -41,7 +48,7 @@ namespace osu.Game.Overlays Scheduler.AddOnce(triggerTabChanged); }); - Ruleset.BindValueChanged(_ => + ruleset.BindValueChanged(_ => { if (Header.Current.Value == RankingsScope.Spotlights) return; @@ -50,6 +57,19 @@ namespace osu.Game.Overlays }); } + private bool requiresRulesetUpdate = true; + + protected override void PopIn() + { + if (requiresRulesetUpdate) + { + ruleset.Value = parentRuleset.Value; + requiresRulesetUpdate = false; + } + + base.PopIn(); + } + protected override void OnTabChanged(RankingsScope tab) { // country filtering is only valid for performance scope. @@ -81,7 +101,7 @@ namespace osu.Game.Overlays { LoadDisplay(new SpotlightsLayout { - Ruleset = { BindTarget = Ruleset } + Ruleset = { BindTarget = ruleset } }); return; } @@ -106,13 +126,13 @@ namespace osu.Game.Overlays switch (Header.Current.Value) { case RankingsScope.Performance: - return new GetUserRankingsRequest(Ruleset.Value, country: Country.Value?.FlagName); + return new GetUserRankingsRequest(ruleset.Value, country: Country.Value?.FlagName); case RankingsScope.Country: - return new GetCountryRankingsRequest(Ruleset.Value); + return new GetCountryRankingsRequest(ruleset.Value); case RankingsScope.Score: - return new GetUserRankingsRequest(Ruleset.Value, UserRankingsType.Score); + return new GetUserRankingsRequest(ruleset.Value, UserRankingsType.Score); } return null; From e33c3fcdbf9940e864ebdd53f70f812021b0e76f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 23 Aug 2021 13:53:03 +0300 Subject: [PATCH 1452/2442] Add test coverage for new changes --- .../Visual/Online/TestSceneRankingsOverlay.cs | 36 +++++++++---------- .../TestSceneOverlayRulesetSelector.cs | 22 ------------ 2 files changed, 18 insertions(+), 40 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs index aff510dd95..342ae76377 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs @@ -1,12 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; -using osu.Game.Overlays; using NUnit.Framework; -using osu.Game.Users; using osu.Framework.Bindables; +using osu.Framework.Graphics.Containers; +using osu.Game.Overlays; using osu.Game.Overlays.Rankings; +using osu.Game.Rulesets.Catch; +using osu.Game.Users; namespace osu.Game.Tests.Visual.Online { @@ -14,25 +15,20 @@ namespace osu.Game.Tests.Visual.Online { protected override bool UseOnlineAPI => true; - [Cached(typeof(RankingsOverlay))] - private readonly RankingsOverlay rankingsOverlay; + private TestRankingsOverlay rankingsOverlay; private readonly Bindable countryBindable = new Bindable(); private readonly Bindable scope = new Bindable(); - public TestSceneRankingsOverlay() - { - Add(rankingsOverlay = new TestRankingsOverlay - { - Country = { BindTarget = countryBindable }, - Header = { Current = { BindTarget = scope } }, - }); - } + [SetUp] + public void SetUp() => Schedule(loadRankingsOverlay); [Test] - public void TestShow() + public void TestParentRulesetCopiedOnInitialShow() { - AddStep("Show", rankingsOverlay.Show); + AddStep("set ruleset", () => Ruleset.Value = new CatchRuleset().RulesetInfo); + AddStep("reload rankings overlay", loadRankingsOverlay); + AddAssert("catch ruleset selected", () => Ruleset.Value.Equals(new CatchRuleset().RulesetInfo)); } [Test] @@ -50,10 +46,14 @@ namespace osu.Game.Tests.Visual.Online AddStep("Show US", () => rankingsOverlay.ShowCountry(us_country)); } - [Test] - public void TestHide() + private void loadRankingsOverlay() { - AddStep("Hide", rankingsOverlay.Hide); + Child = rankingsOverlay = new TestRankingsOverlay + { + Country = { BindTarget = countryBindable }, + Header = { Current = { BindTarget = scope } }, + State = { Value = Visibility.Visible }, + }; } private static readonly Country us_country = new Country diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayRulesetSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayRulesetSelector.cs index de55914fa8..f4fa41a3b7 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayRulesetSelector.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayRulesetSelector.cs @@ -70,27 +70,5 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("Select catch", () => ruleset.Value = new CatchRuleset().RulesetInfo); AddAssert("Check catch selected", () => selector.Current.Value.Equals(new CatchRuleset().RulesetInfo)); } - - [Test] - public void TestUserPreferredRuleset() - { - OverlayRulesetSelector localSelector = null; - - AddStep("Set osu! preferred ruleset", () => API.LocalUser.Value.PlayMode = OsuRuleset.SHORT_NAME); - AddStep("load overlay ruleset selector", () => Child = new ColourProvidedContainer(OverlayColourScheme.Red, localSelector = new OverlayRulesetSelector())); - AddAssert("Check osu! selected", () => localSelector.Current.Value.Equals(new OsuRuleset().RulesetInfo)); - - AddStep("Set osu!taiko preferred ruleset", () => API.LocalUser.Value.PlayMode = TaikoRuleset.SHORT_NAME); - AddStep("load overlay ruleset selector", () => Child = new ColourProvidedContainer(OverlayColourScheme.Red, localSelector = new OverlayRulesetSelector())); - AddAssert("Check osu!taiko selected", () => localSelector.Current.Value.Equals(new TaikoRuleset().RulesetInfo)); - - AddStep("Set osu!catch preferred ruleset", () => API.LocalUser.Value.PlayMode = CatchRuleset.SHORT_NAME); - AddStep("load overlay ruleset selector", () => Child = new ColourProvidedContainer(OverlayColourScheme.Red, localSelector = new OverlayRulesetSelector())); - AddAssert("Check osu!catch selected", () => localSelector.Current.Value.Equals(new CatchRuleset().RulesetInfo)); - - AddStep("Set osu!mania preferred ruleset", () => API.LocalUser.Value.PlayMode = ManiaRuleset.SHORT_NAME); - AddStep("load overlay ruleset selector", () => Child = new ColourProvidedContainer(OverlayColourScheme.Red, localSelector = new OverlayRulesetSelector())); - AddAssert("Check osu!mania selected", () => localSelector.Current.Value.Equals(new ManiaRuleset().RulesetInfo)); - } } } From fed0e15cea3f331722ece7b1a0f805eef16a5ad9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 23 Aug 2021 20:23:46 +0900 Subject: [PATCH 1453/2442] Fix typo in `ArchiveModelManager` --- osu.Game/Database/ArchiveModelManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 87bf54f981..ddd2bc5d1e 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -806,7 +806,7 @@ namespace osu.Game.Database protected TModel CheckForExisting(TModel model) => model.Hash == null ? null : ModelStore.ConsumableItems.FirstOrDefault(b => b.Hash == model.Hash); /// - /// Whether inport can be skipped after finding an existing import early in the process. + /// Whether import can be skipped after finding an existing import early in the process. /// Only valid when is not overridden. /// /// The existing model. From 7b3f7cc7c1e4534afdd96cf2c97499b418e4a1c4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 23 Aug 2021 20:24:00 +0900 Subject: [PATCH 1454/2442] Change skin import to also include directory names in the skin name where appropriate --- osu.Game/Skinning/SkinManager.cs | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 0f805990b9..0aa1c62d04 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -136,18 +136,19 @@ namespace osu.Game.Skinning protected override string ComputeHash(SkinInfo item, ArchiveReader reader = null) { - // we need to populate early to create a hash based off skin.ini contents - if (item.Name?.Contains(".osk", StringComparison.OrdinalIgnoreCase) == true) - populateMetadata(item, GetSkin(item)); + var instance = GetSkin(item); - if (item.Creator != null && item.Creator != unknown_creator_string) + // in the case the skin has a skin.ini file, we are going to create a hash based on that. + // we don't want to do this in the case we don't have a skin.ini, as it would match only on the filename portion, + // causing potentially unique skin imports to be considered as a duplicate. + if (!string.IsNullOrEmpty(instance.Configuration.SkinInfo.Name)) { - // this is the optimal way to hash legacy skins, but will need to be reconsidered when we move forward with skin implementation. - // likely, the skin should expose a real version (ie. the version of the skin, not the skin.ini version it's targeting). + // we need to populate early to create a hash based off skin.ini contents + populateMetadata(item, instance, reader?.Name); + return item.ToString().ComputeSHA2Hash(); } - // if there was no creator, the ToString above would give the filename, which alone isn't really enough to base any decisions on. return base.ComputeHash(item, reader); } @@ -157,13 +158,12 @@ namespace osu.Game.Skinning model.InstantiationInfo ??= instance.GetType().GetInvariantInstantiationInfo(); - if (model.Name?.Contains(".osk", StringComparison.OrdinalIgnoreCase) == true) - populateMetadata(model, instance); + populateMetadata(model, instance, archive?.Name); return Task.CompletedTask; } - private void populateMetadata(SkinInfo item, Skin instance) + private void populateMetadata(SkinInfo item, Skin instance, string archiveName) { if (!string.IsNullOrEmpty(instance.Configuration.SkinInfo.Name)) { @@ -175,6 +175,13 @@ namespace osu.Game.Skinning item.Name = item.Name.Replace(".osk", "", StringComparison.OrdinalIgnoreCase); item.Creator ??= unknown_creator_string; } + + // generally when importing from a folder, the ".osk" extension will not be present. + // if we ever need a more reliable method of determining this, the type of `ArchiveReader` can be checked. + bool isArchiveImport = archiveName?.Contains(".osk", StringComparison.OrdinalIgnoreCase) == true; + + if (archiveName != null && !isArchiveImport && archiveName != item.Name) + item.Name = $"{item.Name} ({archiveName})"; } /// From 0cbe95d6611c0a6921c6be892f9db02af3735bc4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 23 Aug 2021 20:25:46 +0900 Subject: [PATCH 1455/2442] Add test coverage of different folder names with same skin.ini --- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 36 +++++++++++++++++++---- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index bab8dfc983..0329ca11e2 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -138,16 +138,38 @@ namespace osu.Game.Tests.Skins.IO } } - private MemoryStream createOsk(string name, string author) + [Test] + public async Task TestSameMetadataNameDifferentFolderName() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportSkinTest))) + { + try + { + var osu = LoadOsuIntoHost(host); + + var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1", false), "my custom skin 1")); + + var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1", false), "my custom skin 2")); + + Assert.That(imported2.Hash, Is.Not.EqualTo(imported.Hash)); + } + finally + { + host.Exit(); + } + } + } + + private MemoryStream createOsk(string name, string author, bool makeUnique = true) { var zipStream = new MemoryStream(); using var zip = ZipArchive.Create(); - zip.AddEntry("skin.ini", generateSkinIni(name, author)); + zip.AddEntry("skin.ini", generateSkinIni(name, author, makeUnique)); zip.SaveTo(zipStream); return zipStream; } - private MemoryStream generateSkinIni(string name, string author) + private MemoryStream generateSkinIni(string name, string author, bool makeUnique = true) { var stream = new MemoryStream(); var writer = new StreamWriter(stream); @@ -155,8 +177,12 @@ namespace osu.Game.Tests.Skins.IO writer.WriteLine("[General]"); writer.WriteLine($"Name: {name}"); writer.WriteLine($"Author: {author}"); - writer.WriteLine(); - writer.WriteLine($"# unique {Guid.NewGuid()}"); + + if (makeUnique) + { + writer.WriteLine(); + writer.WriteLine($"# unique {Guid.NewGuid()}"); + } writer.Flush(); From a2484692b3a29cc71c383e3f8b43f250d43c6393 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 23 Aug 2021 20:37:19 +0900 Subject: [PATCH 1456/2442] Change brackets to square --- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 4 ++++ osu.Game/Skinning/SkinManager.cs | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index 0329ca11e2..7a9fc20426 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -148,8 +148,12 @@ namespace osu.Game.Tests.Skins.IO var osu = LoadOsuIntoHost(host); var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1", false), "my custom skin 1")); + Assert.That(imported.Name, Is.EqualTo("name 1 [my custom skin 1]")); + Assert.That(imported.Creator, Is.EqualTo("author 1")); var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1", false), "my custom skin 2")); + Assert.That(imported2.Name, Is.EqualTo("name 1 [my custom skin 2]")); + Assert.That(imported2.Creator, Is.EqualTo("author 1")); Assert.That(imported2.Hash, Is.Not.EqualTo(imported.Hash)); } diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 0aa1c62d04..edeb17cbad 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -181,7 +181,7 @@ namespace osu.Game.Skinning bool isArchiveImport = archiveName?.Contains(".osk", StringComparison.OrdinalIgnoreCase) == true; if (archiveName != null && !isArchiveImport && archiveName != item.Name) - item.Name = $"{item.Name} ({archiveName})"; + item.Name = $"{item.Name} [{archiveName}]"; } /// From bd42d7aada02c787cd3d32396decb36f836c2683 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 23 Aug 2021 18:01:01 +0300 Subject: [PATCH 1457/2442] Hide popover on file selection --- .../Edit/Setup/FileChooserLabelledTextBox.cs | 7 +-- .../Screens/Edit/Setup/ResourcesSection.cs | 59 ++++--------------- 2 files changed, 14 insertions(+), 52 deletions(-) diff --git a/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs b/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs index e68e581942..fd43349793 100644 --- a/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs +++ b/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs @@ -31,11 +31,6 @@ namespace osu.Game.Screens.Edit.Setup public IEnumerable HandledExtensions => handledExtensions; - /// - /// The target container to display the file chooser in. - /// - public Container Target; - private readonly Bindable currentFile = new Bindable(); [Resolved] @@ -72,7 +67,7 @@ namespace osu.Game.Screens.Edit.Setup if (file.NewValue == null) return; - Target.Clear(); + this.HidePopover(); Current.Value = file.NewValue.FullName; } diff --git a/osu.Game/Screens/Edit/Setup/ResourcesSection.cs b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs index ba22c82ecc..8e739a786f 100644 --- a/osu.Game/Screens/Edit/Setup/ResourcesSection.cs +++ b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs @@ -6,7 +6,6 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterfaceV2; @@ -39,62 +38,30 @@ namespace osu.Game.Screens.Edit.Setup [BackgroundDependencyLoader] private void load() { - Container audioTrackFileChooserContainer = createFileChooserContainer(); - Container backgroundFileChooserContainer = createFileChooserContainer(); - Children = new Drawable[] { - new FillFlowContainer + backgroundTextBox = new FileChooserLabelledTextBox(".jpg", ".jpeg", ".png") { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - backgroundTextBox = new FileChooserLabelledTextBox(".jpg", ".jpeg", ".png") - { - Label = "Background", - FixedLabelWidth = LABEL_WIDTH, - PlaceholderText = "Click to select a background image", - Current = { Value = working.Value.Metadata.BackgroundFile }, - Target = backgroundFileChooserContainer, - TabbableContentContainer = this - }, - backgroundFileChooserContainer, - } + Label = "Background", + FixedLabelWidth = LABEL_WIDTH, + PlaceholderText = "Click to select a background image", + Current = { Value = working.Value.Metadata.BackgroundFile }, + TabbableContentContainer = this }, - new FillFlowContainer + audioTrackTextBox = new FileChooserLabelledTextBox(".mp3", ".ogg") { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - audioTrackTextBox = new FileChooserLabelledTextBox(".mp3", ".ogg") - { - Label = "Audio Track", - FixedLabelWidth = LABEL_WIDTH, - PlaceholderText = "Click to select a track", - Current = { Value = working.Value.Metadata.AudioFile }, - Target = audioTrackFileChooserContainer, - TabbableContentContainer = this - }, - audioTrackFileChooserContainer, - } - } + Label = "Audio Track", + FixedLabelWidth = LABEL_WIDTH, + PlaceholderText = "Click to select a track", + Current = { Value = working.Value.Metadata.AudioFile }, + TabbableContentContainer = this + }, }; backgroundTextBox.Current.BindValueChanged(backgroundChanged); audioTrackTextBox.Current.BindValueChanged(audioTrackChanged); } - private static Container createFileChooserContainer() => - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - }; - public bool ChangeBackgroundImage(string path) { var info = new FileInfo(path); From 9a507ed2736c4c79d7382024e535bcec78cda5b5 Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Mon, 23 Aug 2021 16:27:29 +0100 Subject: [PATCH 1458/2442] update selected section on children count change --- osu.Game/Graphics/Containers/SectionsContainer.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 76492cab55..00c55bdd57 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -202,6 +202,8 @@ namespace osu.Game.Graphics.Containers }); } + private int lastKnownChildrenCount = 0; + protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); @@ -220,10 +222,12 @@ namespace osu.Game.Graphics.Containers } float currentScroll = scrollContainer.Current; + var presentChildren = Children.Where(c => c.IsPresent); - if (currentScroll != lastKnownScroll) + if (currentScroll != lastKnownScroll || presentChildren.Count() != lastKnownChildrenCount) { lastKnownScroll = currentScroll; + lastKnownChildrenCount = presentChildren.Count(); // reset last clicked section because user started scrolling themselves if (scrollContainer.UserScrolling) @@ -249,8 +253,6 @@ namespace osu.Game.Graphics.Containers float scrollCentre = fixedHeaderSize + scrollContainer.DisplayableContent * scroll_y_centre + selectionLenienceAboveSection; - var presentChildren = Children.Where(c => c.IsPresent); - if (lastClickedSection != null) SelectedSection.Value = lastClickedSection; else if (Precision.AlmostBigger(0, scrollContainer.Current)) From 20222f09c463a8a443b9def3c5b207f478b20d69 Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Mon, 23 Aug 2021 16:55:31 +0100 Subject: [PATCH 1459/2442] oops redundant default value --- osu.Game/Graphics/Containers/SectionsContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 00c55bdd57..cd6d0a9432 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -202,7 +202,7 @@ namespace osu.Game.Graphics.Containers }); } - private int lastKnownChildrenCount = 0; + private int lastKnownChildrenCount; protected override void UpdateAfterChildren() { From 6bea744e345696926b0e0e4159b3796fa1cf6dbc Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Mon, 23 Aug 2021 17:13:25 +0100 Subject: [PATCH 1460/2442] invalidate scroll position --- osu.Game/Graphics/Containers/SectionsContainer.cs | 8 +++----- osu.Game/Overlays/SettingsPanel.cs | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index cd6d0a9432..76492cab55 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -202,8 +202,6 @@ namespace osu.Game.Graphics.Containers }); } - private int lastKnownChildrenCount; - protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); @@ -222,12 +220,10 @@ namespace osu.Game.Graphics.Containers } float currentScroll = scrollContainer.Current; - var presentChildren = Children.Where(c => c.IsPresent); - if (currentScroll != lastKnownScroll || presentChildren.Count() != lastKnownChildrenCount) + if (currentScroll != lastKnownScroll) { lastKnownScroll = currentScroll; - lastKnownChildrenCount = presentChildren.Count(); // reset last clicked section because user started scrolling themselves if (scrollContainer.UserScrolling) @@ -253,6 +249,8 @@ namespace osu.Game.Graphics.Containers float scrollCentre = fixedHeaderSize + scrollContainer.DisplayableContent * scroll_y_centre + selectionLenienceAboveSection; + var presentChildren = Children.Where(c => c.IsPresent); + if (lastClickedSection != null) SelectedSection.Value = lastClickedSection; else if (Precision.AlmostBigger(0, scrollContainer.Current)) diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index e4e76592d8..5589786169 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -211,7 +211,7 @@ namespace osu.Game.Overlays loading.Hide(); - searchTextBox.Current.BindValueChanged(term => SectionsContainer.SearchContainer.SearchTerm = term.NewValue, true); + searchTextBox.Current.BindValueChanged(term => SectionsContainer.SearchTerm = term.NewValue, true); searchTextBox.TakeFocus(); loadSidebarButtons(); From 9d807656f26e6bc3882d38630b83269da670c1ff Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 23 Aug 2021 21:08:59 +0300 Subject: [PATCH 1461/2442] Add more complete test coverage of decoupling --- .../Visual/Online/TestSceneRankingsOverlay.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs index 342ae76377..027f17fff4 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs @@ -7,6 +7,8 @@ using osu.Framework.Graphics.Containers; using osu.Game.Overlays; using osu.Game.Overlays.Rankings; using osu.Game.Rulesets.Catch; +using osu.Game.Rulesets.Mania; +using osu.Game.Rulesets.Osu; using osu.Game.Users; namespace osu.Game.Tests.Visual.Online @@ -24,11 +26,20 @@ namespace osu.Game.Tests.Visual.Online public void SetUp() => Schedule(loadRankingsOverlay); [Test] - public void TestParentRulesetCopiedOnInitialShow() + public void TestParentRulesetDecoupledAfterInitialShow() { - AddStep("set ruleset", () => Ruleset.Value = new CatchRuleset().RulesetInfo); + AddStep("enable global ruleset", () => Ruleset.Disabled = false); + AddStep("set global ruleset to osu!catch", () => Ruleset.Value = new CatchRuleset().RulesetInfo); AddStep("reload rankings overlay", loadRankingsOverlay); - AddAssert("catch ruleset selected", () => Ruleset.Value.Equals(new CatchRuleset().RulesetInfo)); + AddAssert("rankings ruleset set to osu!catch", () => rankingsOverlay.Header.Ruleset.Value.ShortName == CatchRuleset.SHORT_NAME); + + AddStep("set global ruleset to osu!", () => Ruleset.Value = new OsuRuleset().RulesetInfo); + AddAssert("rankings ruleset still osu!catch", () => rankingsOverlay.Header.Ruleset.Value.ShortName == CatchRuleset.SHORT_NAME); + + AddStep("disable global ruleset", () => Ruleset.Disabled = true); + AddAssert("rankings ruleset still enabled", () => rankingsOverlay.Header.Ruleset.Disabled == false); + AddStep("set rankings ruleset to osu!mania", () => rankingsOverlay.Header.Ruleset.Value = new ManiaRuleset().RulesetInfo); + AddAssert("rankings ruleset set to osu!mania", () => rankingsOverlay.Header.Ruleset.Value.ShortName == ManiaRuleset.SHORT_NAME); } [Test] From 6e3d05c7ce5c42cd932ddab403c6f1654caffe2d Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Tue, 24 Aug 2021 09:42:53 +0800 Subject: [PATCH 1462/2442] Display an icon to signify incompatibility instead of a red tint --- osu.Game/Overlays/Mods/ModButton.cs | 43 +++++++++++++++++------------ 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index 359d1a7981..f4233c65cb 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -33,6 +33,7 @@ namespace osu.Game.Overlays.Mods private ModIcon backgroundIcon; private readonly SpriteText text; private readonly Container iconsContainer; + private readonly SpriteIcon incompatibleIcon; /// /// Fired when the selection changes. @@ -243,28 +244,25 @@ namespace osu.Game.Overlays.Mods backgroundIcon.Mod = foregroundIcon.Mod; foregroundIcon.Mod = mod; text.Text = mod.Name; - updateColour(mod); + Colour = mod.HasImplementation ? Color4.White : Color4.Gray; + updateCompatibility(mod); } - private Colour4 lightRed; - private Colour4 darkRed; - - private void updateColour(Mod mod) + private void updateCompatibility(Mod mod) { - var baseColour = mod.HasImplementation ? Color4.White : Color4.Gray; - if (globalSelectedMods != null) { if (!globalSelectedMods.Value.Contains(mod)) { if (!ModUtils.CheckCompatibleSet(globalSelectedMods.Value.Concat(new[] { mod }))) { - baseColour = mod.HasImplementation ? lightRed : darkRed; + incompatibleIcon.FadeIn(); + return; } } } - Colour = baseColour; + incompatibleIcon.FadeOut(); } private void createIcons() @@ -312,11 +310,24 @@ namespace osu.Game.Overlays.Mods { scaleContainer = new Container { - Child = iconsContainer = new Container + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, + iconsContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + }, + incompatibleIcon = new SpriteIcon + { + Origin = Anchor.BottomRight, + Anchor = Anchor.BottomRight, + Icon = FontAwesome.Solid.Ban, + Colour = Color4.Red, + Size = new Vector2(30), + Shadow = true, + Alpha = 0 + } }, RelativeSizeAxes = Axes.Both, Origin = Anchor.Centre, @@ -338,11 +349,9 @@ namespace osu.Game.Overlays.Mods } [BackgroundDependencyLoader] - private void load(OsuColour colour) + private void load() { - lightRed = colour.RedLight; - darkRed = colour.RedDark; - globalSelectedMods.BindValueChanged(_ => updateColour(SelectedMod ?? Mods.FirstOrDefault()), true); + globalSelectedMods.BindValueChanged(_ => updateCompatibility(SelectedMod ?? Mods.FirstOrDefault()), true); } public ITooltip GetCustomTooltip() => new ModButtonTooltip(); From b8fe03b77f8912bf1f6cf9b65548194a54e4ad0b Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Tue, 24 Aug 2021 09:50:09 +0800 Subject: [PATCH 1463/2442] Use `Mod.Equals` for comparison --- osu.Game/Overlays/Mods/ModButtonTooltip.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModButtonTooltip.cs b/osu.Game/Overlays/Mods/ModButtonTooltip.cs index 1b889281f0..666ed07e28 100644 --- a/osu.Game/Overlays/Mods/ModButtonTooltip.cs +++ b/osu.Game/Overlays/Mods/ModButtonTooltip.cs @@ -80,16 +80,16 @@ namespace osu.Game.Overlays.Mods protected override void PopIn() => this.FadeIn(200, Easing.OutQuint); protected override void PopOut() => this.FadeOut(200, Easing.OutQuint); - private string lastMod; + private Mod lastMod; public bool SetContent(object content) { if (!(content is Mod mod)) return false; - if (mod.Acronym == lastMod) return true; + if (mod.Equals(lastMod)) return true; - lastMod = mod.Acronym; + lastMod = mod; descriptionText.Text = mod.Description; From 7188a3268fbdfc24fb66b8aa6783ca151833c72d Mon Sep 17 00:00:00 2001 From: MBmasher Date: Tue, 24 Aug 2021 14:01:54 +1000 Subject: [PATCH 1464/2442] Apply a nerf to stacks for Flashlight skill --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index d225486cc8..f048142b56 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { } - protected override double SkillMultiplier => 0.13; + protected override double SkillMultiplier => 0.15; protected override double StrainDecayBase => 0.15; protected override double DecayWeight => 1.0; protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. @@ -53,10 +53,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills cumulativeStrainTime += osuPrevious.StrainTime; // We want to nerf objects that can be easily seen within the Flashlight circle radius. - if (i == 0 && jumpDistance < 50.0) - smallDistNerf = jumpDistance / 50.0; + if (i == 0) + smallDistNerf = Math.Min(1.0, jumpDistance / 50.0); - result += Math.Pow(0.8, i) * scalingFactor * jumpDistance / cumulativeStrainTime; + // We also want to nerf stacks so that only the first object of the stack is accounted for. + double stackNerf = Math.Min(1.0, osuPrevious.JumpDistance * scalingFactor / 50.0); + + result += Math.Pow(0.8, i) * stackNerf * scalingFactor * jumpDistance / cumulativeStrainTime; } } } From c2974cfc65b1b74697729776dd533a621d84f33b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 24 Aug 2021 13:20:01 +0900 Subject: [PATCH 1465/2442] Add full multiplayer gameplay flow test --- osu.Game.Tests/Resources/TestResources.cs | 2 ++ .../Multiplayer/TestSceneMultiplayer.cs | 36 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/osu.Game.Tests/Resources/TestResources.cs b/osu.Game.Tests/Resources/TestResources.cs index cef0532f9d..7170a76b8b 100644 --- a/osu.Game.Tests/Resources/TestResources.cs +++ b/osu.Game.Tests/Resources/TestResources.cs @@ -9,6 +9,8 @@ namespace osu.Game.Tests.Resources { public static class TestResources { + public const double QUICK_BEATMAP_LENGTH = 10000; + public static DllResourceStore GetStore() => new DllResourceStore(typeof(TestResources).Assembly); public static Stream OpenResource(string name) => GetStore().GetStream($"Resources/{name}"); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 6c1aed71e6..7a3507d944 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -28,6 +28,8 @@ using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; +using osu.Game.Screens.Play; +using osu.Game.Screens.Ranking; using osu.Game.Tests.Resources; using osu.Game.Users; using osuTK.Input; @@ -430,6 +432,40 @@ namespace osu.Game.Tests.Visual.Multiplayer } } + [Test] + public void TestGameplayFlow() + { + createRoom(() => new Room + { + Name = { Value = "Test Room" }, + Playlist = + { + new PlaylistItem + { + Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + } + } + }); + + AddRepeatStep("click spectate button", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().Single()); + InputManager.Click(MouseButton.Left); + }, 2); + + AddUntilStep("wait for player", () => Stack.CurrentScreen is Player); + + // Gameplay runs in real-time, so we need to incrementally check if gameplay has finished in order to not time out. + for (double i = 1000; i < TestResources.QUICK_BEATMAP_LENGTH; i += 1000) + { + var time = i; + AddUntilStep($"wait for time > {i}", () => this.ChildrenOfType().SingleOrDefault()?.GameplayClock.CurrentTime > time); + } + + AddUntilStep("wait for results", () => Stack.CurrentScreen is ResultsScreen); + } + private void createRoom(Func room) { AddUntilStep("wait for lounge", () => multiplayerScreen.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); From df170afbc433b3ea8faeb9afe2ec5ac35f16e34a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 24 Aug 2021 13:22:06 +0900 Subject: [PATCH 1466/2442] Fix multiplayer crashing when entering gameplay --- .../Multiplayer/TestSceneGameplayChatDisplay.cs | 2 +- .../Visual/Multiplayer/TestSceneMultiplayerPlayer.cs | 2 +- .../OnlinePlay/Match/Components/MatchChatDisplay.cs | 12 ++++++------ .../OnlinePlay/Multiplayer/GameplayChatDisplay.cs | 5 +++-- .../Multiplayer/MultiplayerMatchSubScreen.cs | 4 ++-- .../OnlinePlay/Multiplayer/MultiplayerPlayer.cs | 7 ++++--- .../Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs | 4 ++-- .../OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs | 4 ++-- osu.Game/Screens/Play/RoomSubmittingPlayer.cs | 10 ++++++---- 9 files changed, 27 insertions(+), 23 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs index eadfc9b279..a3a1cacb0d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs @@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { base.SetUpSteps(); - AddStep("load chat display", () => Child = chatDisplay = new GameplayChatDisplay + AddStep("load chat display", () => Child = chatDisplay = new GameplayChatDisplay(SelectedRoom.Value) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs index 80da7a7e5e..a1543f99e1 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("initialise gameplay", () => { - Stack.Push(player = new MultiplayerPlayer(Client.CurrentMatchPlayingItem.Value, Client.Room?.Users.ToArray())); + Stack.Push(player = new MultiplayerPlayer(Client.APIRoom, Client.CurrentMatchPlayingItem.Value, Client.Room?.Users.ToArray())); }); } diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs index 5f960c1b5c..05a3546cec 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs @@ -10,21 +10,21 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components { public class MatchChatDisplay : StandAloneChatDisplay { - [Resolved(typeof(Room), nameof(Room.RoomID))] - private Bindable roomId { get; set; } - - [Resolved(typeof(Room), nameof(Room.ChannelId))] - private Bindable channelId { get; set; } + private readonly IBindable roomId = new Bindable(); + private readonly IBindable channelId = new Bindable(); [Resolved(CanBeNull = true)] private ChannelManager channelManager { get; set; } private readonly bool leaveChannelOnDispose; - public MatchChatDisplay(bool leaveChannelOnDispose = true) + public MatchChatDisplay(Room room, bool leaveChannelOnDispose = true) : base(true) { this.leaveChannelOnDispose = leaveChannelOnDispose; + + roomId.BindTo(room.RoomID); + channelId.BindTo(room.ChannelId); } protected override void LoadComplete() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs index 9fe41842f3..3af72fa25a 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Input.Bindings; +using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.Play; @@ -29,8 +30,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public override bool PropagateNonPositionalInputSubTree => true; - public GameplayChatDisplay() - : base(leaveChannelOnDispose: false) + public GameplayChatDisplay(Room room) + : base(room, leaveChannelOnDispose: false) { RelativeSizeAxes = Axes.X; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 06e60903aa..fb18e7a31f 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -173,7 +173,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer }, }, new Drawable[] { new OverlinedHeader("Chat") { Margin = new MarginPadding { Vertical = 5 }, }, }, - new Drawable[] { new MatchChatDisplay { RelativeSizeAxes = Axes.Both } } + new Drawable[] { new MatchChatDisplay(Room) { RelativeSizeAxes = Axes.Both } } }, RowDimensions = new[] { @@ -395,7 +395,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer return new MultiSpectatorScreen(users.Take(PlayerGrid.MAX_PLAYERS).ToArray()); default: - return new PlayerLoader(() => new MultiplayerPlayer(SelectedItem.Value, users)); + return new PlayerLoader(() => new MultiplayerPlayer(Room, SelectedItem.Value, users)); } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index 24657943d7..4c26feb067 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -45,10 +45,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer /// /// Construct a multiplayer player. /// + /// The room. /// The playlist item to be played. /// The users which are participating in this game. - public MultiplayerPlayer(PlaylistItem playlistItem, MultiplayerRoomUser[] users) - : base(playlistItem, new PlayerConfiguration + public MultiplayerPlayer(Room room, PlaylistItem playlistItem, MultiplayerRoomUser[] users) + : base(room, playlistItem, new PlayerConfiguration { AllowPause = false, AllowRestart = false, @@ -92,7 +93,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } }); - LoadComponentAsync(new GameplayChatDisplay + LoadComponentAsync(new GameplayChatDisplay(Room) { Expanded = { BindTarget = HUDOverlay.ShowHud }, }, chat => leaderboardFlow.Insert(2, chat)); diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs index 567ea6b988..c441728bb6 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs @@ -20,8 +20,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { public Action Exited; - public PlaylistsPlayer(PlaylistItem playlistItem, PlayerConfiguration configuration = null) - : base(playlistItem, configuration) + public PlaylistsPlayer(Room room, PlaylistItem playlistItem, PlayerConfiguration configuration = null) + : base(room, playlistItem, configuration) { } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 98fed3b467..a9ce9c52c4 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -158,7 +158,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists }, new Drawable[] { leaderboard = new MatchLeaderboard { RelativeSizeAxes = Axes.Both }, }, new Drawable[] { new OverlinedHeader("Chat"), }, - new Drawable[] { new MatchChatDisplay { RelativeSizeAxes = Axes.Both } } + new Drawable[] { new MatchChatDisplay(Room) { RelativeSizeAxes = Axes.Both } } }, RowDimensions = new[] { @@ -199,7 +199,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists Logger.Log($"Polling adjusted (selection: {selectionPollingComponent.TimeBetweenPolls.Value})"); } - protected override Screen CreateGameplayScreen() => new PlayerLoader(() => new PlaylistsPlayer(SelectedItem.Value) + protected override Screen CreateGameplayScreen() => new PlayerLoader(() => new PlaylistsPlayer(Room, SelectedItem.Value) { Exited = () => leaderboard.RefreshScores() }); diff --git a/osu.Game/Screens/Play/RoomSubmittingPlayer.cs b/osu.Game/Screens/Play/RoomSubmittingPlayer.cs index 7ba12f5db6..593b67a7b0 100644 --- a/osu.Game/Screens/Play/RoomSubmittingPlayer.cs +++ b/osu.Game/Screens/Play/RoomSubmittingPlayer.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 osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Online.API; using osu.Game.Online.Rooms; @@ -14,15 +13,18 @@ namespace osu.Game.Screens.Play /// public abstract class RoomSubmittingPlayer : SubmittingPlayer { - [Resolved(typeof(Room), nameof(Room.RoomID))] - protected Bindable RoomId { get; private set; } + protected readonly IBindable RoomId = new Bindable(); protected readonly PlaylistItem PlaylistItem; + protected readonly Room Room; - protected RoomSubmittingPlayer(PlaylistItem playlistItem, PlayerConfiguration configuration = null) + protected RoomSubmittingPlayer(Room room, PlaylistItem playlistItem, PlayerConfiguration configuration = null) : base(configuration) { + Room = room; PlaylistItem = playlistItem; + + RoomId.BindTo(room.RoomID); } protected override APIRequest CreateTokenRequest() From de0de451fe4ce6de0a4ee407ee579297fc408586 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 24 Aug 2021 13:29:19 +0900 Subject: [PATCH 1467/2442] Refactor to remove resolved dependency --- .../Multiplayer/Match/BeatmapSelectionControl.cs | 2 +- .../OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs | 4 +++- .../OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 2 +- osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs | 8 ++++---- .../OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs | 2 +- .../Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs | 5 +++++ 6 files changed, 15 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs index 2e94e51385..6f1817a77c 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs @@ -52,7 +52,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match Action = () => { if (matchSubScreen.IsCurrentScreen()) - matchSubScreen.Push(new MultiplayerMatchSongSelect()); + matchSubScreen.Push(new MultiplayerMatchSongSelect(matchSubScreen.Room)); }, Alpha = 0 } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 3733b85a5e..ad4bb90551 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -24,9 +24,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer /// /// Construct a new instance of multiplayer song select. /// + /// The room. /// An optional initial beatmap selection to perform. /// An optional initial ruleset selection to perform. - public MultiplayerMatchSongSelect(WorkingBeatmap beatmap = null, RulesetInfo ruleset = null) + public MultiplayerMatchSongSelect(Room room, WorkingBeatmap beatmap = null, RulesetInfo ruleset = null) + : base(room) { if (beatmap != null || ruleset != null) { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 06e60903aa..c0c6f183fb 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -412,7 +412,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer return; } - this.Push(new MultiplayerMatchSongSelect(beatmap, ruleset)); + this.Push(new MultiplayerMatchSongSelect(Room, beatmap, ruleset)); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index de1b990573..1a063fd6c6 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -33,9 +33,6 @@ namespace osu.Game.Screens.OnlinePlay [Resolved(typeof(Room), nameof(Room.Playlist))] protected BindableList Playlist { get; private set; } - [Resolved] - private Room room { get; set; } - protected override UserActivity InitialActivity => new UserActivity.InLobby(room); protected readonly Bindable> FreeMods = new Bindable>(Array.Empty()); @@ -45,14 +42,17 @@ namespace osu.Game.Screens.OnlinePlay private IBindable selectedItem { get; set; } private readonly FreeModSelectOverlay freeModSelectOverlay; + private readonly Room room; private WorkingBeatmap initialBeatmap; private RulesetInfo initialRuleset; private IReadOnlyList initialMods; private bool itemSelected; - protected OnlinePlaySongSelect() + protected OnlinePlaySongSelect(Room room) { + this.room = room; + Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }; freeModSelectOverlay = new FreeModSelectOverlay diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 98fed3b467..63a46433aa 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -189,7 +189,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists EditPlaylist = () => { if (this.IsCurrentScreen()) - this.Push(new PlaylistsSongSelect()); + this.Push(new PlaylistsSongSelect(Room)); }, }; diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs index 076fa77336..03c95ec060 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs @@ -16,6 +16,11 @@ namespace osu.Game.Screens.OnlinePlay.Playlists [Resolved] private BeatmapManager beatmaps { get; set; } + public PlaylistsSongSelect(Room room) + : base(room) + { + } + protected override BeatmapDetailArea CreateBeatmapDetailArea() => new MatchBeatmapDetailArea { CreateNewItem = createNewItem From b719887810f40ad0013fb3ad1f58b115c2dc4581 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 24 Aug 2021 13:34:23 +0900 Subject: [PATCH 1468/2442] Fix test compile errors --- .../Multiplayer/TestSceneMultiplayerMatchSongSelect.cs | 8 +++++++- .../Visual/Multiplayer/TestScenePlaylistsSongSelect.cs | 7 ++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs index 8bcb9cebbc..0d6b428cce 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs @@ -15,6 +15,7 @@ using osu.Framework.Screens; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Beatmaps; +using osu.Game.Online.Rooms; using osu.Game.Overlays.Mods; using osu.Game.Rulesets; using osu.Game.Rulesets.Catch; @@ -89,7 +90,7 @@ namespace osu.Game.Tests.Visual.Multiplayer SelectedMods.SetDefault(); }); - AddStep("create song select", () => LoadScreen(songSelect = new TestMultiplayerMatchSongSelect())); + AddStep("create song select", () => LoadScreen(songSelect = new TestMultiplayerMatchSongSelect(SelectedRoom.Value))); AddUntilStep("wait for present", () => songSelect.IsCurrentScreen()); } @@ -168,6 +169,11 @@ namespace osu.Game.Tests.Visual.Multiplayer public new Bindable> FreeMods => base.FreeMods; public new BeatmapCarousel Carousel => base.Carousel; + + public TestMultiplayerMatchSongSelect(Room room, WorkingBeatmap beatmap = null, RulesetInfo ruleset = null) + : base(room, beatmap, ruleset) + { + } } } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs index e4bf9b36ed..ba30315663 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs @@ -93,7 +93,7 @@ namespace osu.Game.Tests.Visual.Multiplayer SelectedMods.Value = Array.Empty(); }); - AddStep("create song select", () => LoadScreen(songSelect = new TestPlaylistsSongSelect())); + AddStep("create song select", () => LoadScreen(songSelect = new TestPlaylistsSongSelect(SelectedRoom.Value))); AddUntilStep("wait for present", () => songSelect.IsCurrentScreen()); } @@ -183,6 +183,11 @@ namespace osu.Game.Tests.Visual.Multiplayer private class TestPlaylistsSongSelect : PlaylistsSongSelect { public new MatchBeatmapDetailArea BeatmapDetails => (MatchBeatmapDetailArea)base.BeatmapDetails; + + public TestPlaylistsSongSelect(Room room) + : base(room) + { + } } } } From bf0a1167ec990f64f3f98993f018d2eb758b8395 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 Aug 2021 13:35:39 +0900 Subject: [PATCH 1469/2442] Improve update flow and ensure selected mods is read from local context --- osu.Game/Overlays/Mods/ModButton.cs | 35 ++++++++++------------ osu.Game/Overlays/Mods/ModSelectOverlay.cs | 1 + 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index f4233c65cb..247c78152d 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -49,7 +49,7 @@ namespace osu.Game.Overlays.Mods private int selectedIndex = -1; [Resolved] - private Bindable> globalSelectedMods { get; set; } + private Bindable> selectedMods { get; set; } /// /// Change the selected mod index of this button. @@ -245,24 +245,20 @@ namespace osu.Game.Overlays.Mods foregroundIcon.Mod = mod; text.Text = mod.Name; Colour = mod.HasImplementation ? Color4.White : Color4.Gray; - updateCompatibility(mod); + + Scheduler.AddOnce(updateCompatibility); } - private void updateCompatibility(Mod mod) + private void updateCompatibility() { - if (globalSelectedMods != null) - { - if (!globalSelectedMods.Value.Contains(mod)) - { - if (!ModUtils.CheckCompatibleSet(globalSelectedMods.Value.Concat(new[] { mod }))) - { - incompatibleIcon.FadeIn(); - return; - } - } - } + var m = SelectedMod ?? Mods.First(); - incompatibleIcon.FadeOut(); + bool isIncompatible = false; + + if (selectedMods.Value.Count > 0 && !selectedMods.Value.Contains(m)) + isIncompatible = !ModUtils.CheckCompatibleSet(selectedMods.Value.Append(m)); + + incompatibleIcon.FadeTo(isIncompatible ? 1 : 0, 200, Easing.OutQuint); } private void createIcons() @@ -287,6 +283,7 @@ namespace osu.Game.Overlays.Mods }, }); } + else { iconsContainer.Add(foregroundIcon = new ModIcon(Mod, false) @@ -344,14 +341,14 @@ namespace osu.Game.Overlays.Mods }, new HoverSounds() }; - Mod = mod; } - [BackgroundDependencyLoader] - private void load() + protected override void LoadComplete() { - globalSelectedMods.BindValueChanged(_ => updateCompatibility(SelectedMod ?? Mods.FirstOrDefault()), true); + base.LoadComplete(); + + selectedMods.BindValueChanged(_ => Scheduler.AddOnce(updateCompatibility), true); } public ITooltip GetCustomTooltip() => new ModButtonTooltip(); diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 96eba7808f..fdef48d556 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -74,6 +74,7 @@ namespace osu.Game.Overlays.Mods protected readonly ModSettingsContainer ModSettingsContainer; + [Cached] public readonly Bindable> SelectedMods = new Bindable>(Array.Empty()); private Bindable>> availableMods; From 847726547ad9f203c665312ec9ca2d4d027a6be8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 24 Aug 2021 07:53:49 +0300 Subject: [PATCH 1470/2442] Move mod value change callback inside wedge info text component --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 26 +++++++++++---------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index d40e21cd5e..ac191a38f2 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -44,9 +44,6 @@ namespace osu.Game.Screens.Select [Resolved] private IBindable ruleset { get; set; } - [Resolved] - private IBindable> mods { get; set; } - protected Container DisplayedContent { get; private set; } protected WedgeInfoText Info { get; private set; } @@ -71,7 +68,6 @@ namespace osu.Game.Screens.Select private void load() { ruleset.BindValueChanged(_ => updateDisplay()); - mods.BindValueChanged(_ => updateDisplay()); } private const double animation_duration = 800; @@ -138,7 +134,7 @@ namespace osu.Game.Screens.Select Children = new Drawable[] { new BeatmapInfoWedgeBackground(beatmap), - Info = new WedgeInfoText(beatmap, ruleset.Value, mods.Value), + Info = new WedgeInfoText(beatmap, ruleset.Value), } }, loaded => { @@ -169,15 +165,16 @@ namespace osu.Game.Screens.Select private readonly WorkingBeatmap beatmap; private readonly RulesetInfo ruleset; - private readonly IReadOnlyList mods; + + [Resolved] + private IBindable> mods { get; set; } private ModSettingChangeTracker settingChangeTracker; - public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset, IReadOnlyList mods) + public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset) { this.beatmap = beatmap; ruleset = userRuleset ?? beatmap.BeatmapInfo.Ruleset; - this.mods = mods; } private CancellationTokenSource cancellationSource; @@ -363,10 +360,15 @@ namespace osu.Game.Screens.Select } }; - settingChangeTracker = new ModSettingChangeTracker(mods); - settingChangeTracker.SettingChanged += _ => refreshBPMLabel(); + mods.BindValueChanged(m => + { + settingChangeTracker?.Dispose(); - refreshBPMLabel(); + refreshBPMLabel(); + + settingChangeTracker = new ModSettingChangeTracker(m.NewValue); + settingChangeTracker.SettingChanged += _ => refreshBPMLabel(); + }, true); } private InfoLabel[] getRulesetInfoLabels() @@ -404,7 +406,7 @@ namespace osu.Game.Screens.Select // this doesn't consider mods which apply variable rates, yet. double rate = 1; - foreach (var mod in mods.OfType()) + foreach (var mod in mods.Value.OfType()) rate = mod.ApplyToRate(0, rate); double bpmMax = b.ControlPointInfo.BPMMaximum * rate; From afd01d22d6c29b9bbf1ba6bc0efe344269f1ad64 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 Aug 2021 13:58:31 +0900 Subject: [PATCH 1471/2442] Adjust visuals of incompatible icon and move to own class --- osu.Game/Overlays/Mods/IncompatibleIcon.cs | 64 ++++++++++++++++++++++ osu.Game/Overlays/Mods/ModButton.cs | 17 +++--- 2 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 osu.Game/Overlays/Mods/IncompatibleIcon.cs diff --git a/osu.Game/Overlays/Mods/IncompatibleIcon.cs b/osu.Game/Overlays/Mods/IncompatibleIcon.cs new file mode 100644 index 0000000000..df134fe4a4 --- /dev/null +++ b/osu.Game/Overlays/Mods/IncompatibleIcon.cs @@ -0,0 +1,64 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; +using osu.Game.Graphics; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Mods +{ + public class IncompatibleIcon : VisibilityContainer, IHasTooltip + { + private Circle circle; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Size = new Vector2(20); + + State.Value = Visibility.Hidden; + Alpha = 0; + + InternalChildren = new Drawable[] + { + circle = new Circle + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Gray4, + }, + new SpriteIcon + { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Size = new Vector2(0.6f), + Icon = FontAwesome.Solid.Slash, + Colour = Color4.White, + Shadow = true, + } + }; + } + + protected override void PopIn() + { + this.FadeIn(200, Easing.OutQuint); + circle.FlashColour(Color4.Red, 500, Easing.OutQuint); + this.ScaleTo(1.8f).Then().ScaleTo(1, 500, Easing.OutQuint); + } + + protected override void PopOut() + { + this.FadeOut(200, Easing.OutQuint); + this.ScaleTo(0.8f, 200, Easing.In); + } + + public LocalisableString TooltipText => "Incompatible with current selected mods"; + } +} diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index 247c78152d..76046f2467 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -33,7 +33,7 @@ namespace osu.Game.Overlays.Mods private ModIcon backgroundIcon; private readonly SpriteText text; private readonly Container iconsContainer; - private readonly SpriteIcon incompatibleIcon; + private readonly CompositeDrawable incompatibleIcon; /// /// Fired when the selection changes. @@ -258,7 +258,10 @@ namespace osu.Game.Overlays.Mods if (selectedMods.Value.Count > 0 && !selectedMods.Value.Contains(m)) isIncompatible = !ModUtils.CheckCompatibleSet(selectedMods.Value.Append(m)); - incompatibleIcon.FadeTo(isIncompatible ? 1 : 0, 200, Easing.OutQuint); + if (isIncompatible) + incompatibleIcon.Show(); + else + incompatibleIcon.Hide(); } private void createIcons() @@ -315,15 +318,11 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.Centre, Anchor = Anchor.Centre, }, - incompatibleIcon = new SpriteIcon + incompatibleIcon = new IncompatibleIcon { - Origin = Anchor.BottomRight, + Origin = Anchor.Centre, Anchor = Anchor.BottomRight, - Icon = FontAwesome.Solid.Ban, - Colour = Color4.Red, - Size = new Vector2(30), - Shadow = true, - Alpha = 0 + Position = new Vector2(-13), } }, RelativeSizeAxes = Axes.Both, From c3b7ce0b059bd90529bf00eb8eadd1db256d1c9c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 Aug 2021 14:02:50 +0900 Subject: [PATCH 1472/2442] Remove stray newline --- osu.Game/Overlays/Mods/ModButton.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index 76046f2467..4675eb6bc8 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -286,7 +286,6 @@ namespace osu.Game.Overlays.Mods }, }); } - else { iconsContainer.Add(foregroundIcon = new ModIcon(Mod, false) From 16ddbcd208db9f94765e47e9975c6fa10acb7f3b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 24 Aug 2021 14:25:29 +0900 Subject: [PATCH 1473/2442] Don't bind to RoomId where it's expected to be constant --- .../Match/Components/MatchChatDisplay.cs | 12 ++++++------ .../OnlinePlay/Multiplayer/MultiplayerPlayer.cs | 7 ++++--- .../OnlinePlay/Playlists/PlaylistsPlayer.cs | 4 ++-- osu.Game/Screens/Play/RoomSubmittingPlayer.cs | 14 +++++++------- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs index 05a3546cec..0396562959 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs @@ -10,36 +10,36 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components { public class MatchChatDisplay : StandAloneChatDisplay { - private readonly IBindable roomId = new Bindable(); private readonly IBindable channelId = new Bindable(); [Resolved(CanBeNull = true)] private ChannelManager channelManager { get; set; } + private readonly Room room; private readonly bool leaveChannelOnDispose; public MatchChatDisplay(Room room, bool leaveChannelOnDispose = true) : base(true) { + this.room = room; this.leaveChannelOnDispose = leaveChannelOnDispose; - - roomId.BindTo(room.RoomID); - channelId.BindTo(room.ChannelId); } protected override void LoadComplete() { base.LoadComplete(); + // Required for the time being since this component is created prior to the room being joined. + channelId.BindTo(room.ChannelId); channelId.BindValueChanged(_ => updateChannel(), true); } private void updateChannel() { - if (roomId.Value == null || channelId.Value == 0) + if (room.RoomID.Value == null || channelId.Value == 0) return; - Channel.Value = channelManager?.JoinChannel(new Channel { Id = channelId.Value, Type = ChannelType.Multiplayer, Name = $"#lazermp_{roomId.Value}" }); + Channel.Value = channelManager?.JoinChannel(new Channel { Id = channelId.Value, Type = ChannelType.Multiplayer, Name = $"#lazermp_{room.RoomID.Value}" }); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index 4c26feb067..89acda27c3 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -187,10 +187,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override ResultsScreen CreateResults(ScoreInfo score) { - Debug.Assert(RoomId.Value != null); + Debug.Assert(Room.RoomID.Value != null); + return leaderboard.TeamScores.Count == 2 - ? new MultiplayerTeamResultsScreen(score, RoomId.Value.Value, PlaylistItem, leaderboard.TeamScores) - : new MultiplayerResultsScreen(score, RoomId.Value.Value, PlaylistItem); + ? new MultiplayerTeamResultsScreen(score, Room.RoomID.Value.Value, PlaylistItem, leaderboard.TeamScores) + : new MultiplayerResultsScreen(score, Room.RoomID.Value.Value, PlaylistItem); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs index c441728bb6..c76bad7828 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs @@ -51,8 +51,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists protected override ResultsScreen CreateResults(ScoreInfo score) { - Debug.Assert(RoomId.Value != null); - return new PlaylistsResultsScreen(score, RoomId.Value.Value, PlaylistItem, true); + Debug.Assert(Room.RoomID.Value != null); + return new PlaylistsResultsScreen(score, Room.RoomID.Value.Value, PlaylistItem, true); } protected override async Task PrepareScoreForResultsAsync(Score score) diff --git a/osu.Game/Screens/Play/RoomSubmittingPlayer.cs b/osu.Game/Screens/Play/RoomSubmittingPlayer.cs index 593b67a7b0..1002e7607f 100644 --- a/osu.Game/Screens/Play/RoomSubmittingPlayer.cs +++ b/osu.Game/Screens/Play/RoomSubmittingPlayer.cs @@ -1,7 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Bindables; +using System.Diagnostics; using osu.Game.Online.API; using osu.Game.Online.Rooms; using osu.Game.Scoring; @@ -13,8 +13,6 @@ namespace osu.Game.Screens.Play /// public abstract class RoomSubmittingPlayer : SubmittingPlayer { - protected readonly IBindable RoomId = new Bindable(); - protected readonly PlaylistItem PlaylistItem; protected readonly Room Room; @@ -23,18 +21,20 @@ namespace osu.Game.Screens.Play { Room = room; PlaylistItem = playlistItem; - - RoomId.BindTo(room.RoomID); } protected override APIRequest CreateTokenRequest() { - if (!(RoomId.Value is long roomId)) + if (!(Room.RoomID.Value is long roomId)) return null; return new CreateRoomScoreRequest(roomId, PlaylistItem.ID, Game.VersionHash); } - protected override APIRequest CreateSubmissionRequest(Score score, long token) => new SubmitRoomScoreRequest(token, RoomId.Value ?? 0, PlaylistItem.ID, score.ScoreInfo); + protected override APIRequest CreateSubmissionRequest(Score score, long token) + { + Debug.Assert(Room.RoomID.Value != null); + return new SubmitRoomScoreRequest(token, Room.RoomID.Value.Value, PlaylistItem.ID, score.ScoreInfo); + } } } From 4bbc98737f7a0cba8846f79b909ee2befd338cb6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 Aug 2021 14:39:03 +0900 Subject: [PATCH 1474/2442] Fix english in test steps --- osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs index 4809a737d9..31e5a9b86c 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCommentsContainer.cs @@ -90,7 +90,7 @@ namespace osu.Game.Tests.Visual.Online setUpCommentsResponse(comments); AddStep("show comments", () => commentsContainer.ShowComments(CommentableType.Beatmapset, 123)); - AddAssert("no comment showed", () => !commentsContainer.ChildrenOfType().Any()); + AddAssert("no comment shown", () => !commentsContainer.ChildrenOfType().Any()); } [TestCase(false)] @@ -120,7 +120,7 @@ namespace osu.Game.Tests.Visual.Online setUpCommentsResponse(bundle); AddStep("show comments", () => commentsContainer.ShowComments(CommentableType.Beatmapset, 123)); AddUntilStep("wait comment load", () => commentsContainer.ChildrenOfType().Any()); - AddAssert("only one comment showed", () => + AddAssert("only one comment shown", () => commentsContainer.ChildrenOfType().Count(d => d.Comment.Pinned == withPinned) == 1); } From a5f6c287eaeec370c785bf9b4afbf31734d9cceb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 Aug 2021 14:43:28 +0900 Subject: [PATCH 1475/2442] Split out pinned comment content to only be constructed when required --- osu.Game/Overlays/Comments/DrawableComment.cs | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/osu.Game/Overlays/Comments/DrawableComment.cs b/osu.Game/Overlays/Comments/DrawableComment.cs index d80d566f3a..a44f3a7643 100644 --- a/osu.Game/Overlays/Comments/DrawableComment.cs +++ b/osu.Game/Overlays/Comments/DrawableComment.cs @@ -138,36 +138,13 @@ namespace osu.Game.Overlays.Comments AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, Spacing = new Vector2(10, 0), - Children = new Drawable[] + Children = new[] { username = new LinkFlowContainer(s => s.Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold)) { AutoSizeAxes = Axes.Both }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(2, 0), - Alpha = Comment.Pinned ? 1 : 0, - Children = new Drawable[] - { - new SpriteIcon - { - Icon = FontAwesome.Solid.Thumbtack, - Size = new Vector2(14), - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - }, - new OsuSpriteText - { - Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold), - Text = CommentsStrings.Pinned, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - } - }, - }, + Comment.Pinned ? new PinnedCommentNotice() : Empty(), new ParentUsername(Comment), new OsuSpriteText { @@ -346,9 +323,7 @@ namespace osu.Game.Overlays.Comments this.FadeTo(show.NewValue ? 1 : 0); }, true); childrenExpanded.BindValueChanged(expanded => childCommentsVisibilityContainer.FadeTo(expanded.NewValue ? 1 : 0), true); - updateButtonsState(); - base.LoadComplete(); } @@ -417,6 +392,33 @@ namespace osu.Game.Overlays.Comments }; } + private class PinnedCommentNotice : FillFlowContainer + { + public PinnedCommentNotice() + { + AutoSizeAxes = Axes.Both; + Direction = FillDirection.Horizontal; + Spacing = new Vector2(2, 0); + Children = new Drawable[] + { + new SpriteIcon + { + Icon = FontAwesome.Solid.Thumbtack, + Size = new Vector2(14), + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + new OsuSpriteText + { + Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold), + Text = CommentsStrings.Pinned, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + } + }; + } + } + private class ParentUsername : FillFlowContainer, IHasTooltip { public LocalisableString TooltipText => getParentMessage(); From 342f2d756d2f8e5f0b340a417b550bd52a18f581 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 24 Aug 2021 15:18:05 +0900 Subject: [PATCH 1476/2442] Fix test not working intermittently --- osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs index 290ba3317b..4b54cd3510 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs @@ -142,7 +142,11 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("set hud to never show", () => localConfig.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never)); createNew(); + AddUntilStep("wait for hud load", () => hudOverlay.IsLoaded); + AddUntilStep("wait for components to be hidden", () => !hudOverlay.ChildrenOfType().Single().IsPresent); + + AddStep("reload components", () => hudOverlay.ChildrenOfType().Single().Reload()); AddUntilStep("skinnable components loaded", () => hudOverlay.ChildrenOfType().Single().ComponentsLoaded); } From 9f17c38e36981e8beb5fdd9b3c6d8fb53d991322 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 24 Aug 2021 15:18:27 +0900 Subject: [PATCH 1477/2442] Fix hud overlay components being blocked from load --- osu.Game/Skinning/SkinnableTargetContainer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Skinning/SkinnableTargetContainer.cs b/osu.Game/Skinning/SkinnableTargetContainer.cs index 53b142f09a..9f94cdd7e7 100644 --- a/osu.Game/Skinning/SkinnableTargetContainer.cs +++ b/osu.Game/Skinning/SkinnableTargetContainer.cs @@ -18,6 +18,8 @@ namespace osu.Game.Skinning private readonly BindableList components = new BindableList(); + public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks; + public bool ComponentsLoaded { get; private set; } public SkinnableTargetContainer(SkinnableTarget target) From 3f0f8206534d226b70de53b9bf1d7fbc8080b831 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 Aug 2021 16:54:19 +0900 Subject: [PATCH 1478/2442] Add comment explaining reasoning for override --- osu.Game/Skinning/SkinnableTargetContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinnableTargetContainer.cs b/osu.Game/Skinning/SkinnableTargetContainer.cs index 9f94cdd7e7..e7125bb034 100644 --- a/osu.Game/Skinning/SkinnableTargetContainer.cs +++ b/osu.Game/Skinning/SkinnableTargetContainer.cs @@ -18,7 +18,7 @@ namespace osu.Game.Skinning private readonly BindableList components = new BindableList(); - public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks; + public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks; // ensure that components are loaded even if the target container is hidden (ie. due to user toggle). public bool ComponentsLoaded { get; private set; } From 6252b8aa42a29e06dcf19a128a77b9dd2d362c9a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 Aug 2021 19:05:45 +0900 Subject: [PATCH 1479/2442] Fix hold notes handling all input ever --- osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs | 3 +-- .../Objects/Drawables/DrawableHoldNoteHead.cs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index d1310d42eb..2923a2af2f 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -275,9 +275,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables return false; beginHoldAt(Time.Current - Head.HitObject.StartTime); - Head.UpdateResult(); - return true; + return Head.UpdateResult(); } private void beginHoldAt(double timeOffset) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs index be600f0d47..8458345998 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables Origin = Anchor.TopCentre; } - public void UpdateResult() => base.UpdateResult(true); + public bool UpdateResult() => base.UpdateResult(true); protected override void UpdateInitialTransforms() { From a6c2cbd2e593224b0aef26e2a117e5707bbd6bf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 24 Aug 2021 20:53:27 +0200 Subject: [PATCH 1480/2442] Add countdown settings to beatmap info model --- .../Formats/LegacyBeatmapDecoderTest.cs | 3 +- .../Beatmaps/Formats/OsuJsonDecoderTest.cs | 3 +- osu.Game/Beatmaps/BeatmapInfo.cs | 4 +- osu.Game/Beatmaps/CountdownType.cs | 16 + .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 12 +- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 7 +- ...824185035_AddCountdownSettings.Designer.cs | 513 ++++++++++++++++++ .../20210824185035_AddCountdownSettings.cs | 23 + .../Migrations/OsuDbContextModelSnapshot.cs | 4 +- 9 files changed, 573 insertions(+), 12 deletions(-) create mode 100644 osu.Game/Beatmaps/CountdownType.cs create mode 100644 osu.Game/Migrations/20210824185035_AddCountdownSettings.Designer.cs create mode 100644 osu.Game/Migrations/20210824185035_AddCountdownSettings.cs diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 4fe1cf3790..dd4c24698c 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -58,12 +58,13 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", metadata.AudioFile); Assert.AreEqual(0, beatmapInfo.AudioLeadIn); Assert.AreEqual(164471, metadata.PreviewTime); - Assert.IsFalse(beatmapInfo.Countdown); Assert.AreEqual(0.7f, beatmapInfo.StackLeniency); Assert.IsTrue(beatmapInfo.RulesetID == 0); Assert.IsFalse(beatmapInfo.LetterboxInBreaks); Assert.IsFalse(beatmapInfo.SpecialStyle); Assert.IsFalse(beatmapInfo.WidescreenStoryboard); + Assert.AreEqual(CountdownType.None, beatmapInfo.Countdown); + Assert.AreEqual(0, beatmapInfo.CountdownOffset); } } diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs index e97c83e2c2..1fc3abef9a 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs @@ -50,12 +50,13 @@ namespace osu.Game.Tests.Beatmaps.Formats var beatmap = decodeAsJson(normal); var beatmapInfo = beatmap.BeatmapInfo; Assert.AreEqual(0, beatmapInfo.AudioLeadIn); - Assert.AreEqual(false, beatmapInfo.Countdown); Assert.AreEqual(0.7f, beatmapInfo.StackLeniency); Assert.AreEqual(false, beatmapInfo.SpecialStyle); Assert.IsTrue(beatmapInfo.RulesetID == 0); Assert.AreEqual(false, beatmapInfo.LetterboxInBreaks); Assert.AreEqual(false, beatmapInfo.WidescreenStoryboard); + Assert.AreEqual(CountdownType.None, beatmapInfo.Countdown); + Assert.AreEqual(0, beatmapInfo.CountdownOffset); } [Test] diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 36cb97e8d7..fe734cd1b5 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -83,7 +83,6 @@ namespace osu.Game.Beatmaps // General public double AudioLeadIn { get; set; } - public bool Countdown { get; set; } = true; public float StackLeniency { get; set; } = 0.7f; public bool SpecialStyle { get; set; } @@ -95,6 +94,9 @@ namespace osu.Game.Beatmaps public bool WidescreenStoryboard { get; set; } public bool EpilepsyWarning { get; set; } + public CountdownType Countdown { get; set; } = CountdownType.Normal; + public int CountdownOffset { get; set; } + // Editor // This bookmarks stuff is necessary because DB doesn't know how to store int[] [JsonIgnore] diff --git a/osu.Game/Beatmaps/CountdownType.cs b/osu.Game/Beatmaps/CountdownType.cs new file mode 100644 index 0000000000..1831b4576b --- /dev/null +++ b/osu.Game/Beatmaps/CountdownType.cs @@ -0,0 +1,16 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Beatmaps +{ + /// + /// The type of countdown shown before the start of gameplay on a given beatmap. + /// + public enum CountdownType + { + None = 0, + Normal = 1, + HalfSpeed = 2, + DoubleSpeed = 3 + } +} diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 40bc75e847..0ddc9e4c48 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -121,10 +121,6 @@ namespace osu.Game.Beatmaps.Formats metadata.PreviewTime = getOffsetTime(Parsing.ParseInt(pair.Value)); break; - case @"Countdown": - beatmap.BeatmapInfo.Countdown = Parsing.ParseInt(pair.Value) == 1; - break; - case @"SampleSet": defaultSampleBank = (LegacySampleBank)Enum.Parse(typeof(LegacySampleBank), pair.Value); break; @@ -176,6 +172,14 @@ namespace osu.Game.Beatmaps.Formats case @"EpilepsyWarning": beatmap.BeatmapInfo.EpilepsyWarning = Parsing.ParseInt(pair.Value) == 1; break; + + case @"Countdown": + beatmap.BeatmapInfo.Countdown = (CountdownType)Enum.Parse(typeof(CountdownType), pair.Value); + break; + + case @"CountdownOffset": + beatmap.BeatmapInfo.CountdownOffset = Parsing.ParseInt(pair.Value); + break; } } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 524b556ddc..fade7183a3 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -79,8 +79,7 @@ namespace osu.Game.Beatmaps.Formats if (beatmap.Metadata.AudioFile != null) writer.WriteLine(FormattableString.Invariant($"AudioFilename: {Path.GetFileName(beatmap.Metadata.AudioFile)}")); writer.WriteLine(FormattableString.Invariant($"AudioLeadIn: {beatmap.BeatmapInfo.AudioLeadIn}")); writer.WriteLine(FormattableString.Invariant($"PreviewTime: {beatmap.Metadata.PreviewTime}")); - // Todo: Not all countdown types are supported by lazer yet - writer.WriteLine(FormattableString.Invariant($"Countdown: {(beatmap.BeatmapInfo.Countdown ? '1' : '0')}")); + writer.WriteLine(FormattableString.Invariant($"Countdown: {(int)beatmap.BeatmapInfo.Countdown}")); writer.WriteLine(FormattableString.Invariant($"SampleSet: {toLegacySampleBank(beatmap.ControlPointInfo.SamplePointAt(double.MinValue).SampleBank)}")); writer.WriteLine(FormattableString.Invariant($"StackLeniency: {beatmap.BeatmapInfo.StackLeniency}")); writer.WriteLine(FormattableString.Invariant($"Mode: {beatmap.BeatmapInfo.RulesetID}")); @@ -95,8 +94,8 @@ namespace osu.Game.Beatmaps.Formats // writer.WriteLine(@"SkinPreference:" + b.SkinPreference); if (beatmap.BeatmapInfo.EpilepsyWarning) writer.WriteLine(@"EpilepsyWarning: 1"); - // if (b.CountdownOffset > 0) - // writer.WriteLine(@"CountdownOffset: " + b.CountdownOffset.ToString()); + if (beatmap.BeatmapInfo.CountdownOffset > 0) + writer.WriteLine(FormattableString.Invariant($@"CountdownOffset: {beatmap.BeatmapInfo.CountdownOffset}")); if (beatmap.BeatmapInfo.RulesetID == 3) writer.WriteLine(FormattableString.Invariant($"SpecialStyle: {(beatmap.BeatmapInfo.SpecialStyle ? '1' : '0')}")); writer.WriteLine(FormattableString.Invariant($"WidescreenStoryboard: {(beatmap.BeatmapInfo.WidescreenStoryboard ? '1' : '0')}")); diff --git a/osu.Game/Migrations/20210824185035_AddCountdownSettings.Designer.cs b/osu.Game/Migrations/20210824185035_AddCountdownSettings.Designer.cs new file mode 100644 index 0000000000..afeb42130d --- /dev/null +++ b/osu.Game/Migrations/20210824185035_AddCountdownSettings.Designer.cs @@ -0,0 +1,513 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using osu.Game.Database; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20210824185035_AddCountdownSettings")] + partial class AddCountdownSettings + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.6-servicing-10079"); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("ApproachRate"); + + b.Property("CircleSize"); + + b.Property("DrainRate"); + + b.Property("OverallDifficulty"); + + b.Property("SliderMultiplier"); + + b.Property("SliderTickRate"); + + b.HasKey("ID"); + + b.ToTable("BeatmapDifficulty"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("AudioLeadIn"); + + b.Property("BPM"); + + b.Property("BaseDifficultyID"); + + b.Property("BeatDivisor"); + + b.Property("BeatmapSetInfoID"); + + b.Property("Countdown"); + + b.Property("CountdownOffset"); + + b.Property("DistanceSpacing"); + + b.Property("EpilepsyWarning"); + + b.Property("GridSize"); + + b.Property("Hash"); + + b.Property("Hidden"); + + b.Property("Length"); + + b.Property("LetterboxInBreaks"); + + b.Property("MD5Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + + b.Property("Path"); + + b.Property("RulesetID"); + + b.Property("SpecialStyle"); + + b.Property("StackLeniency"); + + b.Property("StarDifficulty"); + + b.Property("Status"); + + b.Property("StoredBookmarks"); + + b.Property("TimelineZoom"); + + b.Property("Version"); + + b.Property("WidescreenStoryboard"); + + b.HasKey("ID"); + + b.HasIndex("BaseDifficultyID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("Hash"); + + b.HasIndex("MD5Hash"); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("BeatmapInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Artist"); + + b.Property("ArtistUnicode"); + + b.Property("AudioFile"); + + b.Property("AuthorID") + .HasColumnName("AuthorID"); + + b.Property("AuthorString") + .HasColumnName("Author"); + + b.Property("BackgroundFile"); + + b.Property("PreviewTime"); + + b.Property("Source"); + + b.Property("Tags"); + + b.Property("Title"); + + b.Property("TitleUnicode"); + + b.HasKey("ID"); + + b.ToTable("BeatmapMetadata"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BeatmapSetInfoID"); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("FileInfoID"); + + b.ToTable("BeatmapSetFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapSetID"); + + b.Property("Protected"); + + b.Property("Status"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapSetID") + .IsUnique(); + + b.ToTable("BeatmapSetInfo"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Key") + .HasColumnName("Key"); + + b.Property("RulesetID"); + + b.Property("SkinInfoID"); + + b.Property("StringValue") + .HasColumnName("Value"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("SkinInfoID"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("osu.Game.IO.FileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Hash"); + + b.Property("ReferenceCount"); + + b.HasKey("ID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("ReferenceCount"); + + b.ToTable("FileInfo"); + }); + + modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntAction") + .HasColumnName("Action"); + + b.Property("KeysString") + .HasColumnName("Keys"); + + b.Property("RulesetID"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("IntAction"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("KeyBinding"); + }); + + modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Available"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.Property("ShortName"); + + b.HasKey("ID"); + + b.HasIndex("Available"); + + b.HasIndex("ShortName") + .IsUnique(); + + b.ToTable("RulesetInfo"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("ScoreInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("ScoreInfoID"); + + b.ToTable("ScoreFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Accuracy") + .HasColumnType("DECIMAL(1,4)"); + + b.Property("BeatmapInfoID"); + + b.Property("Combo"); + + b.Property("Date"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MaxCombo"); + + b.Property("ModsJson") + .HasColumnName("Mods"); + + b.Property("OnlineScoreID"); + + b.Property("PP"); + + b.Property("Rank"); + + b.Property("RulesetID"); + + b.Property("StatisticsJson") + .HasColumnName("Statistics"); + + b.Property("TotalScore"); + + b.Property("UserID") + .HasColumnName("UserID"); + + b.Property("UserString") + .HasColumnName("User"); + + b.HasKey("ID"); + + b.HasIndex("BeatmapInfoID"); + + b.HasIndex("OnlineScoreID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("ScoreInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("SkinInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("SkinInfoID"); + + b.ToTable("SkinFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Creator"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.ToTable("SkinInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") + .WithMany("Beatmaps") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") + .WithMany("Files") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Settings") + .HasForeignKey("SkinInfoID"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Scoring.ScoreInfo") + .WithMany("Files") + .HasForeignKey("ScoreInfoID"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") + .WithMany("Scores") + .HasForeignKey("BeatmapInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Files") + .HasForeignKey("SkinInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/osu.Game/Migrations/20210824185035_AddCountdownSettings.cs b/osu.Game/Migrations/20210824185035_AddCountdownSettings.cs new file mode 100644 index 0000000000..564f5f4520 --- /dev/null +++ b/osu.Game/Migrations/20210824185035_AddCountdownSettings.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace osu.Game.Migrations +{ + public partial class AddCountdownSettings : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "CountdownOffset", + table: "BeatmapInfo", + nullable: false, + defaultValue: 0); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "CountdownOffset", + table: "BeatmapInfo"); + } + } +} diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs index f518cfb42b..470907ada6 100644 --- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs +++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs @@ -53,7 +53,9 @@ namespace osu.Game.Migrations b.Property("BeatmapSetInfoID"); - b.Property("Countdown"); + b.Property("Countdown"); + + b.Property("CountdownOffset"); b.Property("DistanceSpacing"); From acc27fc79c390c24a450a27f7a316c165f88fccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 24 Aug 2021 22:04:56 +0200 Subject: [PATCH 1481/2442] Add test case for countdown in encode-decode stability test --- osu.Game.Tests/Resources/countdown-settings.osu | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 osu.Game.Tests/Resources/countdown-settings.osu diff --git a/osu.Game.Tests/Resources/countdown-settings.osu b/osu.Game.Tests/Resources/countdown-settings.osu new file mode 100644 index 0000000000..333e48150d --- /dev/null +++ b/osu.Game.Tests/Resources/countdown-settings.osu @@ -0,0 +1,5 @@ +osu file format v14 + +[General] +Countdown: 2 +CountdownOffset: 3 From c91feb29684c3a80b9af50be9ba63a53f3754b91 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Wed, 25 Aug 2021 11:18:21 +1000 Subject: [PATCH 1482/2442] Fix multiplying instead of dividing by scalingFactor --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index f048142b56..f6760235b4 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -54,10 +54,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills // We want to nerf objects that can be easily seen within the Flashlight circle radius. if (i == 0) - smallDistNerf = Math.Min(1.0, jumpDistance / 50.0); + smallDistNerf = Math.Min(1.0, jumpDistance / 75.0); // We also want to nerf stacks so that only the first object of the stack is accounted for. - double stackNerf = Math.Min(1.0, osuPrevious.JumpDistance * scalingFactor / 50.0); + double stackNerf = Math.Min(1.0, (osuPrevious.JumpDistance / scalingFactor) / 25.0); result += Math.Pow(0.8, i) * stackNerf * scalingFactor * jumpDistance / cumulativeStrainTime; } From 414457ba57138b20f5582235fbbe09d9108ef861 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 Aug 2021 13:24:52 +0900 Subject: [PATCH 1483/2442] Add basic xmldoc explaining `CountdownOffset` --- osu.Game/Beatmaps/BeatmapInfo.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index fe734cd1b5..353636c8af 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -95,6 +95,10 @@ namespace osu.Game.Beatmaps public bool EpilepsyWarning { get; set; } public CountdownType Countdown { get; set; } = CountdownType.Normal; + + /// + /// The number of beats to move the countdown backwards (compared to its default location). + /// public int CountdownOffset { get; set; } // Editor From 84637b59ef836b88f61842e4543920f3729d02be Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 25 Aug 2021 07:40:41 +0300 Subject: [PATCH 1484/2442] Define `DifficultyBindableWithCurrent` and use in `SliderControl` --- .../Mods/DifficultyAdjustSettingsControl.cs | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs index 3978378c3a..67b24d24d0 100644 --- a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs +++ b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs @@ -1,6 +1,7 @@ // 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 osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -91,7 +92,7 @@ namespace osu.Game.Rulesets.Mods { // This is required as SettingsItem relies heavily on this bindable for internal use. // The actual update flow is done via the bindable provided in the constructor. - private readonly BindableWithCurrent current = new BindableWithCurrent(); + private readonly DifficultyBindableWithCurrent current = new DifficultyBindableWithCurrent(); public Bindable Current { @@ -114,5 +115,30 @@ namespace osu.Game.Rulesets.Mods RelativeSizeAxes = Axes.X; } } + + private class DifficultyBindableWithCurrent : DifficultyBindable, IHasCurrentValue + { + private Bindable currentBound; + + public Bindable Current + { + get => this; + set + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + if (currentBound != null) UnbindFrom(currentBound); + BindTo(currentBound = value); + } + } + + public DifficultyBindableWithCurrent(float? defaultValue = default) + : base(defaultValue) + { + } + + protected override Bindable CreateInstance() => new DifficultyBindableWithCurrent(); + } } } From e1ab3434ed9feca60ebb6440947a79f318bad808 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 Aug 2021 14:25:57 +0900 Subject: [PATCH 1485/2442] Add ability to handle user join/leave/kick events in `MultiplayerComposite`s --- .../Online/Multiplayer/MultiplayerClient.cs | 36 ++++++++++++------- .../Multiplayer/MultiplayerRoomComposite.cs | 34 ++++++++++++++++++ 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 2a0635c98c..75bbaec0ef 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -33,6 +33,12 @@ namespace osu.Game.Online.Multiplayer /// public event Action? RoomUpdated; + public event Action? UserJoined; + + public event Action? UserLeft; + + public event Action? UserKicked; + /// /// Invoked when the multiplayer server requests the current beatmap to be loaded into play. /// @@ -366,11 +372,26 @@ namespace osu.Game.Online.Multiplayer Room.Users.Add(user); + UserJoined?.Invoke(user); RoomUpdated?.Invoke(); }, false); } - Task IMultiplayerClient.UserLeft(MultiplayerRoomUser user) + Task IMultiplayerClient.UserLeft(MultiplayerRoomUser user) => + handleUserLeft(user, UserLeft); + + Task IMultiplayerClient.UserKicked(MultiplayerRoomUser user) + { + if (LocalUser == null) + return Task.CompletedTask; + + if (user.Equals(LocalUser)) + LeaveRoom(); + + return handleUserLeft(user, UserKicked); + } + + private Task handleUserLeft(MultiplayerRoomUser user, Action? callback) { if (Room == null) return Task.CompletedTask; @@ -383,24 +404,13 @@ namespace osu.Game.Online.Multiplayer Room.Users.Remove(user); PlayingUserIds.Remove(user.UserID); + callback?.Invoke(user); RoomUpdated?.Invoke(); }, false); return Task.CompletedTask; } - Task IMultiplayerClient.UserKicked(MultiplayerRoomUser user) - { - if (LocalUser == null) - return Task.CompletedTask; - - if (user.Equals(LocalUser)) - LeaveRoom(); - - // TODO: also inform users of the kick operation. - return ((IMultiplayerClient)this).UserLeft(user); - } - Task IMultiplayerClient.HostChanged(int userId) { if (Room == null) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs index d334c618f5..0f256160eb 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs @@ -20,9 +20,38 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer base.LoadComplete(); Client.RoomUpdated += OnRoomUpdated; + + Client.UserLeft += UserLeft; + Client.UserKicked += UserKicked; + Client.UserJoined += UserJoined; + OnRoomUpdated(); } + /// + /// Invoked when a user has joined the room. + /// + /// The user. + protected virtual void UserJoined(MultiplayerRoomUser user) + { + } + + /// + /// Invoked when a user has been kicked from the room (including the local user). + /// + /// The user. + protected virtual void UserKicked(MultiplayerRoomUser user) + { + } + + /// + /// Invoked when a user has left the room. + /// + /// The user. + protected virtual void UserLeft(MultiplayerRoomUser user) + { + } + /// /// Invoked when any change occurs to the multiplayer room. /// @@ -33,7 +62,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override void Dispose(bool isDisposing) { if (Client != null) + { + Client.UserLeft -= UserLeft; + Client.UserKicked -= UserKicked; + Client.UserJoined -= UserJoined; Client.RoomUpdated -= OnRoomUpdated; + } base.Dispose(isDisposing); } From 6aa894e55e0f25d184125efa649c610ef6816648 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 Aug 2021 18:23:02 +0900 Subject: [PATCH 1486/2442] Split out separate component --- .../Drawables/DrawableManiaHitObject.cs | 6 -- osu.Game.Rulesets.Mania/UI/Column.cs | 38 ++------- .../Objects/Drawables/DrawableHitObject.cs | 5 ++ .../UI/GameplaySampleTriggerSource.cs | 84 +++++++++++++++++++ 4 files changed, 94 insertions(+), 39 deletions(-) create mode 100644 osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index 5aff4e200b..9ac223a0d7 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -6,7 +6,6 @@ using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Game.Audio; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.Mania.UI; @@ -29,11 +28,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables [Resolved(canBeNull: true)] private ManiaPlayfield playfield { get; set; } - /// - /// Gets the samples that are played by this object during gameplay. - /// - public ISampleInfo[] GetGameplaySamples() => Samples.Samples; - protected override float SamplePlaybackPosition { get diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 9b5893b268..f5e30efd91 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.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.Linq; using osuTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -19,6 +18,7 @@ using osuTK; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; +using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Mania.UI { @@ -28,12 +28,6 @@ namespace osu.Game.Rulesets.Mania.UI public const float COLUMN_WIDTH = 80; public const float SPECIAL_COLUMN_WIDTH = 70; - /// - /// For hitsounds played by this (i.e. not as a result of hitting a hitobject), - /// a certain number of samples are allowed to be played concurrently so that it feels better when spam-pressing the key. - /// - private const int max_concurrent_hitsounds = OsuGameBase.SAMPLE_CONCURRENCY; - /// /// The index of this column as part of the whole playfield. /// @@ -45,10 +39,10 @@ namespace osu.Game.Rulesets.Mania.UI internal readonly Container TopLevelContainer; private readonly DrawablePool hitExplosionPool; private readonly OrderedHitPolicy hitPolicy; - private readonly Container hitSounds; - public Container UnderlayElements => HitObjectArea.UnderlayElements; + private readonly GameplaySampleTriggerSource sampleTriggerSource; + public Column(int index) { Index = index; @@ -64,6 +58,7 @@ namespace osu.Game.Rulesets.Mania.UI InternalChildren = new[] { hitExplosionPool = new DrawablePool(5), + sampleTriggerSource = new GameplaySampleTriggerSource(HitObjectContainer), // For input purposes, the background is added at the highest depth, but is then proxied back below all other elements background.CreateProxy(), HitObjectArea = new ColumnHitObjectArea(Index, HitObjectContainer) { RelativeSizeAxes = Axes.Both }, @@ -72,12 +67,6 @@ namespace osu.Game.Rulesets.Mania.UI RelativeSizeAxes = Axes.Both }, background, - hitSounds = new Container - { - Name = "Column samples pool", - RelativeSizeAxes = Axes.Both, - Children = Enumerable.Range(0, max_concurrent_hitsounds).Select(_ => new SkinnableSound()).ToArray() - }, TopLevelContainer = new Container { RelativeSizeAxes = Axes.Both } }; @@ -133,29 +122,12 @@ namespace osu.Game.Rulesets.Mania.UI HitObjectArea.Explosions.Add(hitExplosionPool.Get(e => e.Apply(result))); } - private int nextHitSoundIndex; - public bool OnPressed(ManiaAction action) { if (action != Action.Value) return false; - var nextObject = - HitObjectContainer.AliveObjects.FirstOrDefault(h => h.HitObject.StartTime > Time.Current) ?? - // fallback to non-alive objects to find next off-screen object - HitObjectContainer.Objects.FirstOrDefault(h => h.HitObject.StartTime > Time.Current) ?? - HitObjectContainer.Objects.LastOrDefault(); - - if (nextObject is DrawableManiaHitObject maniaObject) - { - var hitSound = hitSounds[nextHitSoundIndex]; - - hitSound.Samples = maniaObject.GetGameplaySamples(); - hitSound.Play(); - - nextHitSoundIndex = (nextHitSoundIndex + 1) % max_concurrent_hitsounds; - } - + sampleTriggerSource.Play(); return true; } diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 29d8a475ef..b3e1b24d8d 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -54,6 +54,11 @@ namespace osu.Game.Rulesets.Objects.Drawables /// public readonly Bindable AccentColour = new Bindable(Color4.Gray); + /// + /// Gets the samples that are played by this object during gameplay. + /// + public ISampleInfo[] GetGameplaySamples() => Samples.Samples; + protected PausableSkinnableSound Samples { get; private set; } public virtual IEnumerable GetSamples() => HitObject.Samples; diff --git a/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs b/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs new file mode 100644 index 0000000000..fedbcd541c --- /dev/null +++ b/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs @@ -0,0 +1,84 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Audio; +using osu.Game.Rulesets.Objects; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.UI +{ + /// + /// A component which can trigger the most appropriate hit sound for a given point in time, based on the state of a + /// + public class GameplaySampleTriggerSource : CompositeDrawable + { + private readonly HitObjectContainer hitObjectContainer; + + private int nextHitSoundIndex; + + /// + /// The number of concurrent samples allowed to be played concurrently so that it feels better when spam-pressing a key. + /// + private const int max_concurrent_hitsounds = OsuGameBase.SAMPLE_CONCURRENCY; + + private readonly Container hitSounds; + + [Resolved] + private DrawableRuleset drawableRuleset { get; set; } + + public GameplaySampleTriggerSource(HitObjectContainer hitObjectContainer) + { + this.hitObjectContainer = hitObjectContainer; + InternalChildren = new Drawable[] + { + hitSounds = new Container + { + Name = "concurrent sample pool", + RelativeSizeAxes = Axes.Both, + Children = Enumerable.Range(0, max_concurrent_hitsounds).Select(_ => new SkinnableSound()).ToArray() + }, + }; + } + + private ISampleInfo[] playableSampleInfo; + + /// + /// Play the most appropriate hit sound for the current point in time. + /// + public void Play() + { + var nextObject = + hitObjectContainer.AliveObjects.FirstOrDefault(h => h.HitObject.StartTime > Time.Current)?.HitObject ?? + // fallback to non-alive objects to find next off-screen object + // TODO: make lookup more efficient? + drawableRuleset.Objects.FirstOrDefault(h => h.StartTime > Time.Current) ?? + drawableRuleset.Objects.LastOrDefault(); + + if (nextObject != null) + { + var hitSound = getNextSample(); + playableSampleInfo = GetPlayableSampleInfo(nextObject); + hitSound.Samples = playableSampleInfo; + hitSound.Play(); + } + } + + protected virtual ISampleInfo[] GetPlayableSampleInfo(HitObject nextObject) => + // TODO: avoid cast somehow? + nextObject.Samples.Cast().ToArray(); + + private SkinnableSound getNextSample() + { + var hitSound = hitSounds[nextHitSoundIndex]; + + // round robin over available samples to allow for concurrent playback. + nextHitSoundIndex = (nextHitSoundIndex + 1) % max_concurrent_hitsounds; + + return hitSound; + } + } +} From 4a294d4de47c9cef709457ee8cf8357be11aa894 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 Aug 2021 19:05:59 +0900 Subject: [PATCH 1487/2442] Optimise fallback logic to reduce lookups to bare minimum --- .../UI/GameplaySampleTriggerSource.cs | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs b/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs index fedbcd541c..51f3052509 100644 --- a/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs +++ b/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs @@ -44,32 +44,44 @@ namespace osu.Game.Rulesets.UI }; } - private ISampleInfo[] playableSampleInfo; + private HitObject fallbackObject; /// /// Play the most appropriate hit sound for the current point in time. /// public void Play() { - var nextObject = - hitObjectContainer.AliveObjects.FirstOrDefault(h => h.HitObject.StartTime > Time.Current)?.HitObject ?? - // fallback to non-alive objects to find next off-screen object - // TODO: make lookup more efficient? - drawableRuleset.Objects.FirstOrDefault(h => h.StartTime > Time.Current) ?? - drawableRuleset.Objects.LastOrDefault(); + var nextObject = hitObjectContainer.AliveObjects.FirstOrDefault(h => h.HitObject.StartTime > Time.Current)?.HitObject; + + if (nextObject == null) + { + if (fallbackObject == null || fallbackObject.StartTime < Time.Current) + { + // in the case a next object isn't available in drawable form, we need to do a somewhat expensive traversal to get a valid sound to play. + // note that we don't want to cache the object if it is an alive object, as once it is hit we don't want to continue playing its sound. + // check whether we can use the previous computed sample. + + // fallback to non-alive objects to find next off-screen object + // TODO: make lookup more efficient? + fallbackObject = hitObjectContainer.Entries + .Where(e => e.Result?.HasResult != true && e.HitObject.StartTime > Time.Current)? + .OrderBy(e => e.HitObject.StartTime) + .FirstOrDefault()?.HitObject ?? hitObjectContainer.Entries.FirstOrDefault()?.HitObject; + } + + nextObject = fallbackObject; + } if (nextObject != null) { var hitSound = getNextSample(); - playableSampleInfo = GetPlayableSampleInfo(nextObject); - hitSound.Samples = playableSampleInfo; + hitSound.Samples = GetPlayableSampleInfo(nextObject).Select(s => nextObject.SampleControlPoint.ApplyTo(s)).Cast().ToArray(); hitSound.Play(); } } - protected virtual ISampleInfo[] GetPlayableSampleInfo(HitObject nextObject) => - // TODO: avoid cast somehow? - nextObject.Samples.Cast().ToArray(); + protected virtual HitSampleInfo[] GetPlayableSampleInfo(HitObject nextObject) => + nextObject.Samples.ToArray(); private SkinnableSound getNextSample() { From 681215e5b58a799edc1c9e4098e9a781a5011105 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 Aug 2021 14:57:41 +0900 Subject: [PATCH 1488/2442] Rewrite object lookup to use previous entry regardless This changes the fallback logic to always prefer the previous resolved lifetime entry rather than fallback to the first entry ever. I think this is more correct in all cases. Also rewrites the inline comments to hopefully be easier to parse. --- .../UI/GameplaySampleTriggerSource.cs | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs b/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs index 51f3052509..1713104f01 100644 --- a/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs +++ b/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs @@ -16,15 +16,15 @@ namespace osu.Game.Rulesets.UI /// public class GameplaySampleTriggerSource : CompositeDrawable { - private readonly HitObjectContainer hitObjectContainer; - - private int nextHitSoundIndex; - /// /// The number of concurrent samples allowed to be played concurrently so that it feels better when spam-pressing a key. /// private const int max_concurrent_hitsounds = OsuGameBase.SAMPLE_CONCURRENCY; + private readonly HitObjectContainer hitObjectContainer; + + private int nextHitSoundIndex; + private readonly Container hitSounds; [Resolved] @@ -44,32 +44,38 @@ namespace osu.Game.Rulesets.UI }; } - private HitObject fallbackObject; + private HitObjectLifetimeEntry fallbackObject; /// /// Play the most appropriate hit sound for the current point in time. /// public void Play() { + // The most optimal lookup case we have is when an object is alive. There are usually very few alive objects so there's no drawbacks in attempting this lookup each time. var nextObject = hitObjectContainer.AliveObjects.FirstOrDefault(h => h.HitObject.StartTime > Time.Current)?.HitObject; + // In the case a next object isn't available in drawable form, we need to do a somewhat expensive traversal to get a valid sound to play. if (nextObject == null) { - if (fallbackObject == null || fallbackObject.StartTime < Time.Current) + // This lookup can be skipped if the last entry is still valid (in the future and not yet hit). + if (fallbackObject == null || fallbackObject.HitObject.StartTime < Time.Current || fallbackObject.Result.IsHit) { - // in the case a next object isn't available in drawable form, we need to do a somewhat expensive traversal to get a valid sound to play. - // note that we don't want to cache the object if it is an alive object, as once it is hit we don't want to continue playing its sound. - // check whether we can use the previous computed sample. + // We need to use lifetime entries to find the next object (we can't just use `hitObjectContainer.Objects` due to pooling - it may even be empty). + // If required, we can make this lookup more efficient by adding support to get next-future-entry in LifetimeEntryManager. + var lookup = hitObjectContainer.Entries + .Where(e => e.Result?.HasResult != true && e.HitObject.StartTime > Time.Current) + .OrderBy(e => e.HitObject.StartTime) + .FirstOrDefault(); - // fallback to non-alive objects to find next off-screen object - // TODO: make lookup more efficient? - fallbackObject = hitObjectContainer.Entries - .Where(e => e.Result?.HasResult != true && e.HitObject.StartTime > Time.Current)? - .OrderBy(e => e.HitObject.StartTime) - .FirstOrDefault()?.HitObject ?? hitObjectContainer.Entries.FirstOrDefault()?.HitObject; + // If the lookup failed, use the previously resolved lookup (we still want to play a sound, and it is still likely the most valid result). + if (lookup != null) + fallbackObject = lookup; + + // If we still can't find anything, just play whatever we can to get a sound out. + fallbackObject ??= hitObjectContainer.Entries.FirstOrDefault(); } - nextObject = fallbackObject; + nextObject = fallbackObject?.HitObject; } if (nextObject != null) From a1936b141bb3acf91e79ca6725d14b5300e114fc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 Aug 2021 15:24:01 +0900 Subject: [PATCH 1489/2442] Refactor base class to allow correct usage in taiko drum --- .../UI/GameplaySampleTriggerSource.cs | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs b/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs index 1713104f01..015c85beb9 100644 --- a/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs +++ b/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs @@ -49,7 +49,29 @@ namespace osu.Game.Rulesets.UI /// /// Play the most appropriate hit sound for the current point in time. /// - public void Play() + public virtual void Play() + { + var nextObject = GetMostValidObject(); + + if (nextObject == null) + return; + + var samples = nextObject.Samples + .Select(s => nextObject.SampleControlPoint.ApplyTo(s)) + .Cast() + .ToArray(); + + PlaySamples(samples); + } + + protected void PlaySamples(ISampleInfo[] samples) + { + var hitSound = getNextSample(); + hitSound.Samples = samples; + hitSound.Play(); + } + + protected HitObject GetMostValidObject() { // The most optimal lookup case we have is when an object is alive. There are usually very few alive objects so there's no drawbacks in attempting this lookup each time. var nextObject = hitObjectContainer.AliveObjects.FirstOrDefault(h => h.HitObject.StartTime > Time.Current)?.HitObject; @@ -58,7 +80,7 @@ namespace osu.Game.Rulesets.UI if (nextObject == null) { // This lookup can be skipped if the last entry is still valid (in the future and not yet hit). - if (fallbackObject == null || fallbackObject.HitObject.StartTime < Time.Current || fallbackObject.Result.IsHit) + if (fallbackObject == null || fallbackObject.HitObject.StartTime < Time.Current || fallbackObject.Result?.IsHit == true) { // We need to use lifetime entries to find the next object (we can't just use `hitObjectContainer.Objects` due to pooling - it may even be empty). // If required, we can make this lookup more efficient by adding support to get next-future-entry in LifetimeEntryManager. @@ -78,17 +100,9 @@ namespace osu.Game.Rulesets.UI nextObject = fallbackObject?.HitObject; } - if (nextObject != null) - { - var hitSound = getNextSample(); - hitSound.Samples = GetPlayableSampleInfo(nextObject).Select(s => nextObject.SampleControlPoint.ApplyTo(s)).Cast().ToArray(); - hitSound.Play(); - } + return nextObject; } - protected virtual HitSampleInfo[] GetPlayableSampleInfo(HitObject nextObject) => - nextObject.Samples.ToArray(); - private SkinnableSound getNextSample() { var hitSound = hitSounds[nextHitSoundIndex]; From 8e0a04c4e5c2f4d8a40867856f90832f34b6e500 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 Aug 2021 15:24:13 +0900 Subject: [PATCH 1490/2442] Update taiko `InputDrum` to use new trigger logic --- .../Skinning/TestSceneDrawableBarLine.cs | 4 +- .../Skinning/TestSceneInputDrum.cs | 10 +- .../Skinning/TestSceneTaikoPlayfield.cs | 2 +- .../Audio/DrumSampleContainer.cs | 104 ------------------ .../Skinning/Legacy/LegacyInputDrum.cs | 10 +- .../UI/DrawableTaikoRuleset.cs | 2 +- osu.Game.Rulesets.Taiko/UI/InputDrum.cs | 43 ++++++-- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 10 +- 8 files changed, 49 insertions(+), 136 deletions(-) delete mode 100644 osu.Game.Rulesets.Taiko/Audio/DrumSampleContainer.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs index f9b8e9a985..269a855219 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning Origin = Anchor.Centre, Children = new Drawable[] { - new TaikoPlayfield(new ControlPointInfo()), + new TaikoPlayfield(), hoc = new ScrollingHitObjectContainer() } }; @@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning Origin = Anchor.Centre, Children = new Drawable[] { - new TaikoPlayfield(new ControlPointInfo()), + new TaikoPlayfield(), hoc = new ScrollingHitObjectContainer() } }; diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneInputDrum.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneInputDrum.cs index 055a292fe8..24db046748 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneInputDrum.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneInputDrum.cs @@ -5,7 +5,6 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Taiko.UI; using osuTK; @@ -17,6 +16,13 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning [BackgroundDependencyLoader] private void load() { + var playfield = new TaikoPlayfield(); + + var beatmap = CreateWorkingBeatmap(new TaikoRuleset().RulesetInfo).GetPlayableBeatmap(new TaikoRuleset().RulesetInfo); + + foreach (var h in beatmap.HitObjects) + playfield.Add(h); + SetContents(_ => new TaikoInputManager(new TaikoRuleset().RulesetInfo) { RelativeSizeAxes = Axes.Both, @@ -25,7 +31,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(200), - Child = new InputDrum(new ControlPointInfo()) + Child = new InputDrum(playfield.HitObjectContainer) } }); } diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs index f96297a06d..6f2fcd08f1 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning Beatmap.Value.Track.Start(); }); - AddStep("Load playfield", () => SetContents(_ => new TaikoPlayfield(new ControlPointInfo()) + AddStep("Load playfield", () => SetContents(_ => new TaikoPlayfield { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, diff --git a/osu.Game.Rulesets.Taiko/Audio/DrumSampleContainer.cs b/osu.Game.Rulesets.Taiko/Audio/DrumSampleContainer.cs deleted file mode 100644 index e4dc261363..0000000000 --- a/osu.Game.Rulesets.Taiko/Audio/DrumSampleContainer.cs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Audio; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Skinning; - -namespace osu.Game.Rulesets.Taiko.Audio -{ - /// - /// Stores samples for the input drum. - /// The lifetime of the samples is adjusted so that they are only alive during the appropriate sample control point. - /// - public class DrumSampleContainer : LifetimeManagementContainer - { - private readonly ControlPointInfo controlPoints; - private readonly Dictionary mappings = new Dictionary(); - - private readonly IBindableList samplePoints = new BindableList(); - - public DrumSampleContainer(ControlPointInfo controlPoints) - { - this.controlPoints = controlPoints; - } - - [BackgroundDependencyLoader] - private void load() - { - samplePoints.BindTo(controlPoints.SamplePoints); - samplePoints.BindCollectionChanged((_, __) => recreateMappings(), true); - } - - private void recreateMappings() - { - mappings.Clear(); - ClearInternal(); - - SampleControlPoint[] points = samplePoints.Count == 0 - ? new[] { controlPoints.SamplePointAt(double.MinValue) } - : samplePoints.ToArray(); - - for (int i = 0; i < points.Length; i++) - { - var samplePoint = points[i]; - - var lifetimeStart = i > 0 ? samplePoint.Time : double.MinValue; - var lifetimeEnd = i + 1 < points.Length ? points[i + 1].Time : double.MaxValue; - - AddInternal(mappings[samplePoint.Time] = new DrumSample(samplePoint) - { - LifetimeStart = lifetimeStart, - LifetimeEnd = lifetimeEnd - }); - } - } - - public DrumSample SampleAt(double time) => mappings[controlPoints.SamplePointAt(time).Time]; - - public class DrumSample : CompositeDrawable - { - public override bool RemoveWhenNotAlive => false; - - public PausableSkinnableSound Centre { get; private set; } - public PausableSkinnableSound Rim { get; private set; } - - private readonly SampleControlPoint samplePoint; - - private Bindable sampleBank; - private BindableNumber sampleVolume; - - public DrumSample(SampleControlPoint samplePoint) - { - this.samplePoint = samplePoint; - } - - [BackgroundDependencyLoader] - private void load() - { - sampleBank = samplePoint.SampleBankBindable.GetBoundCopy(); - sampleBank.BindValueChanged(_ => recreate()); - - sampleVolume = samplePoint.SampleVolumeBindable.GetBoundCopy(); - sampleVolume.BindValueChanged(_ => recreate()); - - recreate(); - } - - private void recreate() - { - InternalChildren = new Drawable[] - { - Centre = new PausableSkinnableSound(samplePoint.GetSampleInfo()), - Rim = new PausableSkinnableSound(samplePoint.GetSampleInfo(HitSampleInfo.HIT_CLAP)) - }; - } - } - } -} diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs index 795885d4b9..5a76694913 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs @@ -7,7 +7,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; -using osu.Game.Rulesets.Taiko.Audio; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.UI; using osu.Game.Skinning; using osuTK; @@ -111,7 +112,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy public readonly Sprite Centre; [Resolved] - private DrumSampleContainer sampleContainer { get; set; } + private InputDrum.DrumSampleTriggerSource sampleTriggerSource { get; set; } public LegacyHalfDrum(bool flipped) { @@ -143,17 +144,16 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy public bool OnPressed(TaikoAction action) { Drawable target = null; - var drumSample = sampleContainer.SampleAt(Time.Current); if (action == CentreAction) { target = Centre; - drumSample.Centre?.Play(); + sampleTriggerSource.Play(HitType.Centre); } else if (action == RimAction) { target = Rim; - drumSample.Rim?.Play(); + sampleTriggerSource.Play(HitType.Rim); } if (target != null) diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index 650ce1f5a3..6ddbf3c16b 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Taiko.UI protected override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo); - protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo); + protected override Playfield CreatePlayfield() => new TaikoPlayfield(); public override DrawableHitObject CreateDrawableRepresentation(TaikoHitObject h) => null; diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs index 1ca1be1bdf..24e2dddb49 100644 --- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs +++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs @@ -2,18 +2,19 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osuTK; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Input.Bindings; -using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Audio; using osu.Game.Graphics; -using osu.Game.Rulesets.Taiko.Audio; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; using osu.Game.Skinning; +using osuTK; namespace osu.Game.Rulesets.Taiko.UI { @@ -25,11 +26,11 @@ namespace osu.Game.Rulesets.Taiko.UI private const float middle_split = 0.025f; [Cached] - private DrumSampleContainer sampleContainer; + private DrumSampleTriggerSource sampleTriggerSource; - public InputDrum(ControlPointInfo controlPoints) + public InputDrum(HitObjectContainer hitObjectContainer) { - sampleContainer = new DrumSampleContainer(controlPoints); + sampleTriggerSource = new DrumSampleTriggerSource(hitObjectContainer); RelativeSizeAxes = Axes.Both; } @@ -70,7 +71,7 @@ namespace osu.Game.Rulesets.Taiko.UI } } }), - sampleContainer + sampleTriggerSource }; } @@ -95,7 +96,7 @@ namespace osu.Game.Rulesets.Taiko.UI private readonly Sprite centreHit; [Resolved] - private DrumSampleContainer sampleContainer { get; set; } + private DrumSampleTriggerSource sampleTriggerSource { get; set; } public TaikoHalfDrum(bool flipped) { @@ -156,21 +157,19 @@ namespace osu.Game.Rulesets.Taiko.UI Drawable target = null; Drawable back = null; - var drumSample = sampleContainer.SampleAt(Time.Current); - if (action == CentreAction) { target = centreHit; back = centre; - drumSample.Centre?.Play(); + sampleTriggerSource.Play(HitType.Centre); } else if (action == RimAction) { target = rimHit; back = rim; - drumSample.Rim?.Play(); + sampleTriggerSource.Play(HitType.Rim); } if (target != null) @@ -201,5 +200,25 @@ namespace osu.Game.Rulesets.Taiko.UI { } } + + public class DrumSampleTriggerSource : GameplaySampleTriggerSource + { + public DrumSampleTriggerSource(HitObjectContainer hitObjectContainer) + : base(hitObjectContainer) + { + } + + public void Play(HitType hitType) + { + var hitObject = GetMostValidObject(); + + if (hitObject == null) + return; + + PlaySamples(new ISampleInfo[] { hitObject.SampleControlPoint.GetSampleInfo(hitType == HitType.Rim ? HitSampleInfo.HIT_CLAP : HitSampleInfo.HIT_NORMAL) }); + } + + public override void Play() => throw new InvalidOperationException(@"Use override with HitType parameter instead"); + } } } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 0d9e08b8b7..d650cab729 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -8,7 +8,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Pooling; -using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Judgements; @@ -27,8 +26,6 @@ namespace osu.Game.Rulesets.Taiko.UI { public class TaikoPlayfield : ScrollingPlayfield { - private readonly ControlPointInfo controlPoints; - /// /// Default height of a when inside a . /// @@ -56,11 +53,6 @@ namespace osu.Game.Rulesets.Taiko.UI private Container hitTargetOffsetContent; - public TaikoPlayfield(ControlPointInfo controlPoints) - { - this.controlPoints = controlPoints; - } - [BackgroundDependencyLoader] private void load(OsuColour colours) { @@ -131,7 +123,7 @@ namespace osu.Game.Rulesets.Taiko.UI Children = new Drawable[] { new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.PlayfieldBackgroundLeft), _ => new PlayfieldBackgroundLeft()), - new InputDrum(controlPoints) + new InputDrum(HitObjectContainer) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, From ef2b5e1c51d0fbcc4caece8588590375104a9b90 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 Aug 2021 15:29:47 +0900 Subject: [PATCH 1491/2442] Tidy up variable names and unused resolved properties --- .../UI/GameplaySampleTriggerSource.cs | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs b/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs index 015c85beb9..48905e7232 100644 --- a/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs +++ b/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs @@ -2,8 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Audio; using osu.Game.Rulesets.Objects; @@ -27,20 +25,14 @@ namespace osu.Game.Rulesets.UI private readonly Container hitSounds; - [Resolved] - private DrawableRuleset drawableRuleset { get; set; } - public GameplaySampleTriggerSource(HitObjectContainer hitObjectContainer) { this.hitObjectContainer = hitObjectContainer; - InternalChildren = new Drawable[] + + InternalChild = hitSounds = new Container { - hitSounds = new Container - { - Name = "concurrent sample pool", - RelativeSizeAxes = Axes.Both, - Children = Enumerable.Range(0, max_concurrent_hitsounds).Select(_ => new SkinnableSound()).ToArray() - }, + Name = "concurrent sample pool", + ChildrenEnumerable = Enumerable.Range(0, max_concurrent_hitsounds).Select(_ => new SkinnableSound()) }; } @@ -74,10 +66,10 @@ namespace osu.Game.Rulesets.UI protected HitObject GetMostValidObject() { // The most optimal lookup case we have is when an object is alive. There are usually very few alive objects so there's no drawbacks in attempting this lookup each time. - var nextObject = hitObjectContainer.AliveObjects.FirstOrDefault(h => h.HitObject.StartTime > Time.Current)?.HitObject; + var hitObject = hitObjectContainer.AliveObjects.FirstOrDefault(h => h.HitObject.StartTime > Time.Current)?.HitObject; // In the case a next object isn't available in drawable form, we need to do a somewhat expensive traversal to get a valid sound to play. - if (nextObject == null) + if (hitObject == null) { // This lookup can be skipped if the last entry is still valid (in the future and not yet hit). if (fallbackObject == null || fallbackObject.HitObject.StartTime < Time.Current || fallbackObject.Result?.IsHit == true) @@ -97,15 +89,15 @@ namespace osu.Game.Rulesets.UI fallbackObject ??= hitObjectContainer.Entries.FirstOrDefault(); } - nextObject = fallbackObject?.HitObject; + hitObject = fallbackObject?.HitObject; } - return nextObject; + return hitObject; } private SkinnableSound getNextSample() { - var hitSound = hitSounds[nextHitSoundIndex]; + SkinnableSound hitSound = hitSounds[nextHitSoundIndex]; // round robin over available samples to allow for concurrent playback. nextHitSoundIndex = (nextHitSoundIndex + 1) % max_concurrent_hitsounds; From fc85ae0e349b7f01b4087cdef76250ac160873d8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 Aug 2021 16:55:03 +0900 Subject: [PATCH 1492/2442] Add test coverage --- .../TestSceneGameplaySampleTriggerSource.cs | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs new file mode 100644 index 0000000000..c446a7efd1 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs @@ -0,0 +1,135 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.UI; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneGameplaySampleTriggerSource : PlayerTestScene + { + private TestGameplaySampleTriggerSource sampleTriggerSource; + protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); + + private Beatmap beatmap; + + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) + { + beatmap = new Beatmap + { + BeatmapInfo = new BeatmapInfo + { + BaseDifficulty = new BeatmapDifficulty { CircleSize = 6, SliderMultiplier = 3 }, + Ruleset = ruleset + } + }; + + const double start_offset = 8000; + const double spacing = 2000; + + double t = start_offset; + beatmap.HitObjects.AddRange(new[] + { + new HitCircle + { + // intentionally start objects a bit late so we can test the case of no alive objects. + StartTime = t += spacing, + Samples = new[] { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) } + }, + new HitCircle + { + StartTime = t += spacing, + Samples = new[] { new HitSampleInfo(HitSampleInfo.HIT_WHISTLE) } + }, + new HitCircle + { + StartTime = t += spacing, + Samples = new[] { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }, + SampleControlPoint = new SampleControlPoint { SampleBank = "soft" }, + }, + new HitCircle + { + StartTime = t += spacing, + Samples = new[] { new HitSampleInfo(HitSampleInfo.HIT_WHISTLE) }, + SampleControlPoint = new SampleControlPoint { SampleBank = "soft" }, + }, + }); + + return beatmap; + } + + public override void SetUpSteps() + { + base.SetUpSteps(); + + AddStep("Add trigger source", () => Player.HUDOverlay.Add(sampleTriggerSource = new TestGameplaySampleTriggerSource(Player.DrawableRuleset.Playfield.HitObjectContainer))); + } + + [Test] + public void TestCorrectHitObject() + { + HitObjectLifetimeEntry nextObjectEntry = null; + + AddUntilStep("no alive objects", () => getNextAliveObject() == null); + + AddAssert("check initially correct object", () => sampleTriggerSource.GetMostValidObject() == beatmap.HitObjects[0]); + + AddUntilStep("get next object", () => + { + var nextDrawableObject = getNextAliveObject(); + + if (nextDrawableObject != null) + { + nextObjectEntry = nextDrawableObject.Entry; + InputManager.MoveMouseTo(nextDrawableObject.ScreenSpaceDrawQuad.Centre); + return true; + } + + return false; + }); + + AddUntilStep("hit first hitobject", () => + { + InputManager.Click(MouseButton.Left); + return nextObjectEntry.Result.HasResult; + }); + + AddAssert("check correct object after hit", () => sampleTriggerSource.GetMostValidObject() == beatmap.HitObjects[1]); + + AddUntilStep("check correct object after miss", () => sampleTriggerSource.GetMostValidObject() == beatmap.HitObjects[2]); + AddUntilStep("check correct object after miss", () => sampleTriggerSource.GetMostValidObject() == beatmap.HitObjects[3]); + + AddUntilStep("no alive objects", () => getNextAliveObject() == null); + AddAssert("check correct object after none alive", () => sampleTriggerSource.GetMostValidObject() == beatmap.HitObjects[3]); + } + + private DrawableHitObject getNextAliveObject() => + Player.DrawableRuleset.Playfield.HitObjectContainer.AliveObjects.FirstOrDefault(); + + [Test] + public void TestSampleTriggering() + { + AddRepeatStep("trigger sample", () => sampleTriggerSource.Play(), 10); + } + + public class TestGameplaySampleTriggerSource : GameplaySampleTriggerSource + { + public TestGameplaySampleTriggerSource(HitObjectContainer hitObjectContainer) + : base(hitObjectContainer) + { + } + + public new HitObject GetMostValidObject() => base.GetMostValidObject(); + } + } +} From ccfff50c6f18c5a9b790e02e08cb9b16c3b6bac8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 Aug 2021 16:55:34 +0900 Subject: [PATCH 1493/2442] Apply fixes in line with issues found during testing I was trying to be too smart with caching, but if the `Play` method was not called often enough it would have a recent reference. Unfortunately this requires a separate query to `Entries`, but is also a special case (no future hitobjects). This also removes the time-based checks (result status alone should be all we care about). --- .../UI/GameplaySampleTriggerSource.cs | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs b/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs index 48905e7232..bceb5996b9 100644 --- a/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs +++ b/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs @@ -66,27 +66,23 @@ namespace osu.Game.Rulesets.UI protected HitObject GetMostValidObject() { // The most optimal lookup case we have is when an object is alive. There are usually very few alive objects so there's no drawbacks in attempting this lookup each time. - var hitObject = hitObjectContainer.AliveObjects.FirstOrDefault(h => h.HitObject.StartTime > Time.Current)?.HitObject; + var hitObject = hitObjectContainer.AliveObjects.FirstOrDefault(h => h.Result?.IsHit != true)?.HitObject; // In the case a next object isn't available in drawable form, we need to do a somewhat expensive traversal to get a valid sound to play. if (hitObject == null) { // This lookup can be skipped if the last entry is still valid (in the future and not yet hit). - if (fallbackObject == null || fallbackObject.HitObject.StartTime < Time.Current || fallbackObject.Result?.IsHit == true) + if (fallbackObject == null || fallbackObject.Result?.HasResult == true) { // We need to use lifetime entries to find the next object (we can't just use `hitObjectContainer.Objects` due to pooling - it may even be empty). // If required, we can make this lookup more efficient by adding support to get next-future-entry in LifetimeEntryManager. - var lookup = hitObjectContainer.Entries - .Where(e => e.Result?.HasResult != true && e.HitObject.StartTime > Time.Current) - .OrderBy(e => e.HitObject.StartTime) - .FirstOrDefault(); + fallbackObject = hitObjectContainer.Entries + .Where(e => e.Result?.HasResult != true) + .OrderBy(e => e.HitObject.StartTime) + .FirstOrDefault(); - // If the lookup failed, use the previously resolved lookup (we still want to play a sound, and it is still likely the most valid result). - if (lookup != null) - fallbackObject = lookup; - - // If we still can't find anything, just play whatever we can to get a sound out. - fallbackObject ??= hitObjectContainer.Entries.FirstOrDefault(); + // In the case there are no unjudged objects, the last hit object should be used instead. + fallbackObject ??= hitObjectContainer.Entries.LastOrDefault(); } hitObject = fallbackObject?.HitObject; From fd78d0440bfca93e6313e546bd91d5f5aec3794d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 Aug 2021 17:00:32 +0900 Subject: [PATCH 1494/2442] Update missed conditional --- osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs b/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs index bceb5996b9..ac2067a913 100644 --- a/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs +++ b/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs @@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.UI protected HitObject GetMostValidObject() { // The most optimal lookup case we have is when an object is alive. There are usually very few alive objects so there's no drawbacks in attempting this lookup each time. - var hitObject = hitObjectContainer.AliveObjects.FirstOrDefault(h => h.Result?.IsHit != true)?.HitObject; + var hitObject = hitObjectContainer.AliveObjects.FirstOrDefault(h => h.Result?.HasResult != true)?.HitObject; // In the case a next object isn't available in drawable form, we need to do a somewhat expensive traversal to get a valid sound to play. if (hitObject == null) From 599145b46aaaf543146307f575ec2e0632b7f3ac Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 25 Aug 2021 11:29:48 +0300 Subject: [PATCH 1495/2442] Stop clocks when removing them from sync manager --- .../OnlinePlay/Multiplayer/Spectate/CatchUpSyncManager.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/CatchUpSyncManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/CatchUpSyncManager.cs index cf0dfbb585..b8f47c16ff 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/CatchUpSyncManager.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/CatchUpSyncManager.cs @@ -61,7 +61,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate playerClocks.Add(clock); } - public void RemovePlayerClock(ISpectatorPlayerClock clock) => playerClocks.Remove(clock); + public void RemovePlayerClock(ISpectatorPlayerClock clock) + { + playerClocks.Remove(clock); + clock.Stop(); + } protected override void Update() { From 196c74fce87048c33e6c3ed1ee099b870438adb3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 25 Aug 2021 11:30:13 +0300 Subject: [PATCH 1496/2442] Gray out and remove player clock when users stop playing --- .../Multiplayer/Spectate/MultiSpectatorScreen.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs index d10917259d..bf7c738882 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs @@ -8,6 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; using osu.Game.Online.Multiplayer; using osu.Game.Online.Spectator; using osu.Game.Screens.Play; @@ -32,6 +33,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate /// public bool AllPlayersLoaded => instances.All(p => p?.PlayerLoaded == true); + [Resolved] + private OsuColour colours { get; set; } + [Resolved] private SpectatorClient spectatorClient { get; set; } @@ -215,6 +219,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate protected override void EndGameplay(int userId) { RemoveUser(userId); + + var instance = instances.Single(i => i.UserId == userId); + + instance.FadeColour(colours.Gray4, 400, Easing.OutQuint); + syncManager.RemovePlayerClock(instance.GameplayClock); leaderboard.RemoveClock(userId); } From 13acdb5f19137d5868ac946885ed29a0e3a9e6f9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 25 Aug 2021 11:30:37 +0300 Subject: [PATCH 1497/2442] Add test coverage --- .../TestSceneMultiSpectatorScreen.cs | 48 ++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index 18e4a6c575..1c198c11aa 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; @@ -19,6 +20,7 @@ using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.PlayerSettings; using osu.Game.Tests.Beatmaps.IO; using osu.Game.Users; +using osuTK.Graphics; namespace osu.Game.Tests.Visual.Multiplayer { @@ -314,6 +316,25 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("player 2 playing from correct point in time", () => getPlayer(PLAYER_2_ID).ChildrenOfType().Single().FrameStableClock.CurrentTime > 30000); } + [Test] + public void TestPlayersLeaveWhileSpectating() + { + start(Enumerable.Range(PLAYER_1_ID, 8).ToArray()); + sendFrames(Enumerable.Range(PLAYER_1_ID, 8).ToArray(), 300); + + loadSpectateScreen(); + + for (int i = 7; i >= 0; i--) + { + var id = PLAYER_1_ID + i; + + end(new[] { id }); + AddUntilStep("player area grayed", () => getInstance(id).Colour != Color4.White); + AddUntilStep("score quit set", () => getLeaderboardScore(id).HasQuit.Value); + sendFrames(Enumerable.Range(PLAYER_1_ID, i).ToArray(), 300); + } + } + private void loadSpectateScreen(bool waitForPlayerLoad = true) { AddStep("load screen", () => @@ -333,10 +354,31 @@ namespace osu.Game.Tests.Visual.Multiplayer { foreach (int id in userIds) { - OnlinePlayDependencies.Client.AddUser(new User { Id = id }, true); + var user = new MultiplayerRoomUser(id) + { + User = new User { Id = id }, + }; + OnlinePlayDependencies.Client.AddUser(user.User, true); SpectatorClient.StartPlay(id, beatmapId ?? importedBeatmapId); - playingUsers.Add(new MultiplayerRoomUser(id)); + + playingUsers.Add(user); + } + }); + } + + private void end(int[] userIds) + { + AddStep("end play", () => + { + foreach (int id in userIds) + { + var user = playingUsers.Single(u => u.UserID == id); + + OnlinePlayDependencies.Client.RemoveUser(user.User.AsNonNull()); + SpectatorClient.EndPlay(id); + + playingUsers.Remove(user); } }); } @@ -374,5 +416,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private Player getPlayer(int userId) => getInstance(userId).ChildrenOfType().Single(); private PlayerArea getInstance(int userId) => spectatorScreen.ChildrenOfType().Single(p => p.UserId == userId); + + private GameplayLeaderboardScore getLeaderboardScore(int userId) => spectatorScreen.ChildrenOfType().Single(s => s.User?.Id == userId); } } From 7e6e2a7e292b5030739c07b342c46d0f68118323 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 Aug 2021 17:39:06 +0900 Subject: [PATCH 1498/2442] Remove unused assignment --- .../Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs index c446a7efd1..3e0a937ffa 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs @@ -59,7 +59,7 @@ namespace osu.Game.Tests.Visual.Gameplay }, new HitCircle { - StartTime = t += spacing, + StartTime = t + spacing, Samples = new[] { new HitSampleInfo(HitSampleInfo.HIT_WHISTLE) }, SampleControlPoint = new SampleControlPoint { SampleBank = "soft" }, }, From c32168c61f2d358ca88173a9f74bdccafbf699ab Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 25 Aug 2021 17:03:33 +0300 Subject: [PATCH 1499/2442] Add failing test case --- .../Editing/TestSceneEditorScreenModes.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 osu.Game.Tests/Visual/Editing/TestSceneEditorScreenModes.cs diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorScreenModes.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorScreenModes.cs new file mode 100644 index 0000000000..98d8a41674 --- /dev/null +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorScreenModes.cs @@ -0,0 +1,29 @@ +// 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.Linq; +using NUnit.Framework; +using osu.Framework.Testing; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; +using osu.Game.Screens.Edit; +using osu.Game.Screens.Edit.Components.Menus; + +namespace osu.Game.Tests.Visual.Editing +{ + public class TestSceneEditorScreenModes : EditorTestScene + { + protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); + + [Test] + public void TestSwitchScreensInstantaneously() + { + AddStep("switch between all screens at once", () => + { + foreach (var screen in Enum.GetValues(typeof(EditorScreenMode)).Cast()) + Editor.ChildrenOfType().Single().Mode.Value = screen; + }); + } + } +} From 3ad0b529fbdeff6297f6a0ba8be73255a32bd895 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 25 Aug 2021 16:58:06 +0300 Subject: [PATCH 1500/2442] Make `EditorScreen` inherit from `VisibilityContainer` rather than unsafe transforms --- osu.Game/Screens/Edit/Editor.cs | 12 +++++------- osu.Game/Screens/Edit/EditorScreen.cs | 15 +++++++++------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index e8d919311b..57c78f3c65 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -605,19 +605,14 @@ namespace osu.Game.Screens.Edit { var lastScreen = currentScreen; - lastScreen? - .ScaleTo(0.98f, 200, Easing.OutQuint) - .FadeOut(200, Easing.OutQuint); + lastScreen?.Hide(); try { if ((currentScreen = screenContainer.SingleOrDefault(s => s.Type == e.NewValue)) != null) { screenContainer.ChangeChildDepth(currentScreen, lastScreen?.Depth + 1 ?? 0); - - currentScreen - .ScaleTo(1, 200, Easing.OutQuint) - .FadeIn(200, Easing.OutQuint); + currentScreen.Show(); return; } @@ -650,7 +645,10 @@ namespace osu.Game.Screens.Edit LoadComponentAsync(currentScreen, newScreen => { if (newScreen == currentScreen) + { screenContainer.Add(newScreen); + newScreen.Show(); + } }); } finally diff --git a/osu.Game/Screens/Edit/EditorScreen.cs b/osu.Game/Screens/Edit/EditorScreen.cs index 7fbb6a8ca0..d7fe5207d0 100644 --- a/osu.Game/Screens/Edit/EditorScreen.cs +++ b/osu.Game/Screens/Edit/EditorScreen.cs @@ -10,7 +10,7 @@ namespace osu.Game.Screens.Edit /// /// TODO: eventually make this inherit Screen and add a local screen stack inside the Editor. /// - public abstract class EditorScreen : Container + public abstract class EditorScreen : VisibilityContainer { [Resolved] protected EditorBeatmap EditorBeatmap { get; private set; } @@ -31,13 +31,16 @@ namespace osu.Game.Screens.Edit InternalChild = content = new Container { RelativeSizeAxes = Axes.Both }; } - protected override void LoadComplete() + protected override void PopIn() { - base.LoadComplete(); + this.ScaleTo(1f, 200, Easing.OutQuint) + .FadeIn(200, Easing.OutQuint); + } - this.FadeTo(0) - .Then() - .FadeTo(1f, 250, Easing.OutQuint); + protected override void PopOut() + { + this.ScaleTo(0.98f, 200, Easing.OutQuint) + .FadeOut(200, Easing.OutQuint); } } } From f9b25a01590ac076ac3dfc98eda9e7dab552d359 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 25 Aug 2021 15:09:57 +0300 Subject: [PATCH 1501/2442] Add test case for switching to each screen in editor test scenes --- osu.Game/Tests/Visual/EditorTestScene.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index a393802309..2644daa3a4 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -3,6 +3,7 @@ using System.Linq; using JetBrains.Annotations; +using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.IO.Stores; @@ -28,6 +29,9 @@ namespace osu.Game.Tests.Visual protected EditorClock EditorClock { get; private set; } + [Resolved] + private SkinManager skins { get; set; } + /// /// Whether any saves performed by the editor should be isolate (and not persist) to the underlying . /// @@ -57,6 +61,12 @@ namespace osu.Game.Tests.Visual AddStep("get clock", () => EditorClock = Editor.ChildrenOfType().Single()); } + [Test] + public void TestLegacySkin() + { + AddStep("set legacy skin", () => skins.CurrentSkinInfo.Value = DefaultLegacySkin.Info); + } + protected virtual void LoadEditor() { LoadScreen(Editor = CreateEditor()); From b4d6495f99a60194a8609cf3d35d048166001b03 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 25 Aug 2021 15:03:20 +0300 Subject: [PATCH 1502/2442] Fix editor skin providing container not providing playable beatmap --- osu.Game/Screens/Edit/EditorSkinProvidingContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/EditorSkinProvidingContainer.cs b/osu.Game/Screens/Edit/EditorSkinProvidingContainer.cs index 27563b5a0f..decfa879a8 100644 --- a/osu.Game/Screens/Edit/EditorSkinProvidingContainer.cs +++ b/osu.Game/Screens/Edit/EditorSkinProvidingContainer.cs @@ -16,7 +16,7 @@ namespace osu.Game.Screens.Edit private readonly EditorBeatmapSkin? beatmapSkin; public EditorSkinProvidingContainer(EditorBeatmap editorBeatmap) - : base(editorBeatmap.PlayableBeatmap.BeatmapInfo.Ruleset.CreateInstance(), editorBeatmap, editorBeatmap.BeatmapSkin) + : base(editorBeatmap.PlayableBeatmap.BeatmapInfo.Ruleset.CreateInstance(), editorBeatmap.PlayableBeatmap, editorBeatmap.BeatmapSkin) { beatmapSkin = editorBeatmap.BeatmapSkin; } From 998abcbf31350ae033c6159904365337196cb0a9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 25 Aug 2021 18:25:31 +0300 Subject: [PATCH 1503/2442] Replace occurences of `Enumerable.Range(PLAYER_1_ID, ...)` with a method --- .../Multiplayer/TestSceneMultiSpectatorScreen.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index 1c198c11aa..97c992e8ec 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -80,7 +80,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestGeneral() { - int[] userIds = Enumerable.Range(0, 4).Select(i => PLAYER_1_ID + i).ToArray(); + int[] userIds = getPlayerIds(4); start(userIds); loadSpectateScreen(); @@ -319,19 +319,19 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestPlayersLeaveWhileSpectating() { - start(Enumerable.Range(PLAYER_1_ID, 8).ToArray()); - sendFrames(Enumerable.Range(PLAYER_1_ID, 8).ToArray(), 300); + start(getPlayerIds(8)); + sendFrames(getPlayerIds(8), 300); loadSpectateScreen(); - for (int i = 7; i >= 0; i--) + for (int count = 7; count >= 0; count--) { - var id = PLAYER_1_ID + i; + var id = PLAYER_1_ID + count; end(new[] { id }); AddUntilStep("player area grayed", () => getInstance(id).Colour != Color4.White); AddUntilStep("score quit set", () => getLeaderboardScore(id).HasQuit.Value); - sendFrames(Enumerable.Range(PLAYER_1_ID, i).ToArray(), 300); + sendFrames(getPlayerIds(count), 300); } } @@ -418,5 +418,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private PlayerArea getInstance(int userId) => spectatorScreen.ChildrenOfType().Single(p => p.UserId == userId); private GameplayLeaderboardScore getLeaderboardScore(int userId) => spectatorScreen.ChildrenOfType().Single(s => s.User?.Id == userId); + + private int[] getPlayerIds(int count) => Enumerable.Range(PLAYER_1_ID, count).ToArray(); } } From 5acaafa7089f28104c53685bf9db9c505d8c3890 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 25 Aug 2021 18:28:23 +0300 Subject: [PATCH 1504/2442] Make `end` accept one user ID rather than unnecessarily an array --- .../TestSceneMultiSpectatorScreen.cs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index 97c992e8ec..c3be5c56ef 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -328,7 +328,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { var id = PLAYER_1_ID + count; - end(new[] { id }); + end(id); AddUntilStep("player area grayed", () => getInstance(id).Colour != Color4.White); AddUntilStep("score quit set", () => getLeaderboardScore(id).HasQuit.Value); sendFrames(getPlayerIds(count), 300); @@ -367,19 +367,16 @@ namespace osu.Game.Tests.Visual.Multiplayer }); } - private void end(int[] userIds) + private void end(int userId) { - AddStep("end play", () => + AddStep($"end play for {userId}", () => { - foreach (int id in userIds) - { - var user = playingUsers.Single(u => u.UserID == id); + var user = playingUsers.Single(u => u.UserID == userId); - OnlinePlayDependencies.Client.RemoveUser(user.User.AsNonNull()); - SpectatorClient.EndPlay(id); + OnlinePlayDependencies.Client.RemoveUser(user.User.AsNonNull()); + SpectatorClient.EndPlay(userId); - playingUsers.Remove(user); - } + playingUsers.Remove(user); }); } From 956e3c554bd420371fa9b7d3242643184fc6efd5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 26 Aug 2021 00:50:55 +0900 Subject: [PATCH 1505/2442] Avoid skip overlay attempting to show when it is already invalid --- osu.Game/Screens/Play/SkipOverlay.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/SkipOverlay.cs b/osu.Game/Screens/Play/SkipOverlay.cs index 4a74fa1d4f..dd6ba3c2f1 100644 --- a/osu.Game/Screens/Play/SkipOverlay.cs +++ b/osu.Game/Screens/Play/SkipOverlay.cs @@ -37,6 +37,8 @@ namespace osu.Game.Screens.Play private FadeContainer fadeContainer; private double displayTime; + private bool isClickable; + [Resolved] private GameplayClock gameplayClock { get; set; } @@ -124,17 +126,18 @@ namespace osu.Game.Screens.Play { base.Update(); - var progress = fadeOutBeginTime <= displayTime ? 1 : Math.Max(0, 1 - (gameplayClock.CurrentTime - displayTime) / (fadeOutBeginTime - displayTime)); + double progress = fadeOutBeginTime <= displayTime ? 1 : Math.Max(0, 1 - (gameplayClock.CurrentTime - displayTime) / (fadeOutBeginTime - displayTime)); remainingTimeBox.Width = (float)Interpolation.Lerp(remainingTimeBox.Width, progress, Math.Clamp(Time.Elapsed / 40, 0, 1)); - button.Enabled.Value = progress > 0; - buttonContainer.State.Value = progress > 0 ? Visibility.Visible : Visibility.Hidden; + isClickable = progress > 0; + button.Enabled.Value = isClickable; + buttonContainer.State.Value = isClickable ? Visibility.Visible : Visibility.Hidden; } protected override bool OnMouseMove(MouseMoveEvent e) { - if (!e.HasAnyButtonPressed) + if (isClickable && !e.HasAnyButtonPressed) fadeContainer.Show(); return base.OnMouseMove(e); From b1a261c9029e1b6e5cf0b92da39a5fc3d64c4213 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 26 Aug 2021 01:02:27 +0900 Subject: [PATCH 1506/2442] Avoid using scheduled delegates at all for skip overload input handling --- osu.Game/Screens/Play/SkipOverlay.cs | 46 ++++++++++++++++++---------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/osu.Game/Screens/Play/SkipOverlay.cs b/osu.Game/Screens/Play/SkipOverlay.cs index dd6ba3c2f1..71a70991d2 100644 --- a/osu.Game/Screens/Play/SkipOverlay.cs +++ b/osu.Game/Screens/Play/SkipOverlay.cs @@ -12,7 +12,6 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; -using osu.Framework.Threading; using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -120,6 +119,8 @@ namespace osu.Game.Screens.Play button.Action = () => RequestSkip?.Invoke(); displayTime = gameplayClock.CurrentTime; + + fadeContainer.TriggerShow(); } protected override void Update() @@ -138,7 +139,7 @@ namespace osu.Game.Screens.Play protected override bool OnMouseMove(MouseMoveEvent e) { if (isClickable && !e.HasAnyButtonPressed) - fadeContainer.Show(); + fadeContainer.TriggerShow(); return base.OnMouseMove(e); } @@ -167,34 +168,45 @@ namespace osu.Game.Screens.Play public event Action StateChanged; private Visibility state; - private ScheduledDelegate scheduledHide; + private double? nextHideTime; public override bool IsPresent => true; + public void TriggerShow() + { + Show(); + + if (!IsHovered && !IsDragged) + nextHideTime = Time.Current + 1000; + else + nextHideTime = null; + } + + protected override void Update() + { + base.Update(); + + if (nextHideTime != null && nextHideTime <= Time.Current) + { + Hide(); + nextHideTime = null; + } + } + public Visibility State { get => state; set { - bool stateChanged = value != state; + if (value == state) + return; state = value; - scheduledHide?.Cancel(); - switch (state) { case Visibility.Visible: - // we may be triggered to become visible multiple times but we only want to transform once. - if (stateChanged) - this.FadeIn(500, Easing.OutExpo); - - if (!IsHovered && !IsDragged) - { - using (BeginDelayedSequence(1000)) - scheduledHide = Schedule(Hide); - } - + this.FadeIn(500, Easing.OutExpo); break; case Visibility.Hidden: @@ -215,7 +227,7 @@ namespace osu.Game.Screens.Play protected override bool OnMouseDown(MouseDownEvent e) { Show(); - scheduledHide?.Cancel(); + nextHideTime = null; return true; } From 6dcd9427ac809999a5357f075a00fa41cb40c141 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 26 Aug 2021 01:42:57 +0900 Subject: [PATCH 1507/2442] Remove bindable usage in `PathControlPoint` This is quite a breaking change, but I think it is beneficial due to the large amount of usage of this class. I originally intended just to remove the allocations of the two delegates handling the `Changed` flow internally, but as nothing was really using the bindables for anything more than a general "point has changed" case, this felt like a better direction. --- .../JuiceStreamPathTest.cs | 8 +- .../Beatmaps/CatchBeatmapProcessor.cs | 2 +- .../Blueprints/Components/EditablePath.cs | 2 +- .../Edit/CatchSelectionHandler.cs | 2 +- .../Mods/CatchModMirror.cs | 4 +- .../Objects/JuiceStream.cs | 2 +- .../Objects/JuiceStreamPath.cs | 2 +- .../TestScenePathControlPointVisualiser.cs | 4 +- .../TestSceneSliderPlacementBlueprint.cs | 4 +- .../TestSceneSliderSelectionBlueprint.cs | 2 +- .../PathControlPointConnectionPiece.cs | 4 +- .../Components/PathControlPointPiece.cs | 34 ++-- .../Components/PathControlPointVisualiser.cs | 4 +- .../Sliders/SliderPlacementBlueprint.cs | 12 +- .../Sliders/SliderSelectionBlueprint.cs | 14 +- .../Edit/OsuSelectionHandler.cs | 20 +-- osu.Game.Rulesets.Osu/Objects/Slider.cs | 2 +- .../Utils/OsuHitObjectGenerationUtils.cs | 8 +- .../Formats/LegacyBeatmapDecoderTest.cs | 152 +++++++++--------- .../Visual/Gameplay/TestSceneSliderPath.cs | 18 +-- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 16 +- .../Objects/Legacy/ConvertHitObjectParser.cs | 12 +- osu.Game/Rulesets/Objects/PathControlPoint.cs | 41 +++-- osu.Game/Rulesets/Objects/SliderPath.cs | 8 +- .../Rulesets/Objects/SliderPathExtensions.cs | 14 +- 25 files changed, 203 insertions(+), 188 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs b/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs index 5e4b6d9e1a..8fa96fb8c9 100644 --- a/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs @@ -154,7 +154,7 @@ namespace osu.Game.Rulesets.Catch.Tests } while (rng.Next(2) != 0); int length = sliderPath.ControlPoints.Count - start + 1; - sliderPath.ControlPoints[start].Type.Value = length <= 2 ? PathType.Linear : length == 3 ? PathType.PerfectCurve : PathType.Bezier; + sliderPath.ControlPoints[start].Type = length <= 2 ? PathType.Linear : length == 3 ? PathType.PerfectCurve : PathType.Bezier; } while (rng.Next(3) != 0); if (rng.Next(5) == 0) @@ -210,13 +210,13 @@ namespace osu.Game.Rulesets.Catch.Tests path.ConvertToSliderPath(sliderPath, sliderStartY); Assert.That(sliderPath.Distance, Is.EqualTo(path.Distance).Within(1e-3)); - Assert.That(sliderPath.ControlPoints[0].Position.Value.X, Is.EqualTo(path.Vertices[0].X)); + Assert.That(sliderPath.ControlPoints[0].Position.X, Is.EqualTo(path.Vertices[0].X)); assertInvariants(path.Vertices, true); foreach (var point in sliderPath.ControlPoints) { - Assert.That(point.Type.Value, Is.EqualTo(PathType.Linear).Or.Null); - Assert.That(sliderStartY + point.Position.Value.Y, Is.InRange(0, JuiceStreamPath.OSU_PLAYFIELD_HEIGHT)); + Assert.That(point.Type, Is.EqualTo(PathType.Linear).Or.Null); + Assert.That(sliderStartY + point.Position.Y, Is.InRange(0, JuiceStreamPath.OSU_PLAYFIELD_HEIGHT)); } for (int i = 0; i < 10; i++) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 3a5322ce82..a891ec6c0a 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -75,7 +75,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps case JuiceStream juiceStream: // Todo: BUG!! Stable used the last control point as the final position of the path, but it should use the computed path instead. - lastPosition = juiceStream.OriginalX + juiceStream.Path.ControlPoints[^1].Position.Value.X; + lastPosition = juiceStream.OriginalX + juiceStream.Path.ControlPoints[^1].Position.X; // Todo: BUG!! Stable attempted to use the end time of the stream, but referenced it too early in execution and used the start time instead. lastStartTime = juiceStream.StartTime; diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs index 8aaeef045f..1a43a10c81 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs @@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components path.ConvertFromSliderPath(sliderPath); // If the original slider path has non-linear type segments, resample the vertices at nested hit object times to reduce the number of vertices. - if (sliderPath.ControlPoints.Any(p => p.Type.Value != null && p.Type.Value != PathType.Linear)) + if (sliderPath.ControlPoints.Any(p => p.Type != null && p.Type != PathType.Linear)) { path.ResampleVertices(hitObject.NestedHitObjects .Skip(1).TakeWhile(h => !(h is Fruit)) // Only droplets in the first span are used. diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index 36072d7fcb..8cb0804ab7 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -127,7 +127,7 @@ namespace osu.Game.Rulesets.Catch.Edit juiceStream.OriginalX = selectionRange.GetFlippedPosition(juiceStream.OriginalX); foreach (var point in juiceStream.Path.ControlPoints) - point.Position.Value *= new Vector2(-1, 1); + point.Position *= new Vector2(-1, 1); EditorBeatmap.Update(juiceStream); return true; diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs index 932c8cad85..a97e940a64 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs @@ -68,9 +68,9 @@ namespace osu.Game.Rulesets.Catch.Mods /// private static void mirrorJuiceStreamPath(JuiceStream juiceStream) { - var controlPoints = juiceStream.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); + var controlPoints = juiceStream.Path.ControlPoints.Select(p => new PathControlPoint(p.Position, p.Type)).ToArray(); foreach (var point in controlPoints) - point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y); + point.Position = new Vector2(-point.Position.X, point.Position.Y); juiceStream.Path = new SliderPath(controlPoints, juiceStream.Path.ExpectedDistance.Value); } diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index 3088d024d1..a8ad34fcbe 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Catch.Objects if (value != null) { - path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position.Value, c.Type.Value))); + path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position, c.Type))); path.ExpectedDistance.Value = value.ExpectedDistance.Value; } } diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs index f1cdb39e91..7207833fe6 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs @@ -234,7 +234,7 @@ namespace osu.Game.Rulesets.Catch.Objects for (int i = 1; i < vertices.Count; i++) { - sliderPath.ControlPoints[^1].Type.Value = PathType.Linear; + sliderPath.ControlPoints[^1].Type = PathType.Linear; float deltaX = vertices[i].X - lastPosition.X; double length = vertices[i].Distance - currentDistance; diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs index 35b79aa8ac..53a9f06eee 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddAssert("last connection displayed", () => { - var lastConnection = visualiser.Connections.Last(c => c.ControlPoint.Position.Value == new Vector2(300)); + var lastConnection = visualiser.Connections.Last(c => c.ControlPoint.Position == new Vector2(300)); return lastConnection.DrawWidth > 50; }); } @@ -173,7 +173,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor private void assertControlPointPathType(int controlPointIndex, PathType? type) { - AddAssert($"point {controlPointIndex} is {type}", () => slider.Path.ControlPoints[controlPointIndex].Type.Value == type); + AddAssert($"point {controlPointIndex} is {type}", () => slider.Path.ControlPoints[controlPointIndex].Type == type); } private void addContextMenuItemStep(string contextMenuText) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs index 8235e1bc79..e724015905 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs @@ -385,10 +385,10 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor private void assertControlPointCount(int expected) => AddAssert($"has {expected} control points", () => getSlider().Path.ControlPoints.Count == expected); - private void assertControlPointType(int index, PathType type) => AddAssert($"control point {index} is {type}", () => getSlider().Path.ControlPoints[index].Type.Value == type); + private void assertControlPointType(int index, PathType type) => AddAssert($"control point {index} is {type}", () => getSlider().Path.ControlPoints[index].Type == type); private void assertControlPointPosition(int index, Vector2 position) => - AddAssert($"control point {index} at {position}", () => Precision.AlmostEquals(position, getSlider().Path.ControlPoints[index].Position.Value, 1)); + AddAssert($"control point {index} at {position}", () => Precision.AlmostEquals(position, getSlider().Path.ControlPoints[index].Position, 1)); private Slider getSlider() => HitObjectContainer.Count > 0 ? ((DrawableSlider)HitObjectContainer[0]).HitObject : null; diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSelectionBlueprint.cs index 0d828a79c8..cc43eb3852 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSelectionBlueprint.cs @@ -184,7 +184,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { AddStep($"move mouse to control point {index}", () => { - Vector2 position = slider.Position + slider.Path.ControlPoints[index].Position.Value; + Vector2 position = slider.Position + slider.Path.ControlPoints[index].Position; InputManager.MoveMouseTo(drawableObject.Parent.ToScreenSpace(position)); }); } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs index eb7011e8b0..d66c9ea4bf 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components /// private void updateConnectingPath() { - Position = slider.StackedPosition + ControlPoint.Position.Value; + Position = slider.StackedPosition + ControlPoint.Position; path.ClearVertices(); @@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components return; path.AddVertex(Vector2.Zero); - path.AddVertex(slider.Path.ControlPoints[nextIndex].Position.Value - ControlPoint.Position.Value); + path.AddVertex(slider.Path.ControlPoints[nextIndex].Position - ControlPoint.Position); path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero); } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 5b476526c9..2cc95e1891 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -53,7 +53,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private IBindable sliderPosition; private IBindable sliderScale; - private IBindable controlPointPosition; public PathControlPointPiece(Slider slider, PathControlPoint controlPoint) { @@ -69,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components updatePathType(); }); - controlPoint.Type.BindValueChanged(_ => updateMarkerDisplay()); + controlPoint.Changed += updateMarkerDisplay; Origin = Anchor.Centre; AutoSizeAxes = Axes.Both; @@ -117,9 +116,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components sliderPosition = slider.PositionBindable.GetBoundCopy(); sliderPosition.BindValueChanged(_ => updateMarkerDisplay()); - controlPointPosition = ControlPoint.Position.GetBoundCopy(); - controlPointPosition.BindValueChanged(_ => updateMarkerDisplay()); - sliderScale = slider.ScaleBindable.GetBoundCopy(); sliderScale.BindValueChanged(_ => updateMarkerDisplay()); @@ -174,8 +170,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components if (e.Button == MouseButton.Left) { - dragStartPosition = ControlPoint.Position.Value; - dragPathType = PointsInSegment[0].Type.Value; + dragStartPosition = ControlPoint.Position; + dragPathType = PointsInSegment[0].Type; changeHandler?.BeginChange(); return true; @@ -186,7 +182,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components protected override void OnDrag(DragEvent e) { - Vector2[] oldControlPoints = slider.Path.ControlPoints.Select(cp => cp.Position.Value).ToArray(); + Vector2[] oldControlPoints = slider.Path.ControlPoints.Select(cp => cp.Position).ToArray(); var oldPosition = slider.Position; var oldStartTime = slider.StartTime; @@ -202,15 +198,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components // Since control points are relative to the position of the slider, they all need to be offset backwards by the delta for (int i = 1; i < slider.Path.ControlPoints.Count; i++) - slider.Path.ControlPoints[i].Position.Value -= movementDelta; + slider.Path.ControlPoints[i].Position -= movementDelta; } else - ControlPoint.Position.Value = dragStartPosition + (e.MousePosition - e.MouseDownPosition); + ControlPoint.Position = dragStartPosition + (e.MousePosition - e.MouseDownPosition); if (!slider.Path.HasValidLength) { for (var i = 0; i < slider.Path.ControlPoints.Count; i++) - slider.Path.ControlPoints[i].Position.Value = oldControlPoints[i]; + slider.Path.ControlPoints[i].Position = oldControlPoints[i]; slider.Position = oldPosition; slider.StartTime = oldStartTime; @@ -218,7 +214,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components } // Maintain the path type in case it got defaulted to bezier at some point during the drag. - PointsInSegment[0].Type.Value = dragPathType; + PointsInSegment[0].Type = dragPathType; } protected override void OnDragEnd(DragEndEvent e) => changeHandler?.EndChange(); @@ -230,19 +226,19 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components /// private void updatePathType() { - if (ControlPoint.Type.Value != PathType.PerfectCurve) + if (ControlPoint.Type != PathType.PerfectCurve) return; if (PointsInSegment.Count > 3) - ControlPoint.Type.Value = PathType.Bezier; + ControlPoint.Type = PathType.Bezier; if (PointsInSegment.Count != 3) return; - ReadOnlySpan points = PointsInSegment.Select(p => p.Position.Value).ToArray(); + ReadOnlySpan points = PointsInSegment.Select(p => p.Position).ToArray(); RectangleF boundingBox = PathApproximator.CircularArcBoundingBox(points); if (boundingBox.Width >= 640 || boundingBox.Height >= 480) - ControlPoint.Type.Value = PathType.Bezier; + ControlPoint.Type = PathType.Bezier; } /// @@ -250,7 +246,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components /// private void updateMarkerDisplay() { - Position = slider.StackedPosition + ControlPoint.Position.Value; + Position = slider.StackedPosition + ControlPoint.Position; markerRing.Alpha = IsSelected.Value ? 1 : 0; @@ -265,7 +261,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private Color4 getColourFromNodeType() { - if (!(ControlPoint.Type.Value is PathType pathType)) + if (!(ControlPoint.Type is PathType pathType)) return colours.Yellow; switch (pathType) @@ -284,6 +280,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components } } - public LocalisableString TooltipText => ControlPoint.Type.Value.ToString() ?? string.Empty; + public LocalisableString TooltipText => ControlPoint.Type.ToString() ?? string.Empty; } } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 5bbdf9688f..ac1953c632 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -173,12 +173,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components int thirdPointIndex = indexInSegment + 2; if (piece.PointsInSegment.Count > thirdPointIndex + 1) - piece.PointsInSegment[thirdPointIndex].Type.Value = piece.PointsInSegment[0].Type.Value; + piece.PointsInSegment[thirdPointIndex].Type = piece.PointsInSegment[0].Type; break; } - piece.ControlPoint.Type.Value = type; + piece.ControlPoint.Type = type; } [Resolved(CanBeNull = true)] diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 8b20df9a68..b9e4ed6fcb 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -108,7 +108,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders Debug.Assert(lastPoint != null); segmentStart = lastPoint; - segmentStart.Type.Value = PathType.Linear; + segmentStart.Type = PathType.Linear; currentSegmentLength = 1; } @@ -153,15 +153,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { case 1: case 2: - segmentStart.Type.Value = PathType.Linear; + segmentStart.Type = PathType.Linear; break; case 3: - segmentStart.Type.Value = PathType.PerfectCurve; + segmentStart.Type = PathType.PerfectCurve; break; default: - segmentStart.Type.Value = PathType.Bezier; + segmentStart.Type = PathType.Bezier; break; } } @@ -173,7 +173,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders // The cursor does not overlap a previous control point, so it can be added if not already existing. if (cursor == null) { - HitObject.Path.ControlPoints.Add(cursor = new PathControlPoint { Position = { Value = Vector2.Zero } }); + HitObject.Path.ControlPoints.Add(cursor = new PathControlPoint { Position = Vector2.Zero }); // The path type should be adjusted in the progression of updatePathType() (Linear -> PC -> Bezier). currentSegmentLength++; @@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders } // Update the cursor position. - cursor.Position.Value = ToLocalSpace(inputManager.CurrentState.Mouse.Position) - HitObject.Position; + cursor.Position = ToLocalSpace(inputManager.CurrentState.Mouse.Position) - HitObject.Position; } else if (cursor != null) { diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index e810d2fe0c..89724876fa 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -161,7 +161,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { Debug.Assert(placementControlPointIndex != null); - HitObject.Path.ControlPoints[placementControlPointIndex.Value].Position.Value = e.MousePosition - HitObject.Position; + HitObject.Path.ControlPoints[placementControlPointIndex.Value].Position = e.MousePosition - HitObject.Position; } protected override void OnDragEnd(DragEndEvent e) @@ -182,7 +182,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders for (int i = 0; i < controlPoints.Count - 1; i++) { - float dist = new Line(controlPoints[i].Position.Value, controlPoints[i + 1].Position.Value).DistanceToPoint(position); + float dist = new Line(controlPoints[i].Position, controlPoints[i + 1].Position).DistanceToPoint(position); if (dist < minDistance) { @@ -192,7 +192,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders } // Move the control points from the insertion index onwards to make room for the insertion - controlPoints.Insert(insertionIndex, new PathControlPoint { Position = { Value = position } }); + controlPoints.Insert(insertionIndex, new PathControlPoint { Position = position }); return insertionIndex; } @@ -207,8 +207,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { // The first control point in the slider must have a type, so take it from the previous "first" one // Todo: Should be handled within SliderPath itself - if (c == controlPoints[0] && controlPoints.Count > 1 && controlPoints[1].Type.Value == null) - controlPoints[1].Type.Value = controlPoints[0].Type.Value; + if (c == controlPoints[0] && controlPoints.Count > 1 && controlPoints[1].Type == null) + controlPoints[1].Type = controlPoints[0].Type; controlPoints.Remove(c); } @@ -222,9 +222,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders // The path will have a non-zero offset if the head is removed, but sliders don't support this behaviour since the head is positioned at the slider's position // So the slider needs to be offset by this amount instead, and all control points offset backwards such that the path is re-positioned at (0, 0) - Vector2 first = controlPoints[0].Position.Value; + Vector2 first = controlPoints[0].Position; foreach (var c in controlPoints) - c.Position.Value -= first; + c.Position -= first; HitObject.Position += first; } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 358a44e0e6..4a57d36eb4 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -98,9 +98,9 @@ namespace osu.Game.Rulesets.Osu.Edit { foreach (var point in slider.Path.ControlPoints) { - point.Position.Value = new Vector2( - (direction == Direction.Horizontal ? -1 : 1) * point.Position.Value.X, - (direction == Direction.Vertical ? -1 : 1) * point.Position.Value.Y + point.Position = new Vector2( + (direction == Direction.Horizontal ? -1 : 1) * point.Position.X, + (direction == Direction.Vertical ? -1 : 1) * point.Position.Y ); } } @@ -153,7 +153,7 @@ namespace osu.Game.Rulesets.Osu.Edit if (h is IHasPath path) { foreach (var point in path.Path.ControlPoints) - point.Position.Value = RotatePointAroundOrigin(point.Position.Value, Vector2.Zero, delta); + point.Position = RotatePointAroundOrigin(point.Position, Vector2.Zero, delta); } } @@ -163,9 +163,9 @@ namespace osu.Game.Rulesets.Osu.Edit private void scaleSlider(Slider slider, Vector2 scale) { - referencePathTypes ??= slider.Path.ControlPoints.Select(p => p.Type.Value).ToList(); + referencePathTypes ??= slider.Path.ControlPoints.Select(p => p.Type).ToList(); - Quad sliderQuad = GetSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position.Value)); + Quad sliderQuad = GetSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position)); // Limit minimum distance between control points after scaling to almost 0. Less than 0 causes the slider to flip, exactly 0 causes a crash through division by 0. scale = Vector2.ComponentMax(new Vector2(Precision.FLOAT_EPSILON), sliderQuad.Size + scale) - sliderQuad.Size; @@ -178,13 +178,13 @@ namespace osu.Game.Rulesets.Osu.Edit foreach (var point in slider.Path.ControlPoints) { - oldControlPoints.Enqueue(point.Position.Value); - point.Position.Value *= pathRelativeDeltaScale; + oldControlPoints.Enqueue(point.Position); + point.Position *= pathRelativeDeltaScale; } // Maintain the path types in case they were defaulted to bezier at some point during scaling for (int i = 0; i < slider.Path.ControlPoints.Count; ++i) - slider.Path.ControlPoints[i].Type.Value = referencePathTypes[i]; + slider.Path.ControlPoints[i].Type = referencePathTypes[i]; //if sliderhead or sliderend end up outside playfield, revert scaling. Quad scaledQuad = getSurroundingQuad(new OsuHitObject[] { slider }); @@ -194,7 +194,7 @@ namespace osu.Game.Rulesets.Osu.Edit return; foreach (var point in slider.Path.ControlPoints) - point.Position.Value = oldControlPoints.Dequeue(); + point.Position = oldControlPoints.Dequeue(); } private void scaleHitObjects(OsuHitObject[] hitObjects, Anchor reference, Vector2 scale) diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 8ba9597dc3..c4420b1e87 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Objects if (value != null) { - path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position.Value, c.Type.Value))); + path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position, c.Type))); path.ExpectedDistance.Value = value.ExpectedDistance.Value; } } diff --git a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs index 57ec51cf64..bfd6ac3ad3 100644 --- a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs +++ b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs @@ -119,9 +119,9 @@ namespace osu.Game.Rulesets.Osu.Utils slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y)); slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y)); - var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); + var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position, p.Type)).ToArray(); foreach (var point in controlPoints) - point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y); + point.Position = new Vector2(-point.Position.X, point.Position.Y); slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value); } @@ -140,9 +140,9 @@ namespace osu.Game.Rulesets.Osu.Utils slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); - var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); + var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position, p.Type)).ToArray(); foreach (var point in controlPoints) - point.Position.Value = new Vector2(point.Position.Value.X, -point.Position.Value.Y); + point.Position = new Vector2(point.Position.X, -point.Position.Y); slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value); } diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index dd4c24698c..12633ee8c9 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -666,111 +666,111 @@ namespace osu.Game.Tests.Beatmaps.Formats // Multi-segment var first = ((IHasPath)decoded.HitObjects[0]).Path; - Assert.That(first.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero)); - Assert.That(first.ControlPoints[0].Type.Value, Is.EqualTo(PathType.PerfectCurve)); - Assert.That(first.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(161, -244))); - Assert.That(first.ControlPoints[1].Type.Value, Is.EqualTo(null)); + Assert.That(first.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); + Assert.That(first.ControlPoints[0].Type, Is.EqualTo(PathType.PerfectCurve)); + Assert.That(first.ControlPoints[1].Position, Is.EqualTo(new Vector2(161, -244))); + Assert.That(first.ControlPoints[1].Type, Is.EqualTo(null)); - Assert.That(first.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(376, -3))); - Assert.That(first.ControlPoints[2].Type.Value, Is.EqualTo(PathType.Bezier)); - Assert.That(first.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(68, 15))); - Assert.That(first.ControlPoints[3].Type.Value, Is.EqualTo(null)); - Assert.That(first.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(259, -132))); - Assert.That(first.ControlPoints[4].Type.Value, Is.EqualTo(null)); - Assert.That(first.ControlPoints[5].Position.Value, Is.EqualTo(new Vector2(92, -107))); - Assert.That(first.ControlPoints[5].Type.Value, Is.EqualTo(null)); + Assert.That(first.ControlPoints[2].Position, Is.EqualTo(new Vector2(376, -3))); + Assert.That(first.ControlPoints[2].Type, Is.EqualTo(PathType.Bezier)); + Assert.That(first.ControlPoints[3].Position, Is.EqualTo(new Vector2(68, 15))); + Assert.That(first.ControlPoints[3].Type, Is.EqualTo(null)); + Assert.That(first.ControlPoints[4].Position, Is.EqualTo(new Vector2(259, -132))); + Assert.That(first.ControlPoints[4].Type, Is.EqualTo(null)); + Assert.That(first.ControlPoints[5].Position, Is.EqualTo(new Vector2(92, -107))); + Assert.That(first.ControlPoints[5].Type, Is.EqualTo(null)); // Single-segment var second = ((IHasPath)decoded.HitObjects[1]).Path; - Assert.That(second.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero)); - Assert.That(second.ControlPoints[0].Type.Value, Is.EqualTo(PathType.PerfectCurve)); - Assert.That(second.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(161, -244))); - Assert.That(second.ControlPoints[1].Type.Value, Is.EqualTo(null)); - Assert.That(second.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(376, -3))); - Assert.That(second.ControlPoints[2].Type.Value, Is.EqualTo(null)); + Assert.That(second.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); + Assert.That(second.ControlPoints[0].Type, Is.EqualTo(PathType.PerfectCurve)); + Assert.That(second.ControlPoints[1].Position, Is.EqualTo(new Vector2(161, -244))); + Assert.That(second.ControlPoints[1].Type, Is.EqualTo(null)); + Assert.That(second.ControlPoints[2].Position, Is.EqualTo(new Vector2(376, -3))); + Assert.That(second.ControlPoints[2].Type, Is.EqualTo(null)); // Implicit multi-segment var third = ((IHasPath)decoded.HitObjects[2]).Path; - Assert.That(third.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero)); - Assert.That(third.ControlPoints[0].Type.Value, Is.EqualTo(PathType.Bezier)); - Assert.That(third.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(0, 192))); - Assert.That(third.ControlPoints[1].Type.Value, Is.EqualTo(null)); - Assert.That(third.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(224, 192))); - Assert.That(third.ControlPoints[2].Type.Value, Is.EqualTo(null)); + Assert.That(third.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); + Assert.That(third.ControlPoints[0].Type, Is.EqualTo(PathType.Bezier)); + Assert.That(third.ControlPoints[1].Position, Is.EqualTo(new Vector2(0, 192))); + Assert.That(third.ControlPoints[1].Type, Is.EqualTo(null)); + Assert.That(third.ControlPoints[2].Position, Is.EqualTo(new Vector2(224, 192))); + Assert.That(third.ControlPoints[2].Type, Is.EqualTo(null)); - Assert.That(third.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(224, 0))); - Assert.That(third.ControlPoints[3].Type.Value, Is.EqualTo(PathType.Bezier)); - Assert.That(third.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(224, -192))); - Assert.That(third.ControlPoints[4].Type.Value, Is.EqualTo(null)); - Assert.That(third.ControlPoints[5].Position.Value, Is.EqualTo(new Vector2(480, -192))); - Assert.That(third.ControlPoints[5].Type.Value, Is.EqualTo(null)); - Assert.That(third.ControlPoints[6].Position.Value, Is.EqualTo(new Vector2(480, 0))); - Assert.That(third.ControlPoints[6].Type.Value, Is.EqualTo(null)); + Assert.That(third.ControlPoints[3].Position, Is.EqualTo(new Vector2(224, 0))); + Assert.That(third.ControlPoints[3].Type, Is.EqualTo(PathType.Bezier)); + Assert.That(third.ControlPoints[4].Position, Is.EqualTo(new Vector2(224, -192))); + Assert.That(third.ControlPoints[4].Type, Is.EqualTo(null)); + Assert.That(third.ControlPoints[5].Position, Is.EqualTo(new Vector2(480, -192))); + Assert.That(third.ControlPoints[5].Type, Is.EqualTo(null)); + Assert.That(third.ControlPoints[6].Position, Is.EqualTo(new Vector2(480, 0))); + Assert.That(third.ControlPoints[6].Type, Is.EqualTo(null)); // Last control point duplicated var fourth = ((IHasPath)decoded.HitObjects[3]).Path; - Assert.That(fourth.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero)); - Assert.That(fourth.ControlPoints[0].Type.Value, Is.EqualTo(PathType.Bezier)); - Assert.That(fourth.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(1, 1))); - Assert.That(fourth.ControlPoints[1].Type.Value, Is.EqualTo(null)); - Assert.That(fourth.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(2, 2))); - Assert.That(fourth.ControlPoints[2].Type.Value, Is.EqualTo(null)); - Assert.That(fourth.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(3, 3))); - Assert.That(fourth.ControlPoints[3].Type.Value, Is.EqualTo(null)); - Assert.That(fourth.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(3, 3))); - Assert.That(fourth.ControlPoints[4].Type.Value, Is.EqualTo(null)); + Assert.That(fourth.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); + Assert.That(fourth.ControlPoints[0].Type, Is.EqualTo(PathType.Bezier)); + Assert.That(fourth.ControlPoints[1].Position, Is.EqualTo(new Vector2(1, 1))); + Assert.That(fourth.ControlPoints[1].Type, Is.EqualTo(null)); + Assert.That(fourth.ControlPoints[2].Position, Is.EqualTo(new Vector2(2, 2))); + Assert.That(fourth.ControlPoints[2].Type, Is.EqualTo(null)); + Assert.That(fourth.ControlPoints[3].Position, Is.EqualTo(new Vector2(3, 3))); + Assert.That(fourth.ControlPoints[3].Type, Is.EqualTo(null)); + Assert.That(fourth.ControlPoints[4].Position, Is.EqualTo(new Vector2(3, 3))); + Assert.That(fourth.ControlPoints[4].Type, Is.EqualTo(null)); // Last control point in segment duplicated var fifth = ((IHasPath)decoded.HitObjects[4]).Path; - Assert.That(fifth.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero)); - Assert.That(fifth.ControlPoints[0].Type.Value, Is.EqualTo(PathType.Bezier)); - Assert.That(fifth.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(1, 1))); - Assert.That(fifth.ControlPoints[1].Type.Value, Is.EqualTo(null)); - Assert.That(fifth.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(2, 2))); - Assert.That(fifth.ControlPoints[2].Type.Value, Is.EqualTo(null)); - Assert.That(fifth.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(3, 3))); - Assert.That(fifth.ControlPoints[3].Type.Value, Is.EqualTo(null)); - Assert.That(fifth.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(3, 3))); - Assert.That(fifth.ControlPoints[4].Type.Value, Is.EqualTo(null)); + Assert.That(fifth.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); + Assert.That(fifth.ControlPoints[0].Type, Is.EqualTo(PathType.Bezier)); + Assert.That(fifth.ControlPoints[1].Position, Is.EqualTo(new Vector2(1, 1))); + Assert.That(fifth.ControlPoints[1].Type, Is.EqualTo(null)); + Assert.That(fifth.ControlPoints[2].Position, Is.EqualTo(new Vector2(2, 2))); + Assert.That(fifth.ControlPoints[2].Type, Is.EqualTo(null)); + Assert.That(fifth.ControlPoints[3].Position, Is.EqualTo(new Vector2(3, 3))); + Assert.That(fifth.ControlPoints[3].Type, Is.EqualTo(null)); + Assert.That(fifth.ControlPoints[4].Position, Is.EqualTo(new Vector2(3, 3))); + Assert.That(fifth.ControlPoints[4].Type, Is.EqualTo(null)); - Assert.That(fifth.ControlPoints[5].Position.Value, Is.EqualTo(new Vector2(4, 4))); - Assert.That(fifth.ControlPoints[5].Type.Value, Is.EqualTo(PathType.Bezier)); - Assert.That(fifth.ControlPoints[6].Position.Value, Is.EqualTo(new Vector2(5, 5))); - Assert.That(fifth.ControlPoints[6].Type.Value, Is.EqualTo(null)); + Assert.That(fifth.ControlPoints[5].Position, Is.EqualTo(new Vector2(4, 4))); + Assert.That(fifth.ControlPoints[5].Type, Is.EqualTo(PathType.Bezier)); + Assert.That(fifth.ControlPoints[6].Position, Is.EqualTo(new Vector2(5, 5))); + Assert.That(fifth.ControlPoints[6].Type, Is.EqualTo(null)); // Implicit perfect-curve multi-segment(Should convert to bezier to match stable) var sixth = ((IHasPath)decoded.HitObjects[5]).Path; - Assert.That(sixth.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero)); - Assert.That(sixth.ControlPoints[0].Type.Value == PathType.Bezier); - Assert.That(sixth.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(75, 145))); - Assert.That(sixth.ControlPoints[1].Type.Value == null); - Assert.That(sixth.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(170, 75))); + Assert.That(sixth.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); + Assert.That(sixth.ControlPoints[0].Type == PathType.Bezier); + Assert.That(sixth.ControlPoints[1].Position, Is.EqualTo(new Vector2(75, 145))); + Assert.That(sixth.ControlPoints[1].Type == null); + Assert.That(sixth.ControlPoints[2].Position, Is.EqualTo(new Vector2(170, 75))); - Assert.That(sixth.ControlPoints[2].Type.Value == PathType.Bezier); - Assert.That(sixth.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(300, 145))); - Assert.That(sixth.ControlPoints[3].Type.Value == null); - Assert.That(sixth.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(410, 20))); - Assert.That(sixth.ControlPoints[4].Type.Value == null); + Assert.That(sixth.ControlPoints[2].Type == PathType.Bezier); + Assert.That(sixth.ControlPoints[3].Position, Is.EqualTo(new Vector2(300, 145))); + Assert.That(sixth.ControlPoints[3].Type == null); + Assert.That(sixth.ControlPoints[4].Position, Is.EqualTo(new Vector2(410, 20))); + Assert.That(sixth.ControlPoints[4].Type == null); // Explicit perfect-curve multi-segment(Should not convert to bezier) var seventh = ((IHasPath)decoded.HitObjects[6]).Path; - Assert.That(seventh.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero)); - Assert.That(seventh.ControlPoints[0].Type.Value == PathType.PerfectCurve); - Assert.That(seventh.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(75, 145))); - Assert.That(seventh.ControlPoints[1].Type.Value == null); - Assert.That(seventh.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(170, 75))); + Assert.That(seventh.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); + Assert.That(seventh.ControlPoints[0].Type == PathType.PerfectCurve); + Assert.That(seventh.ControlPoints[1].Position, Is.EqualTo(new Vector2(75, 145))); + Assert.That(seventh.ControlPoints[1].Type == null); + Assert.That(seventh.ControlPoints[2].Position, Is.EqualTo(new Vector2(170, 75))); - Assert.That(seventh.ControlPoints[2].Type.Value == PathType.PerfectCurve); - Assert.That(seventh.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(300, 145))); - Assert.That(seventh.ControlPoints[3].Type.Value == null); - Assert.That(seventh.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(410, 20))); - Assert.That(seventh.ControlPoints[4].Type.Value == null); + Assert.That(seventh.ControlPoints[2].Type == PathType.PerfectCurve); + Assert.That(seventh.ControlPoints[3].Position, Is.EqualTo(new Vector2(300, 145))); + Assert.That(seventh.ControlPoints[3].Type == null); + Assert.That(seventh.ControlPoints[4].Position, Is.EqualTo(new Vector2(410, 20))); + Assert.That(seventh.ControlPoints[4].Type == null); } } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs index 606395c289..9750838433 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs @@ -74,14 +74,14 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestAddControlPoint() { AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100)))); - AddStep("add point", () => path.ControlPoints.Add(new PathControlPoint { Position = { Value = new Vector2(100) } })); + AddStep("add point", () => path.ControlPoints.Add(new PathControlPoint { Position = new Vector2(100) })); } [Test] public void TestInsertControlPoint() { AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(100)))); - AddStep("insert point", () => path.ControlPoints.Insert(1, new PathControlPoint { Position = { Value = new Vector2(0, 100) } })); + AddStep("insert point", () => path.ControlPoints.Insert(1, new PathControlPoint { Position = new Vector2(0, 100) })); } [Test] @@ -95,14 +95,14 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestChangePathType() { AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); - AddStep("change type to bezier", () => path.ControlPoints[0].Type.Value = PathType.Bezier); + AddStep("change type to bezier", () => path.ControlPoints[0].Type = PathType.Bezier); } [Test] public void TestAddSegmentByChangingType() { AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)))); - AddStep("change second point type to bezier", () => path.ControlPoints[1].Type.Value = PathType.Bezier); + AddStep("change second point type to bezier", () => path.ControlPoints[1].Type = PathType.Bezier); } [Test] @@ -111,10 +111,10 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("create path", () => { path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); - path.ControlPoints[1].Type.Value = PathType.Bezier; + path.ControlPoints[1].Type = PathType.Bezier; }); - AddStep("change second point type to null", () => path.ControlPoints[1].Type.Value = null); + AddStep("change second point type to null", () => path.ControlPoints[1].Type = null); } [Test] @@ -123,7 +123,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("create path", () => { path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); - path.ControlPoints[1].Type.Value = PathType.Bezier; + path.ControlPoints[1].Type = PathType.Bezier; }); AddStep("remove second point", () => path.ControlPoints.RemoveAt(1)); @@ -185,8 +185,8 @@ namespace osu.Game.Tests.Visual.Gameplay private List createSegment(PathType type, params Vector2[] controlPoints) { - var points = controlPoints.Select(p => new PathControlPoint { Position = { Value = p } }).ToList(); - points[0].Type.Value = type; + var points = controlPoints.Select(p => new PathControlPoint { Position = p }).ToList(); + points[0].Type = type; return points; } } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index fade7183a3..246dc991d5 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -323,22 +323,22 @@ namespace osu.Game.Beatmaps.Formats { PathControlPoint point = pathData.Path.ControlPoints[i]; - if (point.Type.Value != null) + if (point.Type != null) { // We've reached a new (explicit) segment! // Explicit segments have a new format in which the type is injected into the middle of the control point string. // To preserve compatibility with osu-stable as much as possible, explicit segments with the same type are converted to use implicit segments by duplicating the control point. // One exception are consecutive perfect curves, which aren't supported in osu!stable and can lead to decoding issues if encoded as implicit segments - bool needsExplicitSegment = point.Type.Value != lastType || point.Type.Value == PathType.PerfectCurve; + bool needsExplicitSegment = point.Type != lastType || point.Type == PathType.PerfectCurve; // Another exception to this is when the last two control points of the last segment were duplicated. This is not a scenario supported by osu!stable. // Lazer does not add implicit segments for the last two control points of _any_ explicit segment, so an explicit segment is forced in order to maintain consistency with the decoder. if (i > 1) { // We need to use the absolute control point position to determine equality, otherwise floating point issues may arise. - Vector2 p1 = position + pathData.Path.ControlPoints[i - 1].Position.Value; - Vector2 p2 = position + pathData.Path.ControlPoints[i - 2].Position.Value; + Vector2 p1 = position + pathData.Path.ControlPoints[i - 1].Position; + Vector2 p2 = position + pathData.Path.ControlPoints[i - 2].Position; if ((int)p1.X == (int)p2.X && (int)p1.Y == (int)p2.Y) needsExplicitSegment = true; @@ -346,7 +346,7 @@ namespace osu.Game.Beatmaps.Formats if (needsExplicitSegment) { - switch (point.Type.Value) + switch (point.Type) { case PathType.Bezier: writer.Write("B|"); @@ -365,18 +365,18 @@ namespace osu.Game.Beatmaps.Formats break; } - lastType = point.Type.Value; + lastType = point.Type; } else { // New segment with the same type - duplicate the control point - writer.Write(FormattableString.Invariant($"{position.X + point.Position.Value.X}:{position.Y + point.Position.Value.Y}|")); + writer.Write(FormattableString.Invariant($"{position.X + point.Position.X}:{position.Y + point.Position.Y}|")); } } if (i != 0) { - writer.Write(FormattableString.Invariant($"{position.X + point.Position.Value.X}:{position.Y + point.Position.Value.Y}")); + writer.Write(FormattableString.Invariant($"{position.X + point.Position.X}:{position.Y + point.Position.Y}")); writer.Write(i != pathData.Path.ControlPoints.Count - 1 ? "|" : ","); } } diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index e8a5463cce..0942a7264d 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -323,7 +323,7 @@ namespace osu.Game.Rulesets.Objects.Legacy } // The first control point must have a definite type. - vertices[0].Type.Value = type; + vertices[0].Type = type; // A path can have multiple implicit segments of the same type if there are two sequential control points with the same position. // To handle such cases, this code may return multiple path segments with the final control point in each segment having a non-null type. @@ -337,7 +337,7 @@ namespace osu.Game.Rulesets.Objects.Legacy while (++endIndex < vertices.Length - endPointLength) { // Keep incrementing while an implicit segment doesn't need to be started - if (vertices[endIndex].Position.Value != vertices[endIndex - 1].Position.Value) + if (vertices[endIndex].Position != vertices[endIndex - 1].Position) continue; // The last control point of each segment is not allowed to start a new implicit segment. @@ -345,7 +345,7 @@ namespace osu.Game.Rulesets.Objects.Legacy continue; // Force a type on the last point, and return the current control point set as a segment. - vertices[endIndex - 1].Type.Value = type; + vertices[endIndex - 1].Type = type; yield return vertices.AsMemory().Slice(startIndex, endIndex - startIndex); // Skip the current control point - as it's the same as the one that's just been returned. @@ -360,11 +360,11 @@ namespace osu.Game.Rulesets.Objects.Legacy string[] vertexSplit = value.Split(':'); Vector2 pos = new Vector2((int)Parsing.ParseDouble(vertexSplit[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseDouble(vertexSplit[1], Parsing.MAX_COORDINATE_VALUE)) - startPos; - point = new PathControlPoint { Position = { Value = pos } }; + point = new PathControlPoint { Position = pos }; } - static bool isLinear(PathControlPoint[] p) => Precision.AlmostEquals(0, (p[1].Position.Value.Y - p[0].Position.Value.Y) * (p[2].Position.Value.X - p[0].Position.Value.X) - - (p[1].Position.Value.X - p[0].Position.Value.X) * (p[2].Position.Value.Y - p[0].Position.Value.Y)); + static bool isLinear(PathControlPoint[] p) => Precision.AlmostEquals(0, (p[1].Position.Y - p[0].Position.Y) * (p[2].Position.X - p[0].Position.X) + - (p[1].Position.X - p[0].Position.X) * (p[2].Position.Y - p[0].Position.Y)); } private PathControlPoint[] mergePointsLists(List> controlPointList) diff --git a/osu.Game/Rulesets/Objects/PathControlPoint.cs b/osu.Game/Rulesets/Objects/PathControlPoint.cs index f11917f4f4..53eb430fa3 100644 --- a/osu.Game/Rulesets/Objects/PathControlPoint.cs +++ b/osu.Game/Rulesets/Objects/PathControlPoint.cs @@ -3,7 +3,6 @@ using System; using Newtonsoft.Json; -using osu.Framework.Bindables; using osu.Game.Rulesets.Objects.Types; using osuTK; @@ -11,31 +10,55 @@ namespace osu.Game.Rulesets.Objects { public class PathControlPoint : IEquatable { + private Vector2 position; + /// /// The position of this . /// [JsonProperty] - public readonly Bindable Position = new Bindable(); + public Vector2 Position + { + get => position; + set + { + if (value == position) + return; + + position = value; + Changed?.Invoke(); + } + } + + private PathType? type; /// /// The type of path segment starting at this . /// If null, this will be a part of the previous path segment. /// [JsonProperty] - public readonly Bindable Type = new Bindable(); + public PathType? Type + { + get => type; + set + { + if (value == type) + return; + + type = value; + Changed?.Invoke(); + } + } /// /// Invoked when any property of this is changed. /// - internal event Action Changed; + public event Action Changed; /// /// Creates a new . /// public PathControlPoint() { - Position.ValueChanged += _ => Changed?.Invoke(); - Type.ValueChanged += _ => Changed?.Invoke(); } /// @@ -46,10 +69,10 @@ namespace osu.Game.Rulesets.Objects public PathControlPoint(Vector2 position, PathType? type = null) : this() { - Position.Value = position; - Type.Value = type; + Position = position; + Type = type; } - public bool Equals(PathControlPoint other) => Position.Value == other?.Position.Value && Type.Value == other.Type.Value; + public bool Equals(PathControlPoint other) => Position == other?.Position && Type == other.Type; } } diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 55ef0bc5f6..9cc215589b 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -169,7 +169,7 @@ namespace osu.Game.Rulesets.Objects foreach (PathControlPoint point in ControlPoints) { - if (point.Type.Value != null) + if (point.Type != null) { if (!found) pointsInCurrentSegment.Clear(); @@ -215,18 +215,18 @@ namespace osu.Game.Rulesets.Objects Vector2[] vertices = new Vector2[ControlPoints.Count]; for (int i = 0; i < ControlPoints.Count; i++) - vertices[i] = ControlPoints[i].Position.Value; + vertices[i] = ControlPoints[i].Position; int start = 0; for (int i = 0; i < ControlPoints.Count; i++) { - if (ControlPoints[i].Type.Value == null && i < ControlPoints.Count - 1) + if (ControlPoints[i].Type == null && i < ControlPoints.Count - 1) continue; // The current vertex ends the segment var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1); - var segmentType = ControlPoints[start].Type.Value ?? PathType.Linear; + var segmentType = ControlPoints[start].Type ?? PathType.Linear; foreach (Vector2 t in calculateSubPath(segmentVertices, segmentType)) { diff --git a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs index 1438c2f128..663746bfca 100644 --- a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs +++ b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Objects public static void Reverse(this SliderPath sliderPath, out Vector2 positionalOffset) { var points = sliderPath.ControlPoints.ToArray(); - positionalOffset = points.Last().Position.Value; + positionalOffset = points.Last().Position; sliderPath.ControlPoints.Clear(); @@ -28,17 +28,13 @@ namespace osu.Game.Rulesets.Objects for (var i = 0; i < points.Length; i++) { var p = points[i]; - p.Position.Value -= positionalOffset; + p.Position -= positionalOffset; // propagate types forwards to last null type if (i == points.Length - 1) - p.Type.Value = lastType; - else if (p.Type.Value != null) - { - var newType = p.Type.Value; - p.Type.Value = lastType; - lastType = newType; - } + p.Type = lastType; + else if (p.Type != null) + (p.Type, lastType) = (lastType, p.Type); sliderPath.ControlPoints.Insert(0, p); } From 69064c1938e6f574ca8908939fc144aeb35cafae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 26 Aug 2021 03:41:58 +0900 Subject: [PATCH 1508/2442] Avoid unnecessary unbind operations when constructing `FollowPointLifetimeEntry` --- .../Connections/FollowPointLifetimeEntry.cs | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointLifetimeEntry.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointLifetimeEntry.cs index 82bca0a4e2..c0d3572adf 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointLifetimeEntry.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointLifetimeEntry.cs @@ -4,6 +4,7 @@ #nullable enable using System; +using System.Diagnostics; using osu.Framework.Bindables; using osu.Framework.Graphics.Performance; using osu.Game.Rulesets.Objects; @@ -20,8 +21,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections { Start = start; LifetimeStart = Start.StartTime; - - bindEvents(); } private OsuHitObject? end; @@ -41,31 +40,39 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections } } + private bool wasBound = false; + private void bindEvents() { UnbindEvents(); + if (End == null) + return; + // Note: Positions are bound for instantaneous feedback from positional changes from the editor, before ApplyDefaults() is called on hitobjects. Start.DefaultsApplied += onDefaultsApplied; Start.PositionBindable.ValueChanged += onPositionChanged; - if (End != null) - { - End.DefaultsApplied += onDefaultsApplied; - End.PositionBindable.ValueChanged += onPositionChanged; - } + End.DefaultsApplied += onDefaultsApplied; + End.PositionBindable.ValueChanged += onPositionChanged; + + wasBound = true; } public void UnbindEvents() { + if (!wasBound) + return; + + Debug.Assert(End != null); + Start.DefaultsApplied -= onDefaultsApplied; Start.PositionBindable.ValueChanged -= onPositionChanged; - if (End != null) - { - End.DefaultsApplied -= onDefaultsApplied; - End.PositionBindable.ValueChanged -= onPositionChanged; - } + End.DefaultsApplied -= onDefaultsApplied; + End.PositionBindable.ValueChanged -= onPositionChanged; + + wasBound = false; } private void onDefaultsApplied(HitObject obj) => refreshLifetimes(); From f4199958d93995b0db1235fc986e1f07d1ef80ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 26 Aug 2021 04:01:37 +0900 Subject: [PATCH 1509/2442] Avoid unnecessary array/LINQ operations when replay frames have no action changes --- osu.Game/Input/Handlers/ReplayInputHandler.cs | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/osu.Game/Input/Handlers/ReplayInputHandler.cs b/osu.Game/Input/Handlers/ReplayInputHandler.cs index cd76000f98..e4aec4edac 100644 --- a/osu.Game/Input/Handlers/ReplayInputHandler.cs +++ b/osu.Game/Input/Handlers/ReplayInputHandler.cs @@ -42,9 +42,24 @@ namespace osu.Game.Input.Handlers if (!(state is RulesetInputManagerInputState inputState)) throw new InvalidOperationException($"{nameof(ReplayState)} should only be applied to a {nameof(RulesetInputManagerInputState)}"); - var lastPressed = inputState.LastReplayState?.PressedActions ?? new List(); - var released = lastPressed.Except(PressedActions).ToArray(); - var pressed = PressedActions.Except(lastPressed).ToArray(); + T[] released = Array.Empty(); + T[] pressed = Array.Empty(); + + var lastPressed = inputState.LastReplayState?.PressedActions; + + if (lastPressed == null || lastPressed.Count == 0) + { + pressed = PressedActions.ToArray(); + } + else if (PressedActions.Count == 0) + { + released = lastPressed.ToArray(); + } + else if (!lastPressed.SequenceEqual(PressedActions)) + { + released = lastPressed.Except(PressedActions).ToArray(); + pressed = PressedActions.Except(lastPressed).ToArray(); + } inputState.LastReplayState = this; From 8cfb3d456b3014612c835037f83ce06d3130a296 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 26 Aug 2021 04:12:23 +0900 Subject: [PATCH 1510/2442] Avoid expensive text spacing transforms for now --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs index 79655c33e4..b23087a1f3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs @@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables base.PlayAnimation(); if (Result != HitResult.Miss) - JudgementText.TransformSpacingTo(Vector2.Zero).Then().TransformSpacingTo(new Vector2(14, 0), 1800, Easing.OutQuint); + JudgementText.ScaleTo(new Vector2(0.8f, 1)).Then().ScaleTo(new Vector2(1.2f, 1), 1800, Easing.OutQuint); } } } From e32933eb54f5df45e18367387a53c9d32401601f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 26 Aug 2021 04:19:49 +0900 Subject: [PATCH 1511/2442] Avoid `Enum.GetValues` in each score population pass --- osu.Game/Rulesets/Scoring/HitResult.cs | 7 +++++++ osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/HitResult.cs b/osu.Game/Rulesets/Scoring/HitResult.cs index eaa1f95744..5599ed96a3 100644 --- a/osu.Game/Rulesets/Scoring/HitResult.cs +++ b/osu.Game/Rulesets/Scoring/HitResult.cs @@ -1,8 +1,10 @@ // 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.ComponentModel; using System.Diagnostics; +using System.Linq; using osu.Framework.Utils; namespace osu.Game.Rulesets.Scoring @@ -171,6 +173,11 @@ namespace osu.Game.Rulesets.Scoring /// public static bool IsScorable(this HitResult result) => result >= HitResult.Miss && result < HitResult.IgnoreMiss; + /// + /// An array of all scorable s. + /// + public static readonly HitResult[] SCORABLE_TYPES = ((HitResult[])Enum.GetValues(typeof(HitResult))).Where(r => r.IsScorable()).ToArray(); + /// /// Whether a is valid within a given range. /// diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 6a2601170c..16f2607bad 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -339,7 +339,7 @@ namespace osu.Game.Rulesets.Scoring score.Accuracy = Accuracy.Value; score.Rank = Rank.Value; - foreach (var result in Enum.GetValues(typeof(HitResult)).OfType().Where(r => r.IsScorable())) + foreach (var result in HitResultExtensions.SCORABLE_TYPES) score.Statistics[result] = GetStatistic(result); score.HitEvents = hitEvents; From e15198f0771836d44ecf7046f8da5ea3d4cb98fe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 26 Aug 2021 13:47:10 +0900 Subject: [PATCH 1512/2442] Update missed tests --- .../Editor/TestScenePathControlPointVisualiser.cs | 2 +- .../Editor/TestSceneSliderControlPointPiece.cs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs index 53a9f06eee..5a1aa42ed1 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs @@ -166,7 +166,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { AddStep($"move mouse to control point {index}", () => { - Vector2 position = slider.Path.ControlPoints[index].Position.Value; + Vector2 position = slider.Path.ControlPoints[index].Position; InputManager.MoveMouseTo(visualiser.Pieces[0].Parent.ToScreenSpace(position)); }); } diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderControlPointPiece.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderControlPointPiece.cs index 24b947c854..6bfe7f892b 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderControlPointPiece.cs @@ -108,9 +108,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor [Test] public void TestDragControlPointPathAfterChangingType() { - AddStep("change type to bezier", () => slider.Path.ControlPoints[2].Type.Value = PathType.Bezier); + AddStep("change type to bezier", () => slider.Path.ControlPoints[2].Type = PathType.Bezier); AddStep("add point", () => slider.Path.ControlPoints.Add(new PathControlPoint(new Vector2(500, 10)))); - AddStep("change type to perfect", () => slider.Path.ControlPoints[3].Type.Value = PathType.PerfectCurve); + AddStep("change type to perfect", () => slider.Path.ControlPoints[3].Type = PathType.PerfectCurve); moveMouseToControlPoint(4); AddStep("hold", () => InputManager.PressButton(MouseButton.Left)); @@ -137,15 +137,15 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { AddStep($"move mouse to control point {index}", () => { - Vector2 position = slider.Position + slider.Path.ControlPoints[index].Position.Value; + Vector2 position = slider.Position + slider.Path.ControlPoints[index].Position; InputManager.MoveMouseTo(drawableObject.Parent.ToScreenSpace(position)); }); } - private void assertControlPointType(int index, PathType type) => AddAssert($"control point {index} is {type}", () => slider.Path.ControlPoints[index].Type.Value == type); + private void assertControlPointType(int index, PathType type) => AddAssert($"control point {index} is {type}", () => slider.Path.ControlPoints[index].Type == type); private void assertControlPointPosition(int index, Vector2 position) => - AddAssert($"control point {index} at {position}", () => Precision.AlmostEquals(position, slider.Path.ControlPoints[index].Position.Value, 1)); + AddAssert($"control point {index} at {position}", () => Precision.AlmostEquals(position, slider.Path.ControlPoints[index].Position, 1)); private class TestSliderBlueprint : SliderSelectionBlueprint { From e633b2716d99270bb722521ec71718e43c7600be Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 26 Aug 2021 13:58:23 +0900 Subject: [PATCH 1513/2442] Fix regression in outro skip handling logic --- osu.Game/Screens/Play/SkipOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/SkipOverlay.cs b/osu.Game/Screens/Play/SkipOverlay.cs index 71a70991d2..a2145b7014 100644 --- a/osu.Game/Screens/Play/SkipOverlay.cs +++ b/osu.Game/Screens/Play/SkipOverlay.cs @@ -102,7 +102,7 @@ namespace osu.Game.Screens.Play public override void Show() { base.Show(); - fadeContainer.Show(); + fadeContainer.TriggerShow(); } protected override void LoadComplete() From 17f6efc6fecad7d7d59632f6ca84a7922a0c5059 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 26 Aug 2021 14:02:56 +0900 Subject: [PATCH 1514/2442] Fix missed cases of incorrect `.Value` usage Changing from `Bindable` to `Nullable` comes with its issues... --- .../Editor/TestSceneJuiceStreamSelectionBlueprint.cs | 4 ++-- .../Sliders/Components/PathControlPointVisualiser.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs index f5ef5c5e18..5e73a89069 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs @@ -210,9 +210,9 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor new Vector2(50, 200), }), 0.5); AddAssert("1 vertex per 1 nested HO", () => getVertices().Count == hitObject.NestedHitObjects.Count); - AddAssert("slider path not yet changed", () => hitObject.Path.ControlPoints[0].Type.Value == PathType.PerfectCurve); + AddAssert("slider path not yet changed", () => hitObject.Path.ControlPoints[0].Type == PathType.PerfectCurve); addAddVertexSteps(150, 150); - AddAssert("slider path change to linear", () => hitObject.Path.ControlPoints[0].Type.Value == PathType.Linear); + AddAssert("slider path change to linear", () => hitObject.Path.ControlPoints[0].Type == PathType.Linear); } private void addBlueprintStep(double time, float x, SliderPath sliderPath, double velocity) => AddStep("add selection blueprint", () => diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index ac1953c632..6269a41350 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -241,7 +241,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private MenuItem createMenuItemForPathType(PathType? type) { int totalCount = Pieces.Count(p => p.IsSelected.Value); - int countOfState = Pieces.Where(p => p.IsSelected.Value).Count(p => p.ControlPoint.Type.Value == type); + int countOfState = Pieces.Where(p => p.IsSelected.Value).Count(p => p.ControlPoint.Type == type); var item = new TernaryStateRadioMenuItem(type == null ? "Inherit" : type.ToString().Humanize(), MenuItemType.Standard, _ => { From b9ea984c360bebb3d95dac301a9fd01c4ca63900 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 26 Aug 2021 08:18:58 +0300 Subject: [PATCH 1515/2442] Remove redundant default value --- .../Objects/Drawables/Connections/FollowPointLifetimeEntry.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointLifetimeEntry.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointLifetimeEntry.cs index c0d3572adf..30ff6b8984 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointLifetimeEntry.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointLifetimeEntry.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections } } - private bool wasBound = false; + private bool wasBound; private void bindEvents() { From e341f471b01b4b4acc3ce5a51c71e2f6545b0537 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 26 Aug 2021 15:29:22 +0900 Subject: [PATCH 1516/2442] Add lobby sfx for join/leave/kick/ready/unready events --- .../TestSceneMultiplayerLobbyEvents.cs | 216 ++++++++++++++++++ .../Match/MultiplayerReadyButton.cs | 45 ++-- .../Participants/ParticipantsList.cs | 34 ++- 3 files changed, 278 insertions(+), 17 deletions(-) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLobbyEvents.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLobbyEvents.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLobbyEvents.cs new file mode 100644 index 0000000000..accfdceaad --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLobbyEvents.cs @@ -0,0 +1,216 @@ +// 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.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Platform; +using osu.Framework.Testing; +using osu.Framework.Utils; +using osu.Game.Beatmaps; +using osu.Game.Database; +using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; +using osu.Game.Screens; +using osu.Game.Screens.OnlinePlay.Components; +using osu.Game.Screens.OnlinePlay.Lounge; +using osu.Game.Screens.OnlinePlay.Multiplayer; +using osu.Game.Screens.OnlinePlay.Multiplayer.Match; +using osu.Game.Tests.Resources; +using osu.Game.Users; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneMultiplayerLobbyEvents : ScreenTestScene + { + private BeatmapManager beatmaps; + private RulesetStore rulesets; + private BeatmapSetInfo importedSet; + + private DependenciesScreen dependenciesScreen; + private TestMultiplayer multiplayerScreen; + private TestMultiplayerClient client; + + private TestRequestHandlingMultiplayerRoomManager roomManager => multiplayerScreen.RoomManager; + + [Cached(typeof(UserLookupCache))] + private UserLookupCache lookupCache = new TestUserLookupCache(); + + [BackgroundDependencyLoader] + private void load(GameHost host, AudioManager audio) + { + Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); + Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); + } + + public override void SetUpSteps() + { + base.SetUpSteps(); + + AddStep("import beatmap", () => + { + beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); + importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); + }); + + AddStep("create multiplayer screen", () => multiplayerScreen = new TestMultiplayer()); + + AddStep("load dependencies", () => + { + client = new TestMultiplayerClient(roomManager); + + // The screen gets suspended so it stops receiving updates. + Child = client; + + LoadScreen(dependenciesScreen = new DependenciesScreen(client)); + }); + + AddUntilStep("wait for dependencies to load", () => dependenciesScreen.IsLoaded); + + AddStep("load multiplayer", () => LoadScreen(multiplayerScreen)); + AddUntilStep("wait for multiplayer to load", () => multiplayerScreen.IsLoaded); + AddUntilStep("wait for lounge to load", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); + } + + [Test] + public void TestLobbyEvents() + { + createRoom(() => new Room + { + Name = { Value = "Test Room" }, + Playlist = + { + new PlaylistItem + { + Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + } + } + }); + } + + private void playerJoin() + { + int randomUser = RNG.Next(200000, 500000); + client.AddUser(new User { Id = randomUser, Username = $"user {randomUser}" }); + } + + private void playerLeave() + { + User lastUser = client.Room?.Users.Last().User; + + if (lastUser == null || lastUser == client.LocalUser?.User) + return; + + client.RemoveUser(lastUser); + } + + private void playerKicked() + { + User lastUser = client.Room?.Users.Last().User; + + if (lastUser == null || lastUser == client.LocalUser?.User) + return; + + client.KickUser(lastUser.Id); + } + + private void playerReady() + { + var nextUnready = client.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle); + if (nextUnready != null) + client.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Ready); + } + + private void playerUnready() + { + var nextUnready = client.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Ready); + if (nextUnready != null) + client.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Idle); + } + + private void randomEvent() + { + int eventToPerform = RNG.Next(1, 6); + + switch (eventToPerform) + { + case 1: + playerJoin(); + break; + + case 2: + playerLeave(); + break; + + case 3: + playerKicked(); + break; + + case 4: + playerReady(); + break; + + case 5: + playerUnready(); + break; + } + } + + private void createRoom(Func room) + { + AddUntilStep("wait for lounge", () => multiplayerScreen.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); + AddStep("open room", () => multiplayerScreen.ChildrenOfType().Single().Open(room())); + + AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); + AddWaitStep("wait for transition", 2); + + AddStep("create room", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().Single()); + InputManager.Click(MouseButton.Left); + }); + + AddUntilStep("wait for join", () => client.Room != null); + + AddRepeatStep("random stuff happens", randomEvent, 30); + + // ensure we have a handful of players so the ready-up sounds good :9 + AddRepeatStep("player joins", playerJoin, 5); + + // all ready + AddRepeatStep("players ready up", () => + { + var nextUnready = client.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle); + if (nextUnready != null) + client.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Ready); + }, 40); + } + + /// + /// Used for the sole purpose of adding as a resolvable dependency. + /// + private class DependenciesScreen : OsuScreen + { + [Cached(typeof(MultiplayerClient))] + public readonly TestMultiplayerClient Client; + + public DependenciesScreen(TestMultiplayerClient client) + { + Client = client; + } + } + + private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer + { + public new TestRequestHandlingMultiplayerRoomManager RoomManager { get; private set; } + + protected override RoomManager CreateRoomManager() => RoomManager = new TestRequestHandlingMultiplayerRoomManager(); + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 2a40a61257..9c818fc070 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -35,7 +35,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private IBindable operationInProgress; - private Sample sampleReadyCount; + private Sample sampleReady; + private Sample sampleReadyAll; + private Sample sampleUnready; + private double unreadyLastPlaybackTime; private readonly ButtonWithTrianglesExposed button; @@ -54,10 +57,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match [BackgroundDependencyLoader] private void load(AudioManager audio) { - sampleReadyCount = audio.Samples.Get(@"SongSelect/select-difficulty"); - operationInProgress = ongoingOperationTracker.InProgress.GetBoundCopy(); operationInProgress.BindValueChanged(_ => updateState()); + + sampleReady = audio.Samples.Get(@"Multiplayer/player-ready"); + sampleReadyAll = audio.Samples.Get(@"Multiplayer/player-ready-all"); + sampleUnready = audio.Samples.Get(@"Multiplayer/player-unready"); + + unreadyLastPlaybackTime = Time.Current; } protected override void OnRoomUpdated() @@ -107,21 +114,27 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match button.Enabled.Value = enableButton; - if (newCountReady != countReady) - { - countReady = newCountReady; - Scheduler.AddOnce(playSound); - } - } - - private void playSound() - { - if (sampleReadyCount == null) + if (newCountReady == countReady) return; - var channel = sampleReadyCount.GetChannel(); - channel.Frequency.Value = 0.77f + countReady * 0.06f; - channel.Play(); + if (newCountReady > countReady) + { + if (newCountReady == newCountTotal) + sampleReadyAll?.Play(); + else + sampleReady?.Play(); + } + else + { + // debounce sample playback to prevent the mass-unready of game mode changes from deafening players + if (Time.Current - unreadyLastPlaybackTime > 10) + { + sampleUnready?.Play(); + unreadyLastPlaybackTime = Time.Current; + } + } + + countReady = newCountReady; } private void updateButtonColour(bool green) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsList.cs index 3759e45f18..2ad64e115e 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantsList.cs @@ -3,10 +3,13 @@ using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; +using osu.Game.Online.Multiplayer; using osuTK; namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants @@ -15,8 +18,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants { private FillFlowContainer panels; + private Sample userJoinSample; + private Sample userLeftSample; + private Sample userKickedSample; + [BackgroundDependencyLoader] - private void load() + private void load(AudioManager audio) { InternalChild = new OsuContextMenuContainer { @@ -34,6 +41,31 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants } } }; + + userJoinSample = audio.Samples.Get(@"Multiplayer/player-joined"); + userLeftSample = audio.Samples.Get(@"Multiplayer/player-left"); + userKickedSample = audio.Samples.Get(@"Multiplayer/player-kicked"); + } + + protected override void UserJoined(MultiplayerRoomUser user) + { + base.UserJoined(user); + + userJoinSample?.Play(); + } + + protected override void UserLeft(MultiplayerRoomUser user) + { + base.UserLeft(user); + + userLeftSample?.Play(); + } + + protected override void UserKicked(MultiplayerRoomUser user) + { + base.UserKicked(user); + + userKickedSample?.Play(); } protected override void OnRoomUpdated() From 56baecdde45cbfed4789ff2de11c37bb603afb18 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 26 Aug 2021 15:30:20 +0900 Subject: [PATCH 1517/2442] Add missing interaction sfx to MatchTypePicker --- osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.cs index c6f9b0f207..216734e55e 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Components; using osuTK; @@ -75,6 +76,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components }, }, }, + new HoverClickSounds(), }; } From 15812520bd6d99105fb71c4b0448fb6bd545cd6f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 26 Aug 2021 09:44:53 +0300 Subject: [PATCH 1518/2442] Replace global editor test case with mania compose screen test scene --- .../Editor/TestSceneManiaComposeScreen.cs | 64 +++++++++++++++++++ osu.Game/Tests/Visual/EditorTestScene.cs | 10 --- 2 files changed, 64 insertions(+), 10 deletions(-) create mode 100644 osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaComposeScreen.cs diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaComposeScreen.cs b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaComposeScreen.cs new file mode 100644 index 0000000000..0f520215a1 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaComposeScreen.cs @@ -0,0 +1,64 @@ +// 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.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Screens.Edit; +using osu.Game.Screens.Edit.Compose; +using osu.Game.Skinning; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Mania.Tests.Editor +{ + public class TestSceneManiaComposeScreen : EditorClockTestScene + { + [Resolved] + private SkinManager skins { get; set; } + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("setup compose screen", () => + { + var editorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition { Columns = 4 })) + { + BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo }, + }; + + Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap); + + Child = new DependencyProvidingContainer + { + RelativeSizeAxes = Axes.Both, + CachedDependencies = new (Type, object)[] + { + (typeof(EditorBeatmap), editorBeatmap), + (typeof(IBeatSnapProvider), editorBeatmap), + }, + Child = new ComposeScreen { State = { Value = Visibility.Visible } }, + }; + }); + + AddUntilStep("wait for composer", () => this.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); + } + + [Test] + public void TestDefaultSkin() + { + AddStep("set default skin", () => skins.CurrentSkinInfo.Value = SkinInfo.Default); + } + + [Test] + public void TestLegacySkin() + { + AddStep("set legacy skin", () => skins.CurrentSkinInfo.Value = DefaultLegacySkin.Info); + } + } +} diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index 2644daa3a4..a393802309 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -3,7 +3,6 @@ using System.Linq; using JetBrains.Annotations; -using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.IO.Stores; @@ -29,9 +28,6 @@ namespace osu.Game.Tests.Visual protected EditorClock EditorClock { get; private set; } - [Resolved] - private SkinManager skins { get; set; } - /// /// Whether any saves performed by the editor should be isolate (and not persist) to the underlying . /// @@ -61,12 +57,6 @@ namespace osu.Game.Tests.Visual AddStep("get clock", () => EditorClock = Editor.ChildrenOfType().Single()); } - [Test] - public void TestLegacySkin() - { - AddStep("set legacy skin", () => skins.CurrentSkinInfo.Value = DefaultLegacySkin.Info); - } - protected virtual void LoadEditor() { LoadScreen(Editor = CreateEditor()); From ec85d7f3567569dbc9da26306b4d11ab843809ba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 26 Aug 2021 17:15:23 +0900 Subject: [PATCH 1519/2442] Remove unused helper method --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index b3e1b24d8d..29d8a475ef 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -54,11 +54,6 @@ namespace osu.Game.Rulesets.Objects.Drawables /// public readonly Bindable AccentColour = new Bindable(Color4.Gray); - /// - /// Gets the samples that are played by this object during gameplay. - /// - public ISampleInfo[] GetGameplaySamples() => Samples.Samples; - protected PausableSkinnableSound Samples { get; private set; } public virtual IEnumerable GetSamples() => HitObject.Samples; From 15aa0458bc801e3d3ecf6e3a8cc6d93fa0e1ea1d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 26 Aug 2021 17:15:36 +0900 Subject: [PATCH 1520/2442] Use `PausableSkinnableSound` instead --- osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs b/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs index ac2067a913..c18698f77e 100644 --- a/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs +++ b/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.UI InternalChild = hitSounds = new Container { Name = "concurrent sample pool", - ChildrenEnumerable = Enumerable.Range(0, max_concurrent_hitsounds).Select(_ => new SkinnableSound()) + ChildrenEnumerable = Enumerable.Range(0, max_concurrent_hitsounds).Select(_ => new PausableSkinnableSound()) }; } From f078a9d2bf942745f6f400a5c996357b27a70e21 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 26 Aug 2021 17:17:39 +0900 Subject: [PATCH 1521/2442] Fix incorrect step type --- .../Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs index 3e0a937ffa..fccc1a377c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs @@ -80,7 +80,7 @@ namespace osu.Game.Tests.Visual.Gameplay { HitObjectLifetimeEntry nextObjectEntry = null; - AddUntilStep("no alive objects", () => getNextAliveObject() == null); + AddAssert("no alive objects", () => getNextAliveObject() == null); AddAssert("check initially correct object", () => sampleTriggerSource.GetMostValidObject() == beatmap.HitObjects[0]); From 90e81a595d0c2fafae408bc8667043682c8a2587 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 26 Aug 2021 17:19:46 +0900 Subject: [PATCH 1522/2442] Move `DrumSampleTriggerSource` into its own class to avoid nested references --- .../Skinning/Legacy/LegacyInputDrum.cs | 2 +- .../UI/DrumSampleTriggerSource.cs | 30 +++++++++++++++++++ osu.Game.Rulesets.Taiko/UI/InputDrum.cs | 20 ------------- 3 files changed, 31 insertions(+), 21 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/UI/DrumSampleTriggerSource.cs diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs index 5a76694913..9d35093591 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs @@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy public readonly Sprite Centre; [Resolved] - private InputDrum.DrumSampleTriggerSource sampleTriggerSource { get; set; } + private DrumSampleTriggerSource sampleTriggerSource { get; set; } public LegacyHalfDrum(bool flipped) { diff --git a/osu.Game.Rulesets.Taiko/UI/DrumSampleTriggerSource.cs b/osu.Game.Rulesets.Taiko/UI/DrumSampleTriggerSource.cs new file mode 100644 index 0000000000..3279d128d3 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/UI/DrumSampleTriggerSource.cs @@ -0,0 +1,30 @@ +// 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 osu.Game.Audio; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Taiko.UI +{ + public class DrumSampleTriggerSource : GameplaySampleTriggerSource + { + public DrumSampleTriggerSource(HitObjectContainer hitObjectContainer) + : base(hitObjectContainer) + { + } + + public void Play(HitType hitType) + { + var hitObject = GetMostValidObject(); + + if (hitObject == null) + return; + + PlaySamples(new ISampleInfo[] { hitObject.SampleControlPoint.GetSampleInfo(hitType == HitType.Rim ? HitSampleInfo.HIT_CLAP : HitSampleInfo.HIT_NORMAL) }); + } + + public override void Play() => throw new InvalidOperationException(@"Use override with HitType parameter instead"); + } +} diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs index 24e2dddb49..3eafd201b7 100644 --- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs +++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Input.Bindings; -using osu.Game.Audio; using osu.Game.Graphics; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.UI; @@ -201,24 +200,5 @@ namespace osu.Game.Rulesets.Taiko.UI } } - public class DrumSampleTriggerSource : GameplaySampleTriggerSource - { - public DrumSampleTriggerSource(HitObjectContainer hitObjectContainer) - : base(hitObjectContainer) - { - } - - public void Play(HitType hitType) - { - var hitObject = GetMostValidObject(); - - if (hitObject == null) - return; - - PlaySamples(new ISampleInfo[] { hitObject.SampleControlPoint.GetSampleInfo(hitType == HitType.Rim ? HitSampleInfo.HIT_CLAP : HitSampleInfo.HIT_NORMAL) }); - } - - public override void Play() => throw new InvalidOperationException(@"Use override with HitType parameter instead"); - } } } From cea632463e781f59ad307e2f373632998656b41e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 26 Aug 2021 22:30:20 +0300 Subject: [PATCH 1523/2442] Remove empty newline --- osu.Game.Rulesets.Taiko/UI/InputDrum.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs index 3eafd201b7..ddfaf64549 100644 --- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs +++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs @@ -199,6 +199,5 @@ namespace osu.Game.Rulesets.Taiko.UI { } } - } } From 7d9bae450780cab1a51023cd2763a110f0e5eff5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 27 Aug 2021 18:29:49 +0900 Subject: [PATCH 1524/2442] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index cae8a946a3..f68d7124a9 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index bac99a098d..8e39c5a3df 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index f3ecc90bea..0e3e87f3c1 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From 34d185d846f47140ca18e220f3de92fd7623ddeb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 27 Aug 2021 18:39:36 +0900 Subject: [PATCH 1525/2442] Convert final step to until step to avoid unnecessary delays --- .../Visual/Multiplayer/TestSceneMultiplayerLobbyEvents.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLobbyEvents.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLobbyEvents.cs index accfdceaad..57ec0843b5 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLobbyEvents.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLobbyEvents.cs @@ -184,12 +184,14 @@ namespace osu.Game.Tests.Visual.Multiplayer AddRepeatStep("player joins", playerJoin, 5); // all ready - AddRepeatStep("players ready up", () => + AddUntilStep("all players ready", () => { var nextUnready = client.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle); if (nextUnready != null) client.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Ready); - }, 40); + + return client.Room?.Users.All(u => u.State == MultiplayerUserState.Ready) == true; + }); } /// From 23414b0c634534ed4b430b024c2e4010d9096aed Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 27 Aug 2021 18:44:44 +0900 Subject: [PATCH 1526/2442] Combine test scene to avoid huge copy paste --- .../Multiplayer/TestSceneMultiplayer.cs | 101 ++++++++ .../TestSceneMultiplayerLobbyEvents.cs | 218 ------------------ 2 files changed, 101 insertions(+), 218 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLobbyEvents.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 7a3507d944..d84fe4b6fb 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -12,6 +12,7 @@ using osu.Framework.Input; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Testing; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Graphics.UserInterface; @@ -94,6 +95,106 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("empty step", () => { }); } + [Test] + public void TestLobbyEvents() + { + createRoom(() => new Room + { + Name = { Value = "Test Room" }, + Playlist = + { + new PlaylistItem + { + Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + } + } + }); + + AddRepeatStep("random stuff happens", performRandomAction, 30); + + // ensure we have a handful of players so the ready-up sounds good :9 + AddRepeatStep("player joins", addRandomPlayer, 5); + + // all ready + AddUntilStep("all players ready", () => + { + var nextUnready = client.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle); + if (nextUnready != null) + client.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Ready); + + return client.Room?.Users.All(u => u.State == MultiplayerUserState.Ready) == true; + }); + } + + private void addRandomPlayer() + { + int randomUser = RNG.Next(200000, 500000); + client.AddUser(new User { Id = randomUser, Username = $"user {randomUser}" }); + } + + private void removeLastUser() + { + User lastUser = client.Room?.Users.Last().User; + + if (lastUser == null || lastUser == client.LocalUser?.User) + return; + + client.RemoveUser(lastUser); + } + + private void kickLastUser() + { + User lastUser = client.Room?.Users.Last().User; + + if (lastUser == null || lastUser == client.LocalUser?.User) + return; + + client.KickUser(lastUser.Id); + } + + private void markNextPlayerReady() + { + var nextUnready = client.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle); + if (nextUnready != null) + client.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Ready); + } + + private void markNextPlayerIdle() + { + var nextUnready = client.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Ready); + if (nextUnready != null) + client.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Idle); + } + + private void performRandomAction() + { + int eventToPerform = RNG.Next(1, 6); + + switch (eventToPerform) + { + case 1: + addRandomPlayer(); + break; + + case 2: + removeLastUser(); + break; + + case 3: + kickLastUser(); + break; + + case 4: + markNextPlayerReady(); + break; + + case 5: + markNextPlayerIdle(); + break; + } + } + [Test] public void TestCreateRoomViaKeyboard() { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLobbyEvents.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLobbyEvents.cs deleted file mode 100644 index 57ec0843b5..0000000000 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLobbyEvents.cs +++ /dev/null @@ -1,218 +0,0 @@ -// 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.Linq; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Platform; -using osu.Framework.Testing; -using osu.Framework.Utils; -using osu.Game.Beatmaps; -using osu.Game.Database; -using osu.Game.Online.Multiplayer; -using osu.Game.Online.Rooms; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Osu; -using osu.Game.Screens; -using osu.Game.Screens.OnlinePlay.Components; -using osu.Game.Screens.OnlinePlay.Lounge; -using osu.Game.Screens.OnlinePlay.Multiplayer; -using osu.Game.Screens.OnlinePlay.Multiplayer.Match; -using osu.Game.Tests.Resources; -using osu.Game.Users; -using osuTK.Input; - -namespace osu.Game.Tests.Visual.Multiplayer -{ - public class TestSceneMultiplayerLobbyEvents : ScreenTestScene - { - private BeatmapManager beatmaps; - private RulesetStore rulesets; - private BeatmapSetInfo importedSet; - - private DependenciesScreen dependenciesScreen; - private TestMultiplayer multiplayerScreen; - private TestMultiplayerClient client; - - private TestRequestHandlingMultiplayerRoomManager roomManager => multiplayerScreen.RoomManager; - - [Cached(typeof(UserLookupCache))] - private UserLookupCache lookupCache = new TestUserLookupCache(); - - [BackgroundDependencyLoader] - private void load(GameHost host, AudioManager audio) - { - Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); - } - - public override void SetUpSteps() - { - base.SetUpSteps(); - - AddStep("import beatmap", () => - { - beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); - importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); - }); - - AddStep("create multiplayer screen", () => multiplayerScreen = new TestMultiplayer()); - - AddStep("load dependencies", () => - { - client = new TestMultiplayerClient(roomManager); - - // The screen gets suspended so it stops receiving updates. - Child = client; - - LoadScreen(dependenciesScreen = new DependenciesScreen(client)); - }); - - AddUntilStep("wait for dependencies to load", () => dependenciesScreen.IsLoaded); - - AddStep("load multiplayer", () => LoadScreen(multiplayerScreen)); - AddUntilStep("wait for multiplayer to load", () => multiplayerScreen.IsLoaded); - AddUntilStep("wait for lounge to load", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); - } - - [Test] - public void TestLobbyEvents() - { - createRoom(() => new Room - { - Name = { Value = "Test Room" }, - Playlist = - { - new PlaylistItem - { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo }, - Ruleset = { Value = new OsuRuleset().RulesetInfo }, - } - } - }); - } - - private void playerJoin() - { - int randomUser = RNG.Next(200000, 500000); - client.AddUser(new User { Id = randomUser, Username = $"user {randomUser}" }); - } - - private void playerLeave() - { - User lastUser = client.Room?.Users.Last().User; - - if (lastUser == null || lastUser == client.LocalUser?.User) - return; - - client.RemoveUser(lastUser); - } - - private void playerKicked() - { - User lastUser = client.Room?.Users.Last().User; - - if (lastUser == null || lastUser == client.LocalUser?.User) - return; - - client.KickUser(lastUser.Id); - } - - private void playerReady() - { - var nextUnready = client.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle); - if (nextUnready != null) - client.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Ready); - } - - private void playerUnready() - { - var nextUnready = client.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Ready); - if (nextUnready != null) - client.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Idle); - } - - private void randomEvent() - { - int eventToPerform = RNG.Next(1, 6); - - switch (eventToPerform) - { - case 1: - playerJoin(); - break; - - case 2: - playerLeave(); - break; - - case 3: - playerKicked(); - break; - - case 4: - playerReady(); - break; - - case 5: - playerUnready(); - break; - } - } - - private void createRoom(Func room) - { - AddUntilStep("wait for lounge", () => multiplayerScreen.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); - AddStep("open room", () => multiplayerScreen.ChildrenOfType().Single().Open(room())); - - AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); - AddWaitStep("wait for transition", 2); - - AddStep("create room", () => - { - InputManager.MoveMouseTo(this.ChildrenOfType().Single()); - InputManager.Click(MouseButton.Left); - }); - - AddUntilStep("wait for join", () => client.Room != null); - - AddRepeatStep("random stuff happens", randomEvent, 30); - - // ensure we have a handful of players so the ready-up sounds good :9 - AddRepeatStep("player joins", playerJoin, 5); - - // all ready - AddUntilStep("all players ready", () => - { - var nextUnready = client.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle); - if (nextUnready != null) - client.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Ready); - - return client.Room?.Users.All(u => u.State == MultiplayerUserState.Ready) == true; - }); - } - - /// - /// Used for the sole purpose of adding as a resolvable dependency. - /// - private class DependenciesScreen : OsuScreen - { - [Cached(typeof(MultiplayerClient))] - public readonly TestMultiplayerClient Client; - - public DependenciesScreen(TestMultiplayerClient client) - { - Client = client; - } - } - - private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer - { - public new TestRequestHandlingMultiplayerRoomManager RoomManager { get; private set; } - - protected override RoomManager CreateRoomManager() => RoomManager = new TestRequestHandlingMultiplayerRoomManager(); - } - } -} From 2b06dacd0e34493461bfdabcedd9984dbf169fd9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 27 Aug 2021 18:57:18 +0900 Subject: [PATCH 1527/2442] Change debounce back to using scheduler Should better allow for adjusting in the future, as well. --- .../Match/MultiplayerReadyButton.cs | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 9c818fc070..b854b6d964 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -8,6 +8,7 @@ using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Threading; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Online.API; @@ -38,12 +39,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private Sample sampleReady; private Sample sampleReadyAll; private Sample sampleUnready; - private double unreadyLastPlaybackTime; private readonly ButtonWithTrianglesExposed button; private int countReady; + private ScheduledDelegate readySampleDelegate; + public MultiplayerReadyButton() { InternalChild = button = new ButtonWithTrianglesExposed @@ -63,8 +65,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match sampleReady = audio.Samples.Get(@"Multiplayer/player-ready"); sampleReadyAll = audio.Samples.Get(@"Multiplayer/player-ready-all"); sampleUnready = audio.Samples.Get(@"Multiplayer/player-unready"); - - unreadyLastPlaybackTime = Time.Current; } protected override void OnRoomUpdated() @@ -117,24 +117,23 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match if (newCountReady == countReady) return; - if (newCountReady > countReady) + readySampleDelegate?.Cancel(); + readySampleDelegate = Schedule(() => { - if (newCountReady == newCountTotal) - sampleReadyAll?.Play(); - else - sampleReady?.Play(); - } - else - { - // debounce sample playback to prevent the mass-unready of game mode changes from deafening players - if (Time.Current - unreadyLastPlaybackTime > 10) + if (newCountReady > countReady) + { + if (newCountReady == newCountTotal) + sampleReadyAll?.Play(); + else + sampleReady?.Play(); + } + else if (newCountReady < countReady) { sampleUnready?.Play(); - unreadyLastPlaybackTime = Time.Current; } - } - countReady = newCountReady; + countReady = newCountReady; + }); } private void updateButtonColour(bool green) From 97f27897b13ba271ca89db32a2126a754629abe8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 27 Aug 2021 18:57:35 +0900 Subject: [PATCH 1528/2442] Add test coverage of mass multiplayer event firing --- .../Visual/Multiplayer/TestSceneMultiplayer.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index d84fe4b6fb..22338bad84 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; @@ -125,6 +126,20 @@ namespace osu.Game.Tests.Visual.Multiplayer return client.Room?.Users.All(u => u.State == MultiplayerUserState.Ready) == true; }); + + AddStep("unready all players at once", () => + { + Debug.Assert(client.Room != null); + + foreach (var u in client.Room.Users) client.ChangeUserState(u.UserID, MultiplayerUserState.Idle); + }); + + AddStep("ready all players at once", () => + { + Debug.Assert(client.Room != null); + + foreach (var u in client.Room.Users) client.ChangeUserState(u.UserID, MultiplayerUserState.Ready); + }); } private void addRandomPlayer() From b7a031619460698faa284ab71b5b4668775ab00d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 27 Aug 2021 13:14:56 +0300 Subject: [PATCH 1529/2442] Shorten test player count to 4 for less steps --- .../Multiplayer/TestSceneMultiSpectatorScreen.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index c3be5c56ef..6f6769bdb8 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -319,18 +319,18 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestPlayersLeaveWhileSpectating() { - start(getPlayerIds(8)); - sendFrames(getPlayerIds(8), 300); + start(getPlayerIds(4)); + sendFrames(getPlayerIds(4), 300); loadSpectateScreen(); - for (int count = 7; count >= 0; count--) + for (int count = 3; count >= 0; count--) { var id = PLAYER_1_ID + count; end(id); - AddUntilStep("player area grayed", () => getInstance(id).Colour != Color4.White); - AddUntilStep("score quit set", () => getLeaderboardScore(id).HasQuit.Value); + AddUntilStep($"{id} area grayed", () => getInstance(id).Colour != Color4.White); + AddUntilStep($"{id} score quit set", () => getLeaderboardScore(id).HasQuit.Value); sendFrames(getPlayerIds(count), 300); } } From 1650fbb8be04d4a0c7deeb805651803d956fcfe1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 27 Aug 2021 13:15:12 +0300 Subject: [PATCH 1530/2442] Add failing test steps --- .../Multiplayer/TestSceneMultiSpectatorScreen.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index 6f6769bdb8..bfcb55ce33 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -333,6 +333,17 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep($"{id} score quit set", () => getLeaderboardScore(id).HasQuit.Value); sendFrames(getPlayerIds(count), 300); } + + Player player = null; + + AddStep($"get {PLAYER_1_ID} player instance", () => player = getInstance(PLAYER_1_ID).ChildrenOfType().Single()); + + start(new[] { PLAYER_1_ID }); + sendFrames(PLAYER_1_ID, 300); + + AddAssert($"{PLAYER_1_ID} player instance still same", () => getInstance(PLAYER_1_ID).ChildrenOfType().Single() == player); + AddAssert($"{PLAYER_1_ID} area still grayed", () => getInstance(PLAYER_1_ID).Colour != Color4.White); + AddAssert($"{PLAYER_1_ID} score quit still set", () => getLeaderboardScore(PLAYER_1_ID).HasQuit.Value); } private void loadSpectateScreen(bool waitForPlayerLoad = true) From 378734a7f8cdd6678fcc42886f99bacfcfefcba6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 27 Aug 2021 13:16:08 +0300 Subject: [PATCH 1531/2442] Separate solo spectator player and "exit on restart" logic to own class --- osu.Game/Screens/Play/SoloSpectator.cs | 2 +- osu.Game/Screens/Play/SoloSpectatorPlayer.cs | 52 +++++++++++++++++++ osu.Game/Screens/Play/SpectatorPlayer.cs | 30 +++-------- .../Screens/Play/SpectatorPlayerLoader.cs | 7 +-- 4 files changed, 61 insertions(+), 30 deletions(-) create mode 100644 osu.Game/Screens/Play/SoloSpectatorPlayer.cs diff --git a/osu.Game/Screens/Play/SoloSpectator.cs b/osu.Game/Screens/Play/SoloSpectator.cs index 820d776e63..4520e2e825 100644 --- a/osu.Game/Screens/Play/SoloSpectator.cs +++ b/osu.Game/Screens/Play/SoloSpectator.cs @@ -211,7 +211,7 @@ namespace osu.Game.Screens.Play Beatmap.Value = gameplayState.Beatmap; Ruleset.Value = gameplayState.Ruleset.RulesetInfo; - this.Push(new SpectatorPlayerLoader(gameplayState.Score)); + this.Push(new SpectatorPlayerLoader(gameplayState.Score, () => new SoloSpectatorPlayer(gameplayState.Score))); } } diff --git a/osu.Game/Screens/Play/SoloSpectatorPlayer.cs b/osu.Game/Screens/Play/SoloSpectatorPlayer.cs new file mode 100644 index 0000000000..969a5bf2b4 --- /dev/null +++ b/osu.Game/Screens/Play/SoloSpectatorPlayer.cs @@ -0,0 +1,52 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Screens; +using osu.Game.Online.Spectator; +using osu.Game.Scoring; + +namespace osu.Game.Screens.Play +{ + public class SoloSpectatorPlayer : SpectatorPlayer + { + private readonly Score score; + + public SoloSpectatorPlayer(Score score, PlayerConfiguration configuration = null) + : base(score, configuration) + { + this.score = score; + } + + [BackgroundDependencyLoader] + private void load() + { + SpectatorClient.OnUserBeganPlaying += userBeganPlaying; + } + + public override bool OnExiting(IScreen next) + { + SpectatorClient.OnUserBeganPlaying -= userBeganPlaying; + + return base.OnExiting(next); + } + + private void userBeganPlaying(int userId, SpectatorState state) + { + if (userId != score.ScoreInfo.UserID) return; + + Schedule(() => + { + if (this.IsCurrentScreen()) this.Exit(); + }); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (SpectatorClient != null) + SpectatorClient.OnUserBeganPlaying -= userBeganPlaying; + } + } +} diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index 1dae28092a..d7e42a9cd1 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -14,16 +14,16 @@ using osu.Game.Screens.Ranking; namespace osu.Game.Screens.Play { - public class SpectatorPlayer : Player + public abstract class SpectatorPlayer : Player { [Resolved] - private SpectatorClient spectatorClient { get; set; } + protected SpectatorClient SpectatorClient { get; private set; } private readonly Score score; protected override bool CheckModsAllowFailure() => false; // todo: better support starting mid-way through beatmap - public SpectatorPlayer(Score score, PlayerConfiguration configuration = null) + protected SpectatorPlayer(Score score, PlayerConfiguration configuration = null) : base(configuration) { this.score = score; @@ -32,8 +32,6 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader] private void load() { - spectatorClient.OnUserBeganPlaying += userBeganPlaying; - AddInternal(new OsuSpriteText { Text = $"Watching {score.ScoreInfo.User.Username} playing live!", @@ -50,7 +48,7 @@ namespace osu.Game.Screens.Play // Start gameplay along with the very first arrival frame (the latest one). score.Replay.Frames.Clear(); - spectatorClient.OnNewFrames += userSentFrames; + SpectatorClient.OnNewFrames += userSentFrames; } private void userSentFrames(int userId, FrameDataBundle bundle) @@ -93,31 +91,17 @@ namespace osu.Game.Screens.Play public override bool OnExiting(IScreen next) { - spectatorClient.OnUserBeganPlaying -= userBeganPlaying; - spectatorClient.OnNewFrames -= userSentFrames; + SpectatorClient.OnNewFrames -= userSentFrames; return base.OnExiting(next); } - private void userBeganPlaying(int userId, SpectatorState state) - { - if (userId != score.ScoreInfo.UserID) return; - - Schedule(() => - { - if (this.IsCurrentScreen()) this.Exit(); - }); - } - protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - if (spectatorClient != null) - { - spectatorClient.OnUserBeganPlaying -= userBeganPlaying; - spectatorClient.OnNewFrames -= userSentFrames; - } + if (SpectatorClient != null) + SpectatorClient.OnNewFrames -= userSentFrames; } } } diff --git a/osu.Game/Screens/Play/SpectatorPlayerLoader.cs b/osu.Game/Screens/Play/SpectatorPlayerLoader.cs index bdd23962dc..10cc36c9a9 100644 --- a/osu.Game/Screens/Play/SpectatorPlayerLoader.cs +++ b/osu.Game/Screens/Play/SpectatorPlayerLoader.cs @@ -11,12 +11,7 @@ namespace osu.Game.Screens.Play { public readonly ScoreInfo Score; - public SpectatorPlayerLoader(Score score) - : this(score, () => new SpectatorPlayer(score)) - { - } - - public SpectatorPlayerLoader(Score score, Func createPlayer) + public SpectatorPlayerLoader(Score score, Func createPlayer) : base(createPlayer) { if (score.Replay == null) From 804ca88d63821e88badff1ff9ff48d76c300bae5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 27 Aug 2021 19:51:51 +0900 Subject: [PATCH 1532/2442] Update framework --- osu.Android.props | 2 +- osu.Game/Audio/PreviewTrackManager.cs | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index f68d7124a9..be29bad31d 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/Audio/PreviewTrackManager.cs b/osu.Game/Audio/PreviewTrackManager.cs index dab5fcbe5f..1de9e1561f 100644 --- a/osu.Game/Audio/PreviewTrackManager.cs +++ b/osu.Game/Audio/PreviewTrackManager.cs @@ -35,7 +35,7 @@ namespace osu.Game.Audio { // this is a temporary solution to get around muting ourselves. // todo: update this once we have a BackgroundTrackManager or similar. - trackStore = new PreviewTrackStore(audioManager.Mixer, new OnlineStore()); + trackStore = new PreviewTrackStore(audioManager.TrackMixer, new OnlineStore()); audio.AddItem(trackStore); trackStore.AddAdjustment(AdjustableProperty.Volume, globalTrackVolumeAdjust); diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8e39c5a3df..21d10d650c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 0e3e87f3c1..62924ba391 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 681a87d4ec516fac98d65d3123ffcb7b636c6ed8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 28 Aug 2021 07:08:06 +0900 Subject: [PATCH 1533/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index be29bad31d..f18400bf2f 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 21d10d650c..a89d57cd1f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 62924ba391..bb4700a081 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From e527bfd4bf4d36cf71a6871828a129760e7efabc Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 28 Aug 2021 02:37:46 +0300 Subject: [PATCH 1534/2442] Move incompatibility icon logic to local player mod select overlays --- .../Overlays/Mods/LocalPlayerModButton.cs | 69 +++++++++++++++++++ .../Mods/LocalPlayerModSelectOverlay.cs | 12 ++++ osu.Game/Overlays/Mods/ModButton.cs | 55 +++------------ osu.Game/Overlays/Mods/ModSection.cs | 10 +-- 4 files changed, 96 insertions(+), 50 deletions(-) create mode 100644 osu.Game/Overlays/Mods/LocalPlayerModButton.cs diff --git a/osu.Game/Overlays/Mods/LocalPlayerModButton.cs b/osu.Game/Overlays/Mods/LocalPlayerModButton.cs new file mode 100644 index 0000000000..10c81da7a6 --- /dev/null +++ b/osu.Game/Overlays/Mods/LocalPlayerModButton.cs @@ -0,0 +1,69 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Play.HUD; +using osu.Game.Utils; +using osuTK; + +namespace osu.Game.Overlays.Mods +{ + public class LocalPlayerModButton : ModButton + { + private readonly CompositeDrawable incompatibleIcon; + + [Resolved] + private Bindable> selectedMods { get; set; } + + public LocalPlayerModButton(Mod mod) + : base(mod) + { + ButtonContent.Add(incompatibleIcon = new IncompatibleIcon + { + Anchor = Anchor.BottomRight, + Origin = Anchor.Centre, + Position = new Vector2(-13), + }); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + selectedMods.BindValueChanged(_ => Scheduler.AddOnce(updateCompatibility), true); + } + + protected override void DisplayMod(Mod mod) + { + base.DisplayMod(mod); + + Scheduler.AddOnce(updateCompatibility); + } + + private void updateCompatibility() + { + var m = SelectedMod ?? Mods.First(); + + bool isIncompatible = false; + + if (selectedMods.Value.Count > 0 && !selectedMods.Value.Contains(m)) + isIncompatible = !ModUtils.CheckCompatibleSet(selectedMods.Value.Append(m)); + + if (isIncompatible) + incompatibleIcon.Show(); + else + incompatibleIcon.Hide(); + } + } +} diff --git a/osu.Game/Overlays/Mods/LocalPlayerModSelectOverlay.cs b/osu.Game/Overlays/Mods/LocalPlayerModSelectOverlay.cs index db76581108..b8e0c27007 100644 --- a/osu.Game/Overlays/Mods/LocalPlayerModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/LocalPlayerModSelectOverlay.cs @@ -14,5 +14,17 @@ namespace osu.Game.Overlays.Mods foreach (var section in ModSectionsContainer.Children) section.DeselectTypes(mod.IncompatibleMods, true, mod); } + + protected override ModSection CreateModSection(ModType type) => new LocalPlayerModSection(type); + + private class LocalPlayerModSection : ModSection + { + public LocalPlayerModSection(ModType type) + : base(type) + { + } + + protected override ModButton CreateModButton(Mod mod) => new LocalPlayerModButton(mod); + } } } diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index 4675eb6bc8..8f6fc734e8 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -11,16 +11,12 @@ using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; using System; -using System.Collections.Generic; using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics.Cursor; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; -using osu.Game.Utils; namespace osu.Game.Overlays.Mods { @@ -33,7 +29,6 @@ namespace osu.Game.Overlays.Mods private ModIcon backgroundIcon; private readonly SpriteText text; private readonly Container iconsContainer; - private readonly CompositeDrawable incompatibleIcon; /// /// Fired when the selection changes. @@ -48,9 +43,6 @@ namespace osu.Game.Overlays.Mods // A selected index of -1 means not selected. private int selectedIndex = -1; - [Resolved] - private Bindable> selectedMods { get; set; } - /// /// Change the selected mod index of this button. /// @@ -109,7 +101,7 @@ namespace osu.Game.Overlays.Mods .RotateTo(rotate_angle * direction) .RotateTo(0f, mod_switch_duration, mod_switch_easing); - Schedule(() => displayMod(newSelection)); + Schedule(() => DisplayMod(newSelection)); } } @@ -138,7 +130,8 @@ namespace osu.Game.Overlays.Mods } private Mod mod; - private readonly Container scaleContainer; + + protected readonly Container ButtonContent; public Mod Mod { @@ -162,7 +155,7 @@ namespace osu.Game.Overlays.Mods if (Mods.Length > 0) { - displayMod(Mods[0]); + DisplayMod(Mods[0]); } } } @@ -173,13 +166,13 @@ namespace osu.Game.Overlays.Mods protected override bool OnMouseDown(MouseDownEvent e) { - scaleContainer.ScaleTo(0.9f, 800, Easing.Out); + ButtonContent.ScaleTo(0.9f, 800, Easing.Out); return base.OnMouseDown(e); } protected override void OnMouseUp(MouseUpEvent e) { - scaleContainer.ScaleTo(1, 500, Easing.OutElastic); + ButtonContent.ScaleTo(1, 500, Easing.OutElastic); // only trigger the event if we are inside the area of the button if (Contains(e.ScreenSpaceMousePosition)) @@ -238,30 +231,13 @@ namespace osu.Game.Overlays.Mods public void Deselect() => changeSelectedIndex(-1); - private void displayMod(Mod mod) + protected virtual void DisplayMod(Mod mod) { if (backgroundIcon != null) backgroundIcon.Mod = foregroundIcon.Mod; foregroundIcon.Mod = mod; text.Text = mod.Name; Colour = mod.HasImplementation ? Color4.White : Color4.Gray; - - Scheduler.AddOnce(updateCompatibility); - } - - private void updateCompatibility() - { - var m = SelectedMod ?? Mods.First(); - - bool isIncompatible = false; - - if (selectedMods.Value.Count > 0 && !selectedMods.Value.Contains(m)) - isIncompatible = !ModUtils.CheckCompatibleSet(selectedMods.Value.Append(m)); - - if (isIncompatible) - incompatibleIcon.Show(); - else - incompatibleIcon.Hide(); } private void createIcons() @@ -307,7 +283,7 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.TopCentre, Children = new Drawable[] { - scaleContainer = new Container + ButtonContent = new Container { Children = new Drawable[] { @@ -317,12 +293,6 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.Centre, Anchor = Anchor.Centre, }, - incompatibleIcon = new IncompatibleIcon - { - Origin = Anchor.Centre, - Anchor = Anchor.BottomRight, - Position = new Vector2(-13), - } }, RelativeSizeAxes = Axes.Both, Origin = Anchor.Centre, @@ -342,14 +312,7 @@ namespace osu.Game.Overlays.Mods Mod = mod; } - protected override void LoadComplete() - { - base.LoadComplete(); - - selectedMods.BindValueChanged(_ => Scheduler.AddOnce(updateCompatibility), true); - } - - public ITooltip GetCustomTooltip() => new ModButtonTooltip(); + public virtual ITooltip GetCustomTooltip() => new ModButtonTooltip(); public object TooltipContent => SelectedMod ?? Mods.FirstOrDefault(); } diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 6e289dc8aa..faad23a4e1 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -51,14 +51,14 @@ namespace osu.Game.Overlays.Mods if (m == null) return new ModButtonEmpty(); - return new ModButton(m) + return CreateModButton(m).With(b => { - SelectionChanged = mod => + b.SelectionChanged = mod => { ModButtonStateChanged(mod); Action?.Invoke(mod); - }, - }; + }; + }); }).ToArray(); modsLoadCts?.Cancel(); @@ -247,6 +247,8 @@ namespace osu.Game.Overlays.Mods Text = text }; + protected virtual ModButton CreateModButton(Mod mod) => new ModButton(mod); + /// /// Play out all remaining animations immediately to leave mods in a good (final) state. /// From 589f2863ca16b75a959a873d1f4c6a1517107ede Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 28 Aug 2021 02:38:45 +0300 Subject: [PATCH 1535/2442] Move incompatibility tooltip logic to local player mod select overlays This one turned out to be a bit more involved, due to tooltips being shared and having the potential of being used somewhere where it shouldn't be, due to the same content type matching. That's the reason I've defined a protected `TargetContentType`, to be able to separate "local player mod tooltips" and regular mod tooltips apart. Definitely unsure about the solution, but that's as far as I can think of right now. --- .../Overlays/Mods/LocalPlayerModButton.cs | 51 ++++++++++++++++ osu.Game/Overlays/Mods/ModButton.cs | 2 +- osu.Game/Overlays/Mods/ModButtonTooltip.cs | 58 +++++-------------- 3 files changed, 68 insertions(+), 43 deletions(-) diff --git a/osu.Game/Overlays/Mods/LocalPlayerModButton.cs b/osu.Game/Overlays/Mods/LocalPlayerModButton.cs index 10c81da7a6..9427c3e63e 100644 --- a/osu.Game/Overlays/Mods/LocalPlayerModButton.cs +++ b/osu.Game/Overlays/Mods/LocalPlayerModButton.cs @@ -65,5 +65,56 @@ namespace osu.Game.Overlays.Mods else incompatibleIcon.Hide(); } + + public override ITooltip GetCustomTooltip() => new LocalPlayerModButtonTooltip(); + + private class LocalPlayerModButtonTooltip : ModButtonTooltip + { + private readonly OsuSpriteText incompatibleText; + + private readonly Bindable> incompatibleMods = new Bindable>(); + + [Resolved] + private Bindable ruleset { get; set; } + + public LocalPlayerModButtonTooltip() + { + AddRange(new Drawable[] + { + incompatibleText = new OsuSpriteText + { + Margin = new MarginPadding { Top = 5 }, + Font = OsuFont.GetFont(weight: FontWeight.Regular), + Text = "Incompatible with:" + }, + new ModDisplay + { + Current = incompatibleMods, + ExpansionMode = ExpansionMode.AlwaysExpanded, + Scale = new Vector2(0.7f) + } + }); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + incompatibleText.Colour = colours.BlueLight; + } + + protected override Type TargetContentType => typeof(LocalPlayerModButton); + + protected override void UpdateDisplay(Mod mod) + { + base.UpdateDisplay(mod); + + var incompatibleTypes = mod.IncompatibleMods; + + var allMods = ruleset.Value.CreateInstance().GetAllMods(); + + incompatibleMods.Value = allMods.Where(m => m.GetType() != mod.GetType() && incompatibleTypes.Any(t => t.IsInstanceOfType(m))).ToList(); + incompatibleText.Text = incompatibleMods.Value.Any() ? "Incompatible with:" : "Compatible with all mods"; + } + } } } diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index 8f6fc734e8..cc8acb7513 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -314,6 +314,6 @@ namespace osu.Game.Overlays.Mods public virtual ITooltip GetCustomTooltip() => new ModButtonTooltip(); - public object TooltipContent => SelectedMod ?? Mods.FirstOrDefault(); + public object TooltipContent => this; } } diff --git a/osu.Game/Overlays/Mods/ModButtonTooltip.cs b/osu.Game/Overlays/Mods/ModButtonTooltip.cs index 666ed07e28..125357ea44 100644 --- a/osu.Game/Overlays/Mods/ModButtonTooltip.cs +++ b/osu.Game/Overlays/Mods/ModButtonTooltip.cs @@ -1,19 +1,16 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; +using System; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; -using osu.Game.Screens.Play.HUD; using osuTK; namespace osu.Game.Overlays.Mods @@ -22,12 +19,8 @@ namespace osu.Game.Overlays.Mods { private readonly OsuSpriteText descriptionText; private readonly Box background; - private readonly OsuSpriteText incompatibleText; - private readonly Bindable> incompatibleMods = new Bindable>(); - - [Resolved] - private Bindable ruleset { get; set; } + protected override Container Content { get; } public ModButtonTooltip() { @@ -35,13 +28,13 @@ namespace osu.Game.Overlays.Mods Masking = true; CornerRadius = 5; - Children = new Drawable[] + InternalChildren = new Drawable[] { background = new Box { RelativeSizeAxes = Axes.Both }, - new FillFlowContainer + Content = new FillFlowContainer { AutoSizeAxes = Axes.Both, Direction = FillDirection.Vertical, @@ -51,19 +44,7 @@ namespace osu.Game.Overlays.Mods descriptionText = new OsuSpriteText { Font = OsuFont.GetFont(weight: FontWeight.Regular), - Margin = new MarginPadding { Bottom = 5 } }, - incompatibleText = new OsuSpriteText - { - Font = OsuFont.GetFont(weight: FontWeight.Regular), - Text = "Incompatible with:" - }, - new ModDisplay - { - Current = incompatibleMods, - ExpansionMode = ExpansionMode.AlwaysExpanded, - Scale = new Vector2(0.7f) - } } }, }; @@ -74,7 +55,6 @@ namespace osu.Game.Overlays.Mods { background.Colour = colours.Gray3; descriptionText.Colour = colours.BlueLighter; - incompatibleText.Colour = colours.BlueLight; } protected override void PopIn() => this.FadeIn(200, Easing.OutQuint); @@ -82,34 +62,28 @@ namespace osu.Game.Overlays.Mods private Mod lastMod; - public bool SetContent(object content) + protected virtual Type TargetContentType => typeof(ModButton); + + public virtual bool SetContent(object content) { - if (!(content is Mod mod)) + if (!(content is ModButton button) || content.GetType() != TargetContentType) return false; + var mod = button.SelectedMod ?? button.Mods.First(); + if (mod.Equals(lastMod)) return true; lastMod = mod; - descriptionText.Text = mod.Description; - - var incompatibleTypes = mod.IncompatibleMods; - - var allMods = ruleset.Value.CreateInstance().GetAllMods(); - - incompatibleMods.Value = allMods.Where(m => m.GetType() != mod.GetType() && incompatibleTypes.Any(t => t.IsInstanceOfType(m))).ToList(); - - if (!incompatibleMods.Value.Any()) - { - incompatibleText.Text = "Compatible with all mods"; - return true; - } - - incompatibleText.Text = "Incompatible with:"; - + UpdateDisplay(mod); return true; } + protected virtual void UpdateDisplay(Mod mod) + { + descriptionText.Text = mod.Description; + } + public void Move(Vector2 pos) => Position = pos; } } From 7fbeb9ecc7a78fed1ef88d45b42eed54d13a19d7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 28 Aug 2021 14:15:47 +0900 Subject: [PATCH 1536/2442] Add failing test coverage for tournament startup states --- .../Screens/TestSceneGameplayScreen.cs | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs index 2e34c39370..7002db781e 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs @@ -4,8 +4,10 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Tournament.Components; +using osu.Game.Tournament.IPC; using osu.Game.Tournament.Screens.Gameplay; using osu.Game.Tournament.Screens.Gameplay.Components; @@ -16,16 +18,18 @@ namespace osu.Game.Tournament.Tests.Screens [Cached] private TournamentMatchChatDisplay chat = new TournamentMatchChatDisplay { Width = 0.5f }; - [BackgroundDependencyLoader] - private void load() + [Test] + public void TestStartupState([Values] TourneyState state) { - Add(new GameplayScreen()); - Add(chat); + AddStep("set state", () => IPCInfo.State.Value = state); + createScreen(); } [Test] public void TestWarmup() { + createScreen(); + checkScoreVisibility(false); toggleWarmup(); @@ -35,6 +39,20 @@ namespace osu.Game.Tournament.Tests.Screens checkScoreVisibility(false); } + private void createScreen() + { + AddStep("setup screen", () => + { + Remove(chat); + + Children = new Drawable[] + { + new GameplayScreen(), + chat, + }; + }); + } + private void checkScoreVisibility(bool visible) => AddUntilStep($"scores {(visible ? "shown" : "hidden")}", () => this.ChildrenOfType().All(score => score.Alpha == (visible ? 1 : 0))); From e9b97f7937cd842ae410a5b318c76fb5aa3b791c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 28 Aug 2021 14:15:42 +0900 Subject: [PATCH 1537/2442] Fix tournament crashing when osu!(stable) is at ranking screen at startup --- .../Screens/Gameplay/GameplayScreen.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 540b45eb56..44a6006553 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -124,9 +124,6 @@ namespace osu.Game.Tournament.Screens.Gameplay } }); - State.BindTo(ipc.State); - State.BindValueChanged(stateChanged, true); - ladder.ChromaKeyWidth.BindValueChanged(width => chroma.Width = width.NewValue, true); warmup.BindValueChanged(w => @@ -136,6 +133,14 @@ namespace osu.Game.Tournament.Screens.Gameplay }, true); } + protected override void LoadComplete() + { + base.LoadComplete(); + + State.BindTo(ipc.State); + State.BindValueChanged(stateChanged, true); + } + protected override void CurrentMatchChanged(ValueChangedEvent match) { base.CurrentMatchChanged(match); From 303c70791d646858a8c53c9ed32bce7291bf8858 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 28 Aug 2021 16:22:01 +0900 Subject: [PATCH 1538/2442] Add more failing test coverage for `null` `CurrentMatch` --- .../Screens/TestSceneGameplayScreen.cs | 8 ++++++++ .../Screens/TestSceneTeamWinScreen.cs | 5 +++-- osu.Game.Tournament.Tests/TournamentTestScene.cs | 12 +++++++++--- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs index 7002db781e..6879a71f1d 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneGameplayScreen.cs @@ -25,6 +25,14 @@ namespace osu.Game.Tournament.Tests.Screens createScreen(); } + [Test] + public void TestStartupStateNoCurrentMatch([Values] TourneyState state) + { + AddStep("set null current", () => Ladder.CurrentMatch.Value = null); + AddStep("set state", () => IPCInfo.State.Value = state); + createScreen(); + } + [Test] public void TestWarmup() { diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneTeamWinScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneTeamWinScreen.cs index 3ca58dcaf4..4de576b3b0 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneTeamWinScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneTeamWinScreen.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; +using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Tournament.Screens.TeamWin; @@ -10,8 +11,8 @@ namespace osu.Game.Tournament.Tests.Screens { public class TestSceneTeamWinScreen : TournamentTestScene { - [BackgroundDependencyLoader] - private void load() + [Test] + public void TestBasic() { var match = Ladder.CurrentMatch.Value; diff --git a/osu.Game.Tournament.Tests/TournamentTestScene.cs b/osu.Game.Tournament.Tests/TournamentTestScene.cs index 1fa0ffc8e9..93e1e018a5 100644 --- a/osu.Game.Tournament.Tests/TournamentTestScene.cs +++ b/osu.Game.Tournament.Tests/TournamentTestScene.cs @@ -19,6 +19,8 @@ namespace osu.Game.Tournament.Tests { public abstract class TournamentTestScene : OsuTestScene { + private TournamentMatch match; + [Cached] protected LadderInfo Ladder { get; private set; } = new LadderInfo(); @@ -33,19 +35,23 @@ namespace osu.Game.Tournament.Tests { Ladder.Ruleset.Value ??= rulesetStore.AvailableRulesets.First(); - TournamentMatch match = CreateSampleMatch(); + match = CreateSampleMatch(); Ladder.Rounds.Add(match.Round.Value); Ladder.Matches.Add(match); Ladder.Teams.Add(match.Team1.Value); Ladder.Teams.Add(match.Team2.Value); - Ladder.CurrentMatch.Value = match; - Ruleset.BindTo(Ladder.Ruleset); Dependencies.CacheAs(new StableInfo(storage)); } + [SetUpSteps] + public virtual void SetUpSteps() + { + AddStep("set current match", () => Ladder.CurrentMatch.Value = match); + } + public static TournamentMatch CreateSampleMatch() => new TournamentMatch { Team1 = From 6ef096001e00b725810d0987703adb795b874fc1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 28 Aug 2021 16:22:12 +0900 Subject: [PATCH 1539/2442] Fix several cases of incorrect handling of `CurrentMatch` nullability --- .../Screens/Gameplay/Components/MatchRoundDisplay.cs | 2 +- osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/MatchRoundDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/MatchRoundDisplay.cs index 87793f7e1b..2f0e4b5e87 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/MatchRoundDisplay.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/MatchRoundDisplay.cs @@ -20,6 +20,6 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components } private void matchChanged(ValueChangedEvent match) => - Text.Text = match.NewValue.Round.Value?.Name.Value ?? "Unknown Round"; + Text.Text = match.NewValue?.Round.Value?.Name.Value ?? "Unknown Round"; } } diff --git a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs index 44a6006553..7e7c719152 100644 --- a/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs +++ b/osu.Game.Tournament/Screens/Gameplay/GameplayScreen.cs @@ -164,7 +164,7 @@ namespace osu.Game.Tournament.Screens.Gameplay { if (state.NewValue == TourneyState.Ranking) { - if (warmup.Value) return; + if (warmup.Value || CurrentMatch.Value == null) return; if (ipc.Score1.Value > ipc.Score2.Value) CurrentMatch.Value.Team1Score.Value++; From b008a86d8c0904a05a847ea27101c98a3336a825 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 28 Aug 2021 16:35:54 +0900 Subject: [PATCH 1540/2442] Remove unused using statement --- osu.Game.Tournament.Tests/Screens/TestSceneTeamWinScreen.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneTeamWinScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneTeamWinScreen.cs index 4de576b3b0..2c8610b414 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneTeamWinScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneTeamWinScreen.cs @@ -3,7 +3,6 @@ using System.Linq; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Tournament.Screens.TeamWin; From d37df6afeca4e2a1fff47a521dec9d7ab6997adc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 28 Aug 2021 09:44:54 +0200 Subject: [PATCH 1541/2442] Fix test failing after BDL -> `[Test]` change --- .../Screens/TestSceneTeamWinScreen.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneTeamWinScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneTeamWinScreen.cs index 2c8610b414..d07cc4c431 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneTeamWinScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneTeamWinScreen.cs @@ -13,16 +13,19 @@ namespace osu.Game.Tournament.Tests.Screens [Test] public void TestBasic() { - var match = Ladder.CurrentMatch.Value; + AddStep("set up match", () => + { + var match = Ladder.CurrentMatch.Value; - match.Round.Value = Ladder.Rounds.FirstOrDefault(g => g.Name.Value == "Finals"); - match.Completed.Value = true; + match.Round.Value = Ladder.Rounds.FirstOrDefault(g => g.Name.Value == "Finals"); + match.Completed.Value = true; + }); - Add(new TeamWinScreen + AddStep("create screen", () => Add(new TeamWinScreen { FillMode = FillMode.Fit, FillAspectRatio = 16 / 9f - }); + })); } } } From eb90cedc9bafc230180bd5fa260b1067859f1d9d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 28 Aug 2021 20:09:35 +0300 Subject: [PATCH 1542/2442] Fix editor screen test scenes not updated to show their screens --- osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs | 6 +++++- osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs | 7 ++++++- osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs | 6 +++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs index 6f5655006e..4813598c9d 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs @@ -3,6 +3,7 @@ using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Beatmaps; @@ -30,7 +31,10 @@ namespace osu.Game.Tests.Visual.Editing { Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap); - Child = new ComposeScreen(); + Child = new ComposeScreen + { + State = { Value = Visibility.Visible }, + }; } } } diff --git a/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs index 62e12158ab..9253023c9a 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs @@ -3,6 +3,7 @@ using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Screens.Edit; @@ -26,7 +27,11 @@ namespace osu.Game.Tests.Visual.Editing private void load() { Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap); - Child = new SetupScreen(); + + Child = new SetupScreen + { + State = { Value = Visibility.Visible }, + }; } } } diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs index b82e776164..f961fff1e5 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs @@ -3,6 +3,7 @@ using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu; using osu.Game.Screens.Edit; @@ -30,7 +31,10 @@ namespace osu.Game.Tests.Visual.Editing Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap); Beatmap.Disabled = true; - Child = new TimingScreen(); + Child = new TimingScreen + { + State = { Value = Visibility.Visible }, + }; } protected override void Dispose(bool isDisposing) From 7457480b50c9a69c01287feec8dff1b08b6b15db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 28 Aug 2021 18:47:34 +0200 Subject: [PATCH 1543/2442] Add local popover container to lounge subscreen --- osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index cf3d76a3fb..cca1394b6d 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -11,6 +11,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Framework.Logging; @@ -72,6 +73,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private readonly Bindable filter = new Bindable(new FilterCriteria()); private readonly IBindable operationInProgress = new Bindable(); private readonly IBindable isIdle = new BindableBool(); + private PopoverContainer popoverContainer; private LoadingLayer loadingLayer; private RoomsContainer roomsContainer; private SearchTextBox searchTextBox; @@ -90,7 +92,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge InternalChildren = new Drawable[] { ListingPollingComponent = CreatePollingComponent().With(c => c.Filter.BindTarget = filter), - new Container + popoverContainer = new PopoverContainer { Name = @"Rooms area", RelativeSizeAxes = Axes.Both, @@ -285,7 +287,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge searchTextBox.HoldFocus = false; // ensure any password prompt is dismissed. - this.HidePopover(); + popoverContainer.HidePopover(); } public void Join(Room room, string password) => Schedule(() => From e94d96f25093a2b32511554548e1a1314db6ce45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 28 Aug 2021 18:49:24 +0200 Subject: [PATCH 1544/2442] Add local popover container to editor screens --- osu.Game/Screens/Edit/EditorScreen.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/EditorScreen.cs b/osu.Game/Screens/Edit/EditorScreen.cs index d7fe5207d0..5665e6cb55 100644 --- a/osu.Game/Screens/Edit/EditorScreen.cs +++ b/osu.Game/Screens/Edit/EditorScreen.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; namespace osu.Game.Screens.Edit { @@ -28,7 +29,7 @@ namespace osu.Game.Screens.Edit Origin = Anchor.Centre; RelativeSizeAxes = Axes.Both; - InternalChild = content = new Container { RelativeSizeAxes = Axes.Both }; + InternalChild = content = new PopoverContainer { RelativeSizeAxes = Axes.Both }; } protected override void PopIn() From fcc3e57d5dd86d82f0d9c56e6c4c5fa67fe3ef9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 28 Aug 2021 18:50:13 +0200 Subject: [PATCH 1545/2442] Move overlay colour provider up to editor screen --- osu.Game/Screens/Edit/EditorRoundedScreen.cs | 5 ----- osu.Game/Screens/Edit/EditorScreen.cs | 6 ++++++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorRoundedScreen.cs b/osu.Game/Screens/Edit/EditorRoundedScreen.cs index c6ced02021..b271a145f5 100644 --- a/osu.Game/Screens/Edit/EditorRoundedScreen.cs +++ b/osu.Game/Screens/Edit/EditorRoundedScreen.cs @@ -6,7 +6,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; -using osu.Game.Overlays; namespace osu.Game.Screens.Edit { @@ -17,9 +16,6 @@ namespace osu.Game.Screens.Edit [Resolved] private OsuColour colours { get; set; } - [Cached] - protected readonly OverlayColourProvider ColourProvider; - private Container roundedContent; protected override Container Content => roundedContent; @@ -27,7 +23,6 @@ namespace osu.Game.Screens.Edit public EditorRoundedScreen(EditorScreenMode mode) : base(mode) { - ColourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); } [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/Edit/EditorScreen.cs b/osu.Game/Screens/Edit/EditorScreen.cs index 5665e6cb55..2810f78835 100644 --- a/osu.Game/Screens/Edit/EditorScreen.cs +++ b/osu.Game/Screens/Edit/EditorScreen.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; +using osu.Game.Overlays; namespace osu.Game.Screens.Edit { @@ -16,6 +17,9 @@ namespace osu.Game.Screens.Edit [Resolved] protected EditorBeatmap EditorBeatmap { get; private set; } + [Cached] + protected readonly OverlayColourProvider ColourProvider; + protected override Container Content => content; private readonly Container content; @@ -29,6 +33,8 @@ namespace osu.Game.Screens.Edit Origin = Anchor.Centre; RelativeSizeAxes = Axes.Both; + ColourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); + InternalChild = content = new PopoverContainer { RelativeSizeAxes = Axes.Both }; } From d9db1ecee9c6183424b86881b099d732933a424c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 28 Aug 2021 18:51:12 +0200 Subject: [PATCH 1546/2442] Remove game-global popover container --- osu.Game/OsuGameBase.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index f2d575550a..69f6bc1b7b 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -13,7 +13,6 @@ using osu.Framework.Development; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Game.Beatmaps; @@ -342,11 +341,7 @@ namespace osu.Game globalBindings = new GlobalActionContainer(this) }; - MenuCursorContainer.Child = new PopoverContainer - { - RelativeSizeAxes = Axes.Both, - Child = content = new OsuTooltipContainer(MenuCursorContainer.Cursor) { RelativeSizeAxes = Axes.Both } - }; + MenuCursorContainer.Child = content = new OsuTooltipContainer(MenuCursorContainer.Cursor) { RelativeSizeAxes = Axes.Both }; base.Content.Add(CreateScalingContainer().WithChildren(mainContent)); From 2efe82a18db9c46d2502852201fea28dfb114e73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 28 Aug 2021 20:20:42 +0200 Subject: [PATCH 1547/2442] Remove popover container from manual input manager test scene --- .../TestSceneLabelledColourPalette.cs | 21 ++++++++++++------- .../Visual/OsuManualInputManagerTestScene.cs | 7 +------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs index 6fafb8f87a..e1ea02ba67 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledColourPalette.cs @@ -5,6 +5,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Graphics.Cursor; @@ -55,20 +56,24 @@ namespace osu.Game.Tests.Visual.UserInterface { AddStep("create component", () => { - Child = new OsuContextMenuContainer + Child = new PopoverContainer { RelativeSizeAxes = Axes.Both, - Child = new Container + Child = new OsuContextMenuContainer { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Width = 500, - AutoSizeAxes = Axes.Y, - Child = component = new LabelledColourPalette + RelativeSizeAxes = Axes.Both, + Child = new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, - ColourNamePrefix = "My colour #" + Width = 500, + AutoSizeAxes = Axes.Y, + Child = component = new LabelledColourPalette + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + ColourNamePrefix = "My colour #" + } } } }; diff --git a/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs b/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs index c5e2e67eaf..752794d25a 100644 --- a/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs +++ b/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs @@ -3,7 +3,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Testing.Input; using osu.Game.Graphics.Cursor; @@ -35,11 +34,7 @@ namespace osu.Game.Tests.Visual { MenuCursorContainer cursorContainer; - CompositeDrawable mainContent = new PopoverContainer - { - RelativeSizeAxes = Axes.Both, - Child = cursorContainer = new MenuCursorContainer { RelativeSizeAxes = Axes.Both, } - }; + CompositeDrawable mainContent = cursorContainer = new MenuCursorContainer { RelativeSizeAxes = Axes.Both }; cursorContainer.Child = content = new OsuTooltipContainer(cursorContainer.Cursor) { From 38912bfc1663db6ac5e278bc3a1b50ae1f5089bc Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sat, 28 Aug 2021 20:13:01 -0700 Subject: [PATCH 1548/2442] Fix floating overlays not closing when clicking some empty area of the toolbar --- osu.Game/OsuGame.cs | 26 +++++++++++++++--------- osu.Game/Overlays/LoginOverlay.cs | 13 ------------ osu.Game/Overlays/NotificationOverlay.cs | 13 ------------ osu.Game/Overlays/NowPlayingOverlay.cs | 6 ------ osu.Game/Overlays/SettingsPanel.cs | 6 ------ 5 files changed, 16 insertions(+), 48 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 4d952c39c6..a584644fc9 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -104,6 +104,8 @@ namespace osu.Game protected Container ScreenOffsetContainer { get; private set; } + private Container overlayOffsetContainer; + [Resolved] private FrameworkConfigManager frameworkConfig { get; set; } @@ -120,7 +122,7 @@ namespace osu.Game public virtual StableStorage GetStorageForStableInstall() => null; - public float ToolbarOffset => (Toolbar?.Position.Y ?? 0) + (Toolbar?.DrawHeight ?? 0); + private float toolbarOffset => (Toolbar?.Position.Y ?? 0) + (Toolbar?.DrawHeight ?? 0); private IdleTracker idleTracker; @@ -692,9 +694,16 @@ namespace osu.Game }, } }, - overlayContent = new Container { RelativeSizeAxes = Axes.Both }, - rightFloatingOverlayContent = new Container { RelativeSizeAxes = Axes.Both }, - leftFloatingOverlayContent = new Container { RelativeSizeAxes = Axes.Both }, + overlayOffsetContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + overlayContent = new Container { RelativeSizeAxes = Axes.Both }, + rightFloatingOverlayContent = new Container { RelativeSizeAxes = Axes.Both }, + leftFloatingOverlayContent = new Container { RelativeSizeAxes = Axes.Both }, + } + }, topMostOverlayContent = new Container { RelativeSizeAxes = Axes.Both }, idleTracker, new ConfineMouseTracker() @@ -731,7 +740,6 @@ namespace osu.Game loadComponentSingleFile(Notifications.With(d => { - d.GetToolbarHeight = () => ToolbarOffset; d.Anchor = Anchor.TopRight; d.Origin = Anchor.TopRight; }), rightFloatingOverlayContent.Add, true); @@ -757,7 +765,7 @@ namespace osu.Game loadComponentSingleFile(channelManager = new ChannelManager(), AddInternal, true); loadComponentSingleFile(chatOverlay = new ChatOverlay(), overlayContent.Add, true); loadComponentSingleFile(new MessageNotifier(), AddInternal, true); - loadComponentSingleFile(Settings = new SettingsOverlay { GetToolbarHeight = () => ToolbarOffset }, leftFloatingOverlayContent.Add, true); + loadComponentSingleFile(Settings = new SettingsOverlay(), leftFloatingOverlayContent.Add, true); var changelogOverlay = loadComponentSingleFile(new ChangelogOverlay(), overlayContent.Add, true); loadComponentSingleFile(userProfile = new UserProfileOverlay(), overlayContent.Add, true); loadComponentSingleFile(beatmapSetOverlay = new BeatmapSetOverlay(), overlayContent.Add, true); @@ -766,14 +774,12 @@ namespace osu.Game loadComponentSingleFile(new LoginOverlay { - GetToolbarHeight = () => ToolbarOffset, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, }, rightFloatingOverlayContent.Add, true); loadComponentSingleFile(new NowPlayingOverlay { - GetToolbarHeight = () => ToolbarOffset, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, }, rightFloatingOverlayContent.Add, true); @@ -1013,8 +1019,8 @@ namespace osu.Game { base.UpdateAfterChildren(); - ScreenOffsetContainer.Padding = new MarginPadding { Top = ToolbarOffset }; - overlayContent.Padding = new MarginPadding { Top = ToolbarOffset }; + ScreenOffsetContainer.Padding = new MarginPadding { Top = toolbarOffset }; + overlayOffsetContainer.Padding = new MarginPadding { Top = toolbarOffset }; var horizontalOffset = 0f; diff --git a/osu.Game/Overlays/LoginOverlay.cs b/osu.Game/Overlays/LoginOverlay.cs index d0411ba9e7..e7caaa3aca 100644 --- a/osu.Game/Overlays/LoginOverlay.cs +++ b/osu.Game/Overlays/LoginOverlay.cs @@ -10,7 +10,6 @@ using osuTK.Graphics; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; -using System; namespace osu.Game.Overlays { @@ -20,11 +19,6 @@ namespace osu.Game.Overlays private const float transition_time = 400; - /// - /// Provide a source for the toolbar height. - /// - public Func GetToolbarHeight; - public LoginOverlay() { AutoSizeAxes = Axes.Both; @@ -94,12 +88,5 @@ namespace osu.Game.Overlays settingsSection.Bounding = false; this.FadeOut(transition_time); } - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 }; - } } } diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index e3956089c2..2175e17da9 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics.Containers; using osu.Game.Overlays.Notifications; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.Containers; -using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Localisation; @@ -30,11 +29,6 @@ namespace osu.Game.Overlays private FlowContainer sections; - /// - /// Provide a source for the toolbar height. - /// - public Func GetToolbarHeight; - [BackgroundDependencyLoader] private void load() { @@ -168,12 +162,5 @@ namespace osu.Game.Overlays updateCounts(); } - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 }; - } } } diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index f88be91c01..5619d7b38a 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -55,11 +55,6 @@ namespace osu.Game.Overlays protected override string PopInSampleName => "UI/now-playing-pop-in"; protected override string PopOutSampleName => "UI/now-playing-pop-out"; - /// - /// Provide a source for the toolbar height. - /// - public Func GetToolbarHeight; - [Resolved] private MusicController musicController { get; set; } @@ -246,7 +241,6 @@ namespace osu.Game.Overlays base.UpdateAfterChildren(); Height = dragContainer.Height; - dragContainer.Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 }; } protected override void Update() diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index 5589786169..bda4bb5ece 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -54,11 +54,6 @@ namespace osu.Game.Overlays protected override string PopInSampleName => "UI/settings-pop-in"; - /// - /// Provide a source for the toolbar height. - /// - public Func GetToolbarHeight; - private readonly bool showSidebar; private LoadingLayer loading; @@ -193,7 +188,6 @@ namespace osu.Game.Overlays base.UpdateAfterChildren(); ContentContainer.Margin = new MarginPadding { Left = Sidebar?.DrawWidth ?? 0 }; - Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 }; } private const double fade_in_duration = 1000; From 9a5445bdeda54f9d40a48e20a556ebd5dea0d811 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sat, 28 Aug 2021 22:25:13 -0700 Subject: [PATCH 1549/2442] Fix overlays closing when clicking any empty area of the toolbar instead --- osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs | 4 ++-- osu.Game/Overlays/Toolbar/Toolbar.cs | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index b9b098df80..0e635d26c2 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -75,14 +75,14 @@ namespace osu.Game.Graphics.Containers protected override bool OnMouseDown(MouseDownEvent e) { - closeOnMouseUp = !base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition); + closeOnMouseUp = !base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition) && (game?.Toolbar.IsHovered == false); return base.OnMouseDown(e); } protected override void OnMouseUp(MouseUpEvent e) { - if (closeOnMouseUp && !base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition)) + if (closeOnMouseUp && !base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition) && (game?.Toolbar.IsHovered == false)) Hide(); base.OnMouseUp(e); diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 3d88171ba7..918e3b7105 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -41,6 +41,9 @@ namespace osu.Game.Overlays.Toolbar // Toolbar and its components need keyboard input even when hidden. public override bool PropagateNonPositionalInputSubTree => true; + // IsHovered is used + public override bool HandlePositionalInput => true; + public Toolbar() { RelativeSizeAxes = Axes.X; @@ -140,12 +143,13 @@ namespace osu.Game.Overlays.Toolbar protected override bool OnHover(HoverEvent e) { gradientBackground.FadeIn(transition_time, Easing.OutQuint); - return true; + return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { gradientBackground.FadeOut(transition_time, Easing.OutQuint); + base.OnHoverLost(e); } } From e374ef163de240c91bff458fd59e10b62e2d91e7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 29 Aug 2021 15:00:28 +0300 Subject: [PATCH 1550/2442] Update localisable formattable extensions usages inline with framework change --- osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs | 2 +- osu.Game/Overlays/BeatmapSet/BasicStats.cs | 1 + osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs | 2 +- osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs | 1 + osu.Game/Overlays/BeatmapSet/SuccessRate.cs | 2 +- osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs | 1 + osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs | 1 + .../Profile/Header/Components/ProfileHeaderStatisticsButton.cs | 2 +- osu.Game/Overlays/Profile/Header/Components/RankGraph.cs | 1 + osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs | 1 + osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs | 1 + osu.Game/Overlays/Profile/Sections/CounterPill.cs | 2 +- .../Overlays/Profile/Sections/Historical/ProfileLineChart.cs | 1 + .../Overlays/Profile/Sections/Historical/UserHistoryGraph.cs | 1 + osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs | 1 + .../Profile/Sections/Ranks/DrawableProfileWeightedScore.cs | 2 +- osu.Game/Overlays/Rankings/SpotlightSelector.cs | 1 + osu.Game/Overlays/Rankings/Tables/CountriesTable.cs | 2 +- osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs | 2 +- osu.Game/Overlays/Rankings/Tables/RankingsTable.cs | 1 + osu.Game/Overlays/Rankings/Tables/ScoresTable.cs | 2 +- osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs | 1 + osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs | 1 + osu.Game/Screens/Select/Details/UserRatings.cs | 2 +- osu.Game/Utils/FormatUtils.cs | 1 + 25 files changed, 25 insertions(+), 10 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs index c239fda455..dde7680989 100644 --- a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs +++ b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs @@ -4,12 +4,12 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; diff --git a/osu.Game/Overlays/BeatmapSet/BasicStats.cs b/osu.Game/Overlays/BeatmapSet/BasicStats.cs index 2dcb2f1777..5a6cde8229 100644 --- a/osu.Game/Overlays/BeatmapSet/BasicStats.cs +++ b/osu.Game/Overlays/BeatmapSet/BasicStats.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs index b16fb76ec3..60e341d2ac 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs @@ -6,12 +6,12 @@ using System.Linq; using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; -using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs index c934020059..7704fa24df 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; diff --git a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs index b1e9abe3aa..cde4589c98 100644 --- a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs +++ b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs @@ -2,9 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; diff --git a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs index f15fa2705a..180a288729 100644 --- a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; diff --git a/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs index 877637be22..2c8c421eba 100644 --- a/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs +++ b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; diff --git a/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs index 1235836aac..b098f9f840 100644 --- a/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderStatisticsButton.cs @@ -1,10 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK; diff --git a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs index 74a25591b4..e8bce404e1 100644 --- a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using Humanizer; using osu.Framework.Bindables; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Graphics; diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs index 9e52751904..8ca6961950 100644 --- a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index 438f52a2ce..cf930e985c 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; diff --git a/osu.Game/Overlays/Profile/Sections/CounterPill.cs b/osu.Game/Overlays/Profile/Sections/CounterPill.cs index 34211b40b7..bd6cb4d09b 100644 --- a/osu.Game/Overlays/Profile/Sections/CounterPill.cs +++ b/osu.Game/Overlays/Profile/Sections/CounterPill.cs @@ -8,7 +8,7 @@ using osu.Game.Graphics; using osu.Framework.Bindables; using osu.Game.Graphics.Sprites; using osu.Framework.Allocation; -using osu.Framework.Localisation; +using osu.Framework.Extensions.LocalisationExtensions; namespace osu.Game.Overlays.Profile.Sections { diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index 449b1da35d..a75235359a 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -9,6 +9,7 @@ using System.Linq; using osu.Game.Graphics.Sprites; using osu.Framework.Utils; using osu.Framework.Allocation; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Game.Graphics; using osu.Framework.Graphics.Shapes; using osuTK; diff --git a/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs b/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs index ac94f0fc87..d25c53b5ec 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Localisation; using static osu.Game.Users.User; diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs index eb55a0a78d..762716efab 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs @@ -12,6 +12,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Users; using osu.Framework.Allocation; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Game.Resources.Localisation.Web; using osu.Framework.Localisation; diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs index 4e4a665a60..f77464ecb9 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs @@ -1,9 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Resources.Localisation.Web; diff --git a/osu.Game/Overlays/Rankings/SpotlightSelector.cs b/osu.Game/Overlays/Rankings/SpotlightSelector.cs index 5309778a47..0f071883ca 100644 --- a/osu.Game/Overlays/Rankings/SpotlightSelector.cs +++ b/osu.Game/Overlays/Rankings/SpotlightSelector.cs @@ -16,6 +16,7 @@ using System.Collections.Generic; using osu.Framework.Graphics.UserInterface; using osu.Game.Online.API.Requests; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; diff --git a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs index 85a317728f..a908380e95 100644 --- a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs @@ -9,8 +9,8 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using System.Collections.Generic; using osu.Framework.Allocation; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Game.Resources.Localisation.Web; -using osu.Framework.Localisation; namespace osu.Game.Overlays.Rankings.Tables { diff --git a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs index 6facf1e7a2..215cc95198 100644 --- a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs @@ -2,9 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; using osu.Game.Users; diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index bc8eac16a9..6e6230f958 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -10,6 +10,7 @@ using osu.Framework.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Game.Users; using osu.Game.Users.Drawables; using osuTK; diff --git a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs index b6bb66e2c8..934da4501e 100644 --- a/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/ScoresTable.cs @@ -2,9 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; using osu.Game.Users; diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs index b96ab556df..cc2ef55a2b 100644 --- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; diff --git a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs index 68e3f0df7d..d04e60a2ab 100644 --- a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs +++ b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; diff --git a/osu.Game/Screens/Select/Details/UserRatings.cs b/osu.Game/Screens/Select/Details/UserRatings.cs index a7f28b932a..eabc476db9 100644 --- a/osu.Game/Screens/Select/Details/UserRatings.cs +++ b/osu.Game/Screens/Select/Details/UserRatings.cs @@ -8,8 +8,8 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using System.Linq; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Game.Beatmaps; -using osu.Framework.Localisation; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Screens.Select.Details diff --git a/osu.Game/Utils/FormatUtils.cs b/osu.Game/Utils/FormatUtils.cs index e763558647..d14dbb49f3 100644 --- a/osu.Game/Utils/FormatUtils.cs +++ b/osu.Game/Utils/FormatUtils.cs @@ -3,6 +3,7 @@ using System; using Humanizer; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Localisation; namespace osu.Game.Utils From 8f3416d8534405210acb21b939ef22c011e674c9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 29 Aug 2021 16:03:37 +0300 Subject: [PATCH 1551/2442] Assert PP not null when `showPerformancePoints` is true --- osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index a154016824..8fe1d35b62 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -4,6 +4,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions; @@ -192,6 +193,8 @@ namespace osu.Game.Overlays.BeatmapSet.Scores if (showPerformancePoints) { + Debug.Assert(score.PP != null); + content.Add(new OsuSpriteText { Text = score.PP.ToLocalisableString(@"N0"), From 6aaef7b0be3985b30f142f1e8562a744bd4373cc Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 29 Aug 2021 17:19:13 +0300 Subject: [PATCH 1552/2442] Handle null PP during score set in `TopScoreStatisticsSection` Supersedes #14562 Closes #14541 --- .../Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs index 23069eccdf..883e83ce6e 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs @@ -116,7 +116,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores maxComboColumn.Text = value.MaxCombo.ToLocalisableString(@"0\x"); ppColumn.Alpha = value.Beatmap?.Status.GrantsPerformancePoints() == true ? 1 : 0; - ppColumn.Text = value.PP.ToLocalisableString(@"N0"); + ppColumn.Text = value.PP?.ToLocalisableString(@"N0"); statisticsColumns.ChildrenEnumerable = value.GetStatisticsForDisplay().Select(createStatisticsColumn); modsColumn.Mods = value.Mods; From 6dc11543ad527683bb8691e609ec4e486191f138 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 29 Aug 2021 17:20:33 +0300 Subject: [PATCH 1553/2442] Handle (null?) PP in `PerformanceTable` --- osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs index 215cc95198..17c17b1f1a 100644 --- a/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Rankings.Tables protected override Drawable[] CreateUniqueContent(UserStatistics item) => new Drawable[] { - new RowText { Text = item.PP.ToLocalisableString(@"N0"), } + new RowText { Text = item.PP?.ToLocalisableString(@"N0"), } }; } } From 1ca0223c7136b83e6f81a9ab9202e6917a1a506e Mon Sep 17 00:00:00 2001 From: mrowswares <83023433+mrowswares@users.noreply.github.com> Date: Sun, 29 Aug 2021 17:19:26 +0100 Subject: [PATCH 1554/2442] remove straintime & speed skill caps, implement basic doubletap cheese detection --- .../Difficulty/OsuDifficultyCalculator.cs | 2 +- .../Preprocessing/OsuDifficultyHitObject.cs | 2 +- .../Difficulty/Skills/Speed.cs | 20 ++++++++++++++++--- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index e47f82fb39..cfd74a4174 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -82,7 +82,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[] { new Aim(mods), - new Speed(mods) + new Speed(mods, beatmap, clockRate) }; protected override Mod[] DifficultyAdjustmentMods => new Mod[] diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index fa6c5c4d9c..bc97172dbf 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing setDistances(); // Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure - StrainTime = Math.Max(50, DeltaTime); + StrainTime = DeltaTime; } private void setDistances() diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index f0eb199e5f..7bc16485eb 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; @@ -26,12 +27,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override double DifficultyMultiplier => 1.04; private const double min_speed_bonus = 75; // ~200BPM - private const double max_speed_bonus = 45; // ~330BPM private const double speed_balancing_factor = 40; + private double greatWindow; - public Speed(Mod[] mods) + public Speed(Mod[] mods, IBeatmap beatmap, double clockRate) : base(mods) { + greatWindow = (79 - (beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty * 6) + 0.5) / clockRate; + Console.WriteLine(greatWindow); } protected override double StrainValueOf(DifficultyHitObject current) @@ -42,12 +45,23 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills var osuCurrent = (OsuDifficultyHitObject)current; double distance = Math.Min(single_spacing_threshold, osuCurrent.TravelDistance + osuCurrent.JumpDistance); - double deltaTime = Math.Max(max_speed_bonus, current.DeltaTime); + double deltaTime = current.DeltaTime; double speedBonus = 1.0; if (deltaTime < min_speed_bonus) speedBonus = 1 + Math.Pow((min_speed_bonus - deltaTime) / speed_balancing_factor, 2); + // Doubletap detection + if (Previous.Count > 0) + { + var osuPrevious = (OsuDifficultyHitObject)Previous[0]; + if ( (osuPrevious.DeltaTime / osuCurrent.DeltaTime) >= 3 && osuCurrent.DeltaTime <= (2 * greatWindow)) + { + //Console.WriteLine( osuCurrent.StartTime / 1000.0); + return 0; + } + } + double angleBonus = 1.0; if (osuCurrent.Angle != null && osuCurrent.Angle.Value < angle_bonus_begin) From 90c313e2ad4d67d827f5617feacba4b1a693fb12 Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Sun, 29 Aug 2021 19:19:55 +0100 Subject: [PATCH 1555/2442] add methods to get a user from their username --- osu.Game/Online/API/Requests/GetUserRequest.cs | 16 +++++++++++++--- osu.Game/OsuGame.cs | 9 +++++++-- osu.Game/Overlays/UserProfileOverlay.cs | 4 +++- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetUserRequest.cs b/osu.Game/Online/API/Requests/GetUserRequest.cs index 42aad6f9eb..48041cd40c 100644 --- a/osu.Game/Online/API/Requests/GetUserRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRequest.cs @@ -8,15 +8,25 @@ namespace osu.Game.Online.API.Requests { public class GetUserRequest : APIRequest { - private readonly long? userId; + private readonly string userIdentifier; public readonly RulesetInfo Ruleset; + public GetUserRequest() + { + } + public GetUserRequest(long? userId = null, RulesetInfo ruleset = null) { - this.userId = userId; + this.userIdentifier = userId.ToString(); Ruleset = ruleset; } - protected override string Target => userId.HasValue ? $@"users/{userId}/{Ruleset?.ShortName}" : $@"me/{Ruleset?.ShortName}"; + public GetUserRequest(string username = null, RulesetInfo ruleset = null) + { + this.userIdentifier = username; + Ruleset = ruleset; + } + + protected override string Target => (userIdentifier != null) ? $@"users/{userIdentifier}/{Ruleset?.ShortName}" : $@"me/{Ruleset?.ShortName}"; } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 4d952c39c6..26fa1d5a4c 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -329,8 +329,7 @@ namespace osu.Game break; case LinkAction.OpenUserProfile: - if (int.TryParse(link.Argument, out int userId)) - ShowUser(userId); + ShowUser(link.Argument); break; case LinkAction.OpenWiki: @@ -378,6 +377,12 @@ namespace osu.Game /// The user to display. public void ShowUser(int userId) => waitForReady(() => userProfile, _ => userProfile.ShowUser(userId)); + /// + /// Show a user's profile as an overlay. + /// + /// The user to display. + public void ShowUser(string username) => waitForReady(() => userProfile, _ => userProfile.ShowUser(username)); + /// /// Show a beatmap's set as an overlay, displaying the given beatmap. /// diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index 299a14b250..6e74acc96a 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -40,6 +40,8 @@ namespace osu.Game.Overlays public void ShowUser(int userId) => ShowUser(new User { Id = userId }); + public void ShowUser(string username) => ShowUser(new User { Username = username }); + public void ShowUser(User user, bool fetchOnline = true) { if (user == User.SYSTEM_USER) @@ -116,7 +118,7 @@ namespace osu.Game.Overlays if (fetchOnline) { - userReq = new GetUserRequest(user.Id); + userReq = user.Username != null ? new GetUserRequest(user.Username) : new GetUserRequest(user.Id); userReq.Success += userLoadComplete; API.Queue(userReq); } From a190038c33fec8b62784d4c64ae72aef28e15477 Mon Sep 17 00:00:00 2001 From: mrowswares <83023433+mrowswares@users.noreply.github.com> Date: Sun, 29 Aug 2021 20:16:13 +0100 Subject: [PATCH 1556/2442] remove writelines --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 7bc16485eb..7cc5447888 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -34,7 +34,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills : base(mods) { greatWindow = (79 - (beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty * 6) + 0.5) / clockRate; - Console.WriteLine(greatWindow); } protected override double StrainValueOf(DifficultyHitObject current) @@ -57,7 +56,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills var osuPrevious = (OsuDifficultyHitObject)Previous[0]; if ( (osuPrevious.DeltaTime / osuCurrent.DeltaTime) >= 3 && osuCurrent.DeltaTime <= (2 * greatWindow)) { - //Console.WriteLine( osuCurrent.StartTime / 1000.0); return 0; } } From 7bb2269eba507366da5f6cc85c7cee48421ff8a2 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sun, 29 Aug 2021 22:27:56 -0700 Subject: [PATCH 1557/2442] Add overlay closing behavior test --- .../Navigation/TestSceneScreenNavigation.cs | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 3c65f46c79..a112534837 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -323,6 +323,69 @@ namespace osu.Game.Tests.Visual.Navigation AddWaitStep("wait two frames", 2); } + [Test] + public void TestOverlayClosing() + { + // use now playing overlay for "overlay -> background" drag case + // since most overlays use a scroll container that absorbs on mouse down + NowPlayingOverlay nowPlayingOverlay = null; + + AddStep("enter menu", () => InputManager.Key(Key.Enter)); + + AddStep("get and press now playing hotkey", () => + { + nowPlayingOverlay = Game.ChildrenOfType().Single(); + InputManager.Key(Key.F6); + }); + + // drag tests + + // background -> toolbar + AddStep("move cursor to background", () => InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.BottomRight)); + AddStep("press left mouse button", () => InputManager.PressButton(MouseButton.Left)); + AddStep("move cursor to toolbar", () => InputManager.MoveMouseTo(Game.Toolbar.ScreenSpaceDrawQuad.Centre)); + AddStep("release left mouse button", () => InputManager.ReleaseButton(MouseButton.Left)); + AddAssert("now playing is still visible", () => nowPlayingOverlay.State.Value == Visibility.Visible); + + // toolbar -> background + AddStep("press left mouse button", () => InputManager.PressButton(MouseButton.Left)); + AddStep("move cursor to background", () => InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.BottomRight)); + AddStep("release left mouse button", () => InputManager.ReleaseButton(MouseButton.Left)); + AddAssert("now playing is still visible", () => nowPlayingOverlay.State.Value == Visibility.Visible); + + // background -> overlay + AddStep("press left mouse button", () => InputManager.PressButton(MouseButton.Left)); + AddStep("move cursor to now playing overlay", () => InputManager.MoveMouseTo(nowPlayingOverlay.ScreenSpaceDrawQuad.Centre)); + AddStep("release left mouse button", () => InputManager.ReleaseButton(MouseButton.Left)); + AddAssert("now playing is still visible", () => nowPlayingOverlay.State.Value == Visibility.Visible); + + // overlay -> background + AddStep("press left mouse button", () => InputManager.PressButton(MouseButton.Left)); + AddStep("move cursor to background", () => InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.BottomRight)); + AddStep("release left mouse button", () => InputManager.ReleaseButton(MouseButton.Left)); + AddAssert("now playing is still visible", () => nowPlayingOverlay.State.Value == Visibility.Visible); + + // background -> background + AddStep("press left mouse button", () => InputManager.PressButton(MouseButton.Left)); + AddStep("move cursor to left", () => InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.BottomLeft)); + AddStep("release left mouse button", () => InputManager.ReleaseButton(MouseButton.Left)); + AddAssert("now playing is hidden", () => nowPlayingOverlay.State.Value == Visibility.Hidden); + + AddStep("press now playing hotkey", () => InputManager.Key(Key.F6)); + + // click tests + + // toolbar + AddStep("move cursor to toolbar", () => InputManager.MoveMouseTo(Game.Toolbar.ScreenSpaceDrawQuad.Centre)); + AddStep("click left mouse button", () => InputManager.Click(MouseButton.Left)); + AddAssert("now playing is still visible", () => nowPlayingOverlay.State.Value == Visibility.Visible); + + // background + AddStep("move cursor to background", () => InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.BottomRight)); + AddStep("click left mouse button", () => InputManager.Click(MouseButton.Left)); + AddAssert("now playing is hidden", () => nowPlayingOverlay.State.Value == Visibility.Hidden); + } + private void pushEscape() => AddStep("Press escape", () => InputManager.Key(Key.Escape)); From ee49305cad0f0edc6c7a7c0b5c65d8d63b95a3b8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 30 Aug 2021 14:40:25 +0900 Subject: [PATCH 1558/2442] Move taiko legacy speed multiplier to `osu.Game` project Allows it to be used in local case in `LegacyBeatmapEncoder`. --- .../Beatmaps/TaikoBeatmapConverter.cs | 10 ++-------- osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 4 ++-- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 8 +++++++- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 90c99316b1..77f058fad9 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -18,12 +18,6 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps { internal class TaikoBeatmapConverter : BeatmapConverter { - /// - /// osu! is generally slower than taiko, so a factor is added to increase - /// speed. This must be used everywhere slider length or beat length is used. - /// - public const float LEGACY_VELOCITY_MULTIPLIER = 1.4f; - /// /// Because swells are easier in taiko than spinners are in osu!, /// legacy taiko multiplies a factor when converting the number of required hits. @@ -55,7 +49,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps // Rewrite the beatmap info to add the slider velocity multiplier original.BeatmapInfo = original.BeatmapInfo.Clone(); original.BeatmapInfo.BaseDifficulty = original.BeatmapInfo.BaseDifficulty.Clone(); - original.BeatmapInfo.BaseDifficulty.SliderMultiplier *= LEGACY_VELOCITY_MULTIPLIER; + original.BeatmapInfo.BaseDifficulty.SliderMultiplier *= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; Beatmap converted = base.ConvertBeatmap(original, cancellationToken); @@ -155,7 +149,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps // The true distance, accounting for any repeats. This ends up being the drum roll distance later int spans = (obj as IHasRepeats)?.SpanCount() ?? 1; - double distance = distanceData.Distance * spans * LEGACY_VELOCITY_MULTIPLIER; + double distance = distanceData.Distance * spans * LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime); DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(obj.StartTime); diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index c0377c67a5..b0634295d0 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -6,10 +6,10 @@ using System; using System.Threading; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Beatmaps.Formats; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.Taiko.Beatmaps; using osu.Game.Rulesets.Taiko.Judgements; using osuTK; @@ -120,7 +120,7 @@ namespace osu.Game.Rulesets.Taiko.Objects double IHasDistance.Distance => Duration * Velocity; SliderPath IHasPath.Path - => new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(1) }, ((IHasDistance)this).Distance / TaikoBeatmapConverter.LEGACY_VELOCITY_MULTIPLIER); + => new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(1) }, ((IHasDistance)this).Distance / LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER); #endregion } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 246dc991d5..1595ba5b8e 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -24,6 +24,12 @@ namespace osu.Game.Beatmaps.Formats { public const int LATEST_VERSION = 128; + /// + /// osu! is generally slower than taiko, so a factor is added to increase + /// speed. This must be used everywhere slider length or beat length is used. + /// + public const float LEGACY_TAIKO_VELOCITY_MULTIPLIER = 1.4f; + private readonly IBeatmap beatmap; [CanBeNull] @@ -142,7 +148,7 @@ namespace osu.Game.Beatmaps.Formats // Taiko adjusts the slider multiplier (see: TaikoBeatmapConverter.LEGACY_VELOCITY_MULTIPLIER) writer.WriteLine(beatmap.BeatmapInfo.RulesetID == 1 - ? FormattableString.Invariant($"SliderMultiplier: {beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier / 1.4f}") + ? FormattableString.Invariant($"SliderMultiplier: {beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier / LEGACY_TAIKO_VELOCITY_MULTIPLIER}") : FormattableString.Invariant($"SliderMultiplier: {beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier}")); writer.WriteLine(FormattableString.Invariant($"SliderTickRate: {beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate}")); From 4adfe9a6dc3a4b05d67c4c626f2e7b2009d9b785 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 30 Aug 2021 15:30:04 +0900 Subject: [PATCH 1559/2442] Add test coverage of double-convert stability --- .../Beatmaps/Formats/LegacyBeatmapEncoderTest.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index 855a75117d..96986f06d5 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -49,6 +49,22 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.IsTrue(areComboColoursEqual(decodedAfterEncode.beatmapSkin.Configuration, decoded.beatmapSkin.Configuration)); } + [TestCaseSource(nameof(allBeatmaps))] + public void TestEncodeDecodeStabilityDoubleConvert(string name) + { + var decoded = decodeFromLegacy(beatmaps_resource_store.GetStream(name), name); + var decodedAfterEncode = decodeFromLegacy(encodeToLegacy(decoded), name); + + // run an extra convert. this is expected to be stable. + decodedAfterEncode.beatmap = convert(decodedAfterEncode.beatmap); + + sort(decoded.beatmap); + sort(decodedAfterEncode.beatmap); + + Assert.That(decodedAfterEncode.beatmap.Serialize(), Is.EqualTo(decoded.beatmap.Serialize())); + Assert.IsTrue(areComboColoursEqual(decodedAfterEncode.beatmapSkin.Configuration, decoded.beatmapSkin.Configuration)); + } + [Test] public void TestEncodeMultiSegmentSliderWithFloatingPointError() { From 6a6dac609caaad6e60d94a49224efb532e1111eb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 30 Aug 2021 15:30:17 +0900 Subject: [PATCH 1560/2442] Fix instability of taiko double conversion Until now, the taiko speed multiplier was potentially applied more than once if conversion was run multiple times. --- .../Beatmaps/TaikoBeatmapConverter.cs | 19 +++++++++++++++---- osu.Game/Beatmaps/BeatmapDifficulty.cs | 18 +++++++++++++++++- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 77f058fad9..9b73e644c5 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -46,10 +46,12 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps protected override Beatmap ConvertBeatmap(IBeatmap original, CancellationToken cancellationToken) { - // Rewrite the beatmap info to add the slider velocity multiplier - original.BeatmapInfo = original.BeatmapInfo.Clone(); - original.BeatmapInfo.BaseDifficulty = original.BeatmapInfo.BaseDifficulty.Clone(); - original.BeatmapInfo.BaseDifficulty.SliderMultiplier *= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; + if (!(original.BeatmapInfo.BaseDifficulty is TaikoMutliplierAppliedDifficulty)) + { + // Rewrite the beatmap info to add the slider velocity multiplier + original.BeatmapInfo = original.BeatmapInfo.Clone(); + original.BeatmapInfo.BaseDifficulty = new TaikoMutliplierAppliedDifficulty(original.BeatmapInfo.BaseDifficulty); + } Beatmap converted = base.ConvertBeatmap(original, cancellationToken); @@ -188,5 +190,14 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps } protected override Beatmap CreateBeatmap() => new TaikoBeatmap(); + + private class TaikoMutliplierAppliedDifficulty : BeatmapDifficulty + { + public TaikoMutliplierAppliedDifficulty(BeatmapDifficulty difficulty) + { + difficulty.CopyTo(this); + SliderMultiplier *= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; + } + } } } diff --git a/osu.Game/Beatmaps/BeatmapDifficulty.cs b/osu.Game/Beatmaps/BeatmapDifficulty.cs index c56fec67aa..1844b193f2 100644 --- a/osu.Game/Beatmaps/BeatmapDifficulty.cs +++ b/osu.Game/Beatmaps/BeatmapDifficulty.cs @@ -32,7 +32,23 @@ namespace osu.Game.Beatmaps /// /// Returns a shallow-clone of this . /// - public BeatmapDifficulty Clone() => (BeatmapDifficulty)MemberwiseClone(); + public BeatmapDifficulty Clone() + { + var diff = new BeatmapDifficulty(); + CopyTo(diff); + return diff; + } + + public void CopyTo(BeatmapDifficulty difficulty) + { + difficulty.ApproachRate = ApproachRate; + difficulty.DrainRate = DrainRate; + difficulty.CircleSize = CircleSize; + difficulty.OverallDifficulty = OverallDifficulty; + + difficulty.SliderMultiplier = SliderMultiplier; + difficulty.SliderTickRate = SliderTickRate; + } /// /// Maps a difficulty value [0, 10] to a two-piece linear range of values. From 58a052ea1f7bf15a980870d48b7176b9e5c026d8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 30 Aug 2021 16:00:07 +0900 Subject: [PATCH 1561/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index f18400bf2f..8a9bf1b9cd 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index a89d57cd1f..b4d4aa3070 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index bb4700a081..29e9b9fe20 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From fa2bf421886fe70c14706ae2d41831afcf7e49fb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 30 Aug 2021 16:04:54 +0900 Subject: [PATCH 1562/2442] Update tooltip implementations --- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 6 ++---- .../Graphics/Cursor/OsuTooltipContainer.cs | 9 ++------- osu.Game/Graphics/DateTooltip.cs | 8 ++------ osu.Game/Overlays/Mods/ModButtonTooltip.cs | 19 ++++--------------- .../Profile/Header/Components/RankGraph.cs | 5 ++--- .../Sections/Historical/UserHistoryGraph.cs | 5 ++--- osu.Game/Overlays/Profile/UserGraph.cs | 2 +- 7 files changed, 15 insertions(+), 39 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 3210ef0112..199f719893 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -259,10 +259,10 @@ namespace osu.Game.Beatmaps.Drawables private readonly IBindable starDifficulty = new Bindable(); - public bool SetContent(object content) + public void SetContent(object content) { if (!(content is DifficultyIconTooltipContent iconContent)) - return false; + return; difficultyName.Text = iconContent.Beatmap.Version; @@ -273,8 +273,6 @@ namespace osu.Game.Beatmaps.Drawables starRating.Text = $"{difficulty.NewValue.Stars:0.##}"; difficultyFlow.Colour = colours.ForStarDifficulty(difficulty.NewValue.Stars); }, true); - - return true; } public void Move(Vector2 pos) => Position = pos; diff --git a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs index 81dca99ddd..35d7b4e795 100644 --- a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs +++ b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs @@ -31,12 +31,9 @@ namespace osu.Game.Graphics.Cursor private readonly OsuSpriteText text; private bool instantMovement = true; - public override bool SetContent(object content) + public override void SetContent(LocalisableString contentString) { - if (!(content is LocalisableString contentString)) - return false; - - if (contentString == text.Text) return true; + if (contentString == text.Text) return; text.Text = contentString; @@ -47,8 +44,6 @@ namespace osu.Game.Graphics.Cursor } else AutoSizeDuration = 0; - - return true; } public OsuTooltip() diff --git a/osu.Game/Graphics/DateTooltip.cs b/osu.Game/Graphics/DateTooltip.cs index 67fcab43f7..3094f9cc2b 100644 --- a/osu.Game/Graphics/DateTooltip.cs +++ b/osu.Game/Graphics/DateTooltip.cs @@ -12,7 +12,7 @@ using osuTK; namespace osu.Game.Graphics { - public class DateTooltip : VisibilityContainer, ITooltip + public class DateTooltip : VisibilityContainer, ITooltip { private readonly OsuSpriteText dateText, timeText; private readonly Box background; @@ -63,14 +63,10 @@ namespace osu.Game.Graphics protected override void PopIn() => this.FadeIn(200, Easing.OutQuint); protected override void PopOut() => this.FadeOut(200, Easing.OutQuint); - public bool SetContent(object content) + public void SetContent(DateTimeOffset date) { - if (!(content is DateTimeOffset date)) - return false; - dateText.Text = $"{date:d MMMM yyyy} "; timeText.Text = $"{date:HH:mm:ss \"UTC\"z}"; - return true; } public void Move(Vector2 pos) => Position = pos; diff --git a/osu.Game/Overlays/Mods/ModButtonTooltip.cs b/osu.Game/Overlays/Mods/ModButtonTooltip.cs index 666ed07e28..89fcd61d76 100644 --- a/osu.Game/Overlays/Mods/ModButtonTooltip.cs +++ b/osu.Game/Overlays/Mods/ModButtonTooltip.cs @@ -18,7 +18,7 @@ using osuTK; namespace osu.Game.Overlays.Mods { - public class ModButtonTooltip : VisibilityContainer, ITooltip + public class ModButtonTooltip : VisibilityContainer, ITooltip { private readonly OsuSpriteText descriptionText; private readonly Box background; @@ -82,12 +82,9 @@ namespace osu.Game.Overlays.Mods private Mod lastMod; - public bool SetContent(object content) + public void SetContent(Mod mod) { - if (!(content is Mod mod)) - return false; - - if (mod.Equals(lastMod)) return true; + if (mod.Equals(lastMod)) return; lastMod = mod; @@ -99,15 +96,7 @@ namespace osu.Game.Overlays.Mods incompatibleMods.Value = allMods.Where(m => m.GetType() != mod.GetType() && incompatibleTypes.Any(t => t.IsInstanceOfType(m))).ToList(); - if (!incompatibleMods.Value.Any()) - { - incompatibleText.Text = "Compatible with all mods"; - return true; - } - - incompatibleText.Text = "Incompatible with:"; - - return true; + incompatibleText.Text = !incompatibleMods.Value.Any() ? "Compatible with all mods" : "Incompatible with:"; } public void Move(Vector2 pos) => Position = pos; diff --git a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs index e8bce404e1..7ba8ae7c80 100644 --- a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs @@ -81,14 +81,13 @@ namespace osu.Game.Overlays.Profile.Header.Components { } - public override bool SetContent(object content) + public override void SetContent(object content) { if (!(content is TooltipDisplayContent info)) - return false; + return; Counter.Text = info.Rank; BottomText.Text = info.Time; - return true; } } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs b/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs index d25c53b5ec..85287d2325 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs @@ -50,14 +50,13 @@ namespace osu.Game.Overlays.Profile.Sections.Historical this.tooltipCounterName = tooltipCounterName; } - public override bool SetContent(object content) + public override void SetContent(object content) { if (!(content is TooltipDisplayContent info) || info.Name != tooltipCounterName) - return false; + return; Counter.Text = info.Count; BottomText.Text = info.Date; - return true; } } diff --git a/osu.Game/Overlays/Profile/UserGraph.cs b/osu.Game/Overlays/Profile/UserGraph.cs index b7a08b6c5e..b88cc32ff7 100644 --- a/osu.Game/Overlays/Profile/UserGraph.cs +++ b/osu.Game/Overlays/Profile/UserGraph.cs @@ -268,7 +268,7 @@ namespace osu.Game.Overlays.Profile background.Colour = colours.Gray1; } - public abstract bool SetContent(object content); + public abstract void SetContent(object content); private bool instantMove = true; From 678386f5c4672bda1c762ccbe26673259808e928 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 30 Aug 2021 16:05:56 +0900 Subject: [PATCH 1563/2442] Fix missed null coalesce --- osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs index 7704fa24df..5c3906cb39 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs @@ -128,7 +128,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores public int? ScorePosition { - set => rankText.Text = value == null ? (LocalisableString)"-" : value.ToLocalisableString(@"\##"); + set => rankText.Text = value?.ToLocalisableString(@"\##") ?? (LocalisableString)"-"; } /// From da7a871afa1edc784fe111e4f500baaa9d1f740e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 30 Aug 2021 16:27:24 +0900 Subject: [PATCH 1564/2442] Update inline comment to point to new variable location Co-authored-by: PercyDan <50285552+PercyDan54@users.noreply.github.com> --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 1595ba5b8e..fb6806a13d 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -146,7 +146,7 @@ namespace osu.Game.Beatmaps.Formats writer.WriteLine(FormattableString.Invariant($"OverallDifficulty: {beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty}")); writer.WriteLine(FormattableString.Invariant($"ApproachRate: {beatmap.BeatmapInfo.BaseDifficulty.ApproachRate}")); - // Taiko adjusts the slider multiplier (see: TaikoBeatmapConverter.LEGACY_VELOCITY_MULTIPLIER) + // Taiko adjusts the slider multiplier (see: LEGACY_TAIKO_VELOCITY_MULTIPLIER) writer.WriteLine(beatmap.BeatmapInfo.RulesetID == 1 ? FormattableString.Invariant($"SliderMultiplier: {beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier / LEGACY_TAIKO_VELOCITY_MULTIPLIER}") : FormattableString.Invariant($"SliderMultiplier: {beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier}")); From 7257aae7f26bd4c322d04e5aa739207773c5bfbd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 Aug 2021 18:00:57 +0900 Subject: [PATCH 1565/2442] Move samples to `LegacyControlPointInfo` --- .../Formats/LegacyBeatmapDecoderTest.cs | 5 +- .../NonVisual/ControlPointInfoTest.cs | 4 +- .../ControlPoints/ControlPointInfo.cs | 119 +++++++++++------- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 22 ++++ .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 4 +- osu.Game/Rulesets/Objects/HitObject.cs | 7 +- osu.Game/Screens/Edit/Timing/SampleSection.cs | 12 +- 7 files changed, 110 insertions(+), 63 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 12633ee8c9..24410c886f 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -10,6 +10,7 @@ using osu.Game.Tests.Resources; using System.Linq; using osu.Game.Audio; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Objects.Types; using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Timing; @@ -166,7 +167,7 @@ namespace osu.Game.Tests.Beatmaps.Formats using (var stream = new LineBufferedReader(resStream)) { var beatmap = decoder.Decode(stream); - var controlPoints = beatmap.ControlPointInfo; + var controlPoints = (LegacyControlPointInfo)beatmap.ControlPointInfo; Assert.AreEqual(4, controlPoints.TimingPoints.Count); Assert.AreEqual(5, controlPoints.DifficultyPoints.Count); @@ -240,7 +241,7 @@ namespace osu.Game.Tests.Beatmaps.Formats using (var resStream = TestResources.OpenResource("overlapping-control-points.osu")) using (var stream = new LineBufferedReader(resStream)) { - var controlPoints = decoder.Decode(stream).ControlPointInfo; + var controlPoints = (LegacyControlPointInfo)decoder.Decode(stream).ControlPointInfo; Assert.That(controlPoints.TimingPoints.Count, Is.EqualTo(4)); Assert.That(controlPoints.DifficultyPoints.Count, Is.EqualTo(3)); diff --git a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs index 240ae4a90c..e1d6df814e 100644 --- a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs +++ b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs @@ -64,7 +64,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestAddRedundantSample() { - var cpi = new ControlPointInfo(); + var cpi = new LegacyControlPointInfo(); cpi.Add(0, new SampleControlPoint()); // is *not* redundant, special exception for first sample point cpi.Add(1000, new SampleControlPoint()); // is redundant @@ -142,7 +142,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestRemoveGroupAlsoRemovedControlPoints() { - var cpi = new ControlPointInfo(); + var cpi = new LegacyControlPointInfo(); var group = cpi.GroupAt(1000, true); diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index 2d0fc17a7b..7cc4fa6dc4 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -14,6 +14,64 @@ using osu.Game.Utils; namespace osu.Game.Beatmaps.ControlPoints { + public class LegacyControlPointInfo : ControlPointInfo + { + /// + /// All sound points. + /// + [JsonProperty] + public IBindableList SamplePoints => samplePoints; + + private readonly BindableList samplePoints = new BindableList(); + + /// + /// Finds the sound control point that is active at . + /// + /// The time to find the sound control point at. + /// The sound control point. + [NotNull] + public SampleControlPoint SamplePointAt(double time) => BinarySearchWithFallback(SamplePoints, time, SamplePoints.Count > 0 ? SamplePoints[0] : SampleControlPoint.DEFAULT); + + public override void Clear() + { + base.Clear(); + samplePoints.Clear(); + } + + protected override bool CheckAlreadyExisting(double time, ControlPoint newPoint) + { + if (newPoint is SampleControlPoint _) + { + var existing = BinarySearch(SamplePoints, time); + return newPoint?.IsRedundant(existing) == true; + } + + return base.CheckAlreadyExisting(time, newPoint); + } + + protected override void GroupItemAdded(ControlPoint controlPoint) + { + if (controlPoint is SampleControlPoint typed) + { + samplePoints.Add(typed); + return; + } + + base.GroupItemAdded(controlPoint); + } + + protected override void GroupItemRemoved(ControlPoint controlPoint) + { + if (controlPoint is SampleControlPoint typed) + { + samplePoints.Remove(typed); + return; + } + + base.GroupItemRemoved(controlPoint); + } + } + [Serializable] public class ControlPointInfo : IDeepCloneable { @@ -41,14 +99,6 @@ namespace osu.Game.Beatmaps.ControlPoints private readonly SortedList difficultyPoints = new SortedList(Comparer.Default); - /// - /// All sound points. - /// - [JsonProperty] - public IBindableList SamplePoints => samplePoints; - - private readonly BindableList samplePoints = new BindableList(); - /// /// All effect points. /// @@ -69,7 +119,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// The time to find the difficulty control point at. /// The difficulty control point. [NotNull] - public DifficultyControlPoint DifficultyPointAt(double time) => binarySearchWithFallback(DifficultyPoints, time, DifficultyControlPoint.DEFAULT); + public DifficultyControlPoint DifficultyPointAt(double time) => BinarySearchWithFallback(DifficultyPoints, time, DifficultyControlPoint.DEFAULT); /// /// Finds the effect control point that is active at . @@ -77,15 +127,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// The time to find the effect control point at. /// The effect control point. [NotNull] - public EffectControlPoint EffectPointAt(double time) => binarySearchWithFallback(EffectPoints, time, EffectControlPoint.DEFAULT); - - /// - /// Finds the sound control point that is active at . - /// - /// The time to find the sound control point at. - /// The sound control point. - [NotNull] - public SampleControlPoint SamplePointAt(double time) => binarySearchWithFallback(SamplePoints, time, SamplePoints.Count > 0 ? SamplePoints[0] : SampleControlPoint.DEFAULT); + public EffectControlPoint EffectPointAt(double time) => BinarySearchWithFallback(EffectPoints, time, EffectControlPoint.DEFAULT); /// /// Finds the timing control point that is active at . @@ -93,7 +135,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// The time to find the timing control point at. /// The timing control point. [NotNull] - public TimingControlPoint TimingPointAt(double time) => binarySearchWithFallback(TimingPoints, time, TimingPoints.Count > 0 ? TimingPoints[0] : TimingControlPoint.DEFAULT); + public TimingControlPoint TimingPointAt(double time) => BinarySearchWithFallback(TimingPoints, time, TimingPoints.Count > 0 ? TimingPoints[0] : TimingControlPoint.DEFAULT); /// /// Finds the maximum BPM represented by any timing control point. @@ -112,12 +154,11 @@ namespace osu.Game.Beatmaps.ControlPoints /// /// Remove all s and return to a pristine state. /// - public void Clear() + public virtual void Clear() { groups.Clear(); timingPoints.Clear(); difficultyPoints.Clear(); - samplePoints.Clear(); effectPoints.Clear(); } @@ -129,7 +170,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// Whether the control point was added. public bool Add(double time, ControlPoint controlPoint) { - if (checkAlreadyExisting(time, controlPoint)) + if (CheckAlreadyExisting(time, controlPoint)) return false; GroupAt(time, true).Add(controlPoint); @@ -147,8 +188,8 @@ namespace osu.Game.Beatmaps.ControlPoints if (addIfNotExisting) { - newGroup.ItemAdded += groupItemAdded; - newGroup.ItemRemoved += groupItemRemoved; + newGroup.ItemAdded += GroupItemAdded; + newGroup.ItemRemoved += GroupItemRemoved; groups.Insert(~i, newGroup); return newGroup; @@ -162,8 +203,8 @@ namespace osu.Game.Beatmaps.ControlPoints foreach (var item in group.ControlPoints.ToArray()) group.Remove(item); - group.ItemAdded -= groupItemAdded; - group.ItemRemoved -= groupItemRemoved; + group.ItemAdded -= GroupItemAdded; + group.ItemRemoved -= GroupItemRemoved; groups.Remove(group); } @@ -228,10 +269,10 @@ namespace osu.Game.Beatmaps.ControlPoints /// The time to find the control point at. /// The control point to use when is before any control points. /// The active control point at , or a fallback if none found. - private T binarySearchWithFallback(IReadOnlyList list, double time, T fallback) + protected T BinarySearchWithFallback(IReadOnlyList list, double time, T fallback) where T : ControlPoint { - return binarySearch(list, time) ?? fallback; + return BinarySearch(list, time) ?? fallback; } /// @@ -240,7 +281,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// The list to search. /// The time to find the control point at. /// The active control point at . - private T binarySearch(IReadOnlyList list, double time) + protected virtual T BinarySearch(IReadOnlyList list, double time) where T : ControlPoint { if (list == null) @@ -280,7 +321,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// The time to find the timing control point at. /// A point to be added. /// Whether the new point should be added. - private bool checkAlreadyExisting(double time, ControlPoint newPoint) + protected virtual bool CheckAlreadyExisting(double time, ControlPoint newPoint) { ControlPoint existing = null; @@ -288,17 +329,13 @@ namespace osu.Game.Beatmaps.ControlPoints { case TimingControlPoint _: // Timing points are a special case and need to be added regardless of fallback availability. - existing = binarySearch(TimingPoints, time); + existing = BinarySearch(TimingPoints, time); break; case EffectControlPoint _: existing = EffectPointAt(time); break; - case SampleControlPoint _: - existing = binarySearch(SamplePoints, time); - break; - case DifficultyControlPoint _: existing = DifficultyPointAt(time); break; @@ -307,7 +344,7 @@ namespace osu.Game.Beatmaps.ControlPoints return newPoint?.IsRedundant(existing) == true; } - private void groupItemAdded(ControlPoint controlPoint) + protected virtual void GroupItemAdded(ControlPoint controlPoint) { switch (controlPoint) { @@ -319,17 +356,13 @@ namespace osu.Game.Beatmaps.ControlPoints effectPoints.Add(typed); break; - case SampleControlPoint typed: - samplePoints.Add(typed); - break; - case DifficultyControlPoint typed: difficultyPoints.Add(typed); break; } } - private void groupItemRemoved(ControlPoint controlPoint) + protected virtual void GroupItemRemoved(ControlPoint controlPoint) { switch (controlPoint) { @@ -341,10 +374,6 @@ namespace osu.Game.Beatmaps.ControlPoints effectPoints.Remove(typed); break; - case SampleControlPoint typed: - samplePoints.Remove(typed); - break; - case DifficultyControlPoint typed: difficultyPoints.Remove(typed); break; diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 0ddc9e4c48..9bd76a9f2c 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -44,6 +44,13 @@ namespace osu.Game.Beatmaps.Formats offset = FormatVersion < 5 ? 24 : 0; } + protected override Beatmap CreateTemplateObject() + { + var templateBeatmap = base.CreateTemplateObject(); + templateBeatmap.ControlPointInfo = new LegacyControlPointInfo(); + return templateBeatmap; + } + protected override void ParseStreamInto(LineBufferedReader stream, Beatmap beatmap) { this.beatmap = beatmap; @@ -394,8 +401,16 @@ namespace osu.Game.Beatmaps.Formats private readonly HashSet pendingControlPointTypes = new HashSet(); private double pendingControlPointsTime; + private readonly LegacyControlPointInfo controlPointInfo = new LegacyControlPointInfo(); + private void addControlPoint(double time, ControlPoint point, bool timingChange) { + if (point is SampleControlPoint) + { + controlPointInfo.Add(time, point); + return; + } + if (time != pendingControlPointsTime) flushPendingPoints(); @@ -430,8 +445,15 @@ namespace osu.Game.Beatmaps.Formats parser ??= new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(getOffsetTime(), FormatVersion); var obj = parser.Parse(line); + if (obj != null) + { + // assign legacy control points directly to hitobject + //obj.SampleControlPoint = controlPointInfo.SamplePointAt(obj.StartTime); + obj.ApplyDefaults(controlPointInfo, beatmap.BeatmapInfo.BaseDifficulty); + beatmap.HitObjects.Add(obj); + } } private int getOffsetTime(int time) => time + (ApplyOffsets ? offset : 0); diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 246dc991d5..a6c41a3cf6 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -80,7 +80,7 @@ namespace osu.Game.Beatmaps.Formats writer.WriteLine(FormattableString.Invariant($"AudioLeadIn: {beatmap.BeatmapInfo.AudioLeadIn}")); writer.WriteLine(FormattableString.Invariant($"PreviewTime: {beatmap.Metadata.PreviewTime}")); writer.WriteLine(FormattableString.Invariant($"Countdown: {(int)beatmap.BeatmapInfo.Countdown}")); - writer.WriteLine(FormattableString.Invariant($"SampleSet: {toLegacySampleBank(beatmap.ControlPointInfo.SamplePointAt(double.MinValue).SampleBank)}")); + writer.WriteLine(FormattableString.Invariant($"SampleSet: {toLegacySampleBank((beatmap.HitObjects.FirstOrDefault()?.SampleControlPoint ?? SampleControlPoint.DEFAULT).SampleBank)}")); writer.WriteLine(FormattableString.Invariant($"StackLeniency: {beatmap.BeatmapInfo.StackLeniency}")); writer.WriteLine(FormattableString.Invariant($"Mode: {beatmap.BeatmapInfo.RulesetID}")); writer.WriteLine(FormattableString.Invariant($"LetterboxInBreaks: {(beatmap.BeatmapInfo.LetterboxInBreaks ? '1' : '0')}")); @@ -187,7 +187,7 @@ namespace osu.Game.Beatmaps.Formats void outputControlPointEffectsAt(double time, bool isTimingPoint) { - var samplePoint = beatmap.ControlPointInfo.SamplePointAt(time); + var samplePoint = ((LegacyControlPointInfo)beatmap.ControlPointInfo).SamplePointAt(time); var effectPoint = beatmap.ControlPointInfo.EffectPointAt(time); // Apply the control point to a hit sample to uncover legacy properties (e.g. suffix) diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index 422655502d..fd9f02604c 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -106,8 +106,11 @@ namespace osu.Game.Rulesets.Objects { ApplyDefaultsToSelf(controlPointInfo, difficulty); - // This is done here since ApplyDefaultsToSelf may be used to determine the end time - SampleControlPoint = controlPointInfo.SamplePointAt(this.GetEndTime() + control_point_leniency); + if (controlPointInfo is LegacyControlPointInfo legacyInfo) + { + // This is done here since ApplyDefaultsToSelf may be used to determine the end time + SampleControlPoint = legacyInfo.SamplePointAt(this.GetEndTime() + control_point_leniency); + } nestedHitObjects.Clear(); diff --git a/osu.Game/Screens/Edit/Timing/SampleSection.cs b/osu.Game/Screens/Edit/Timing/SampleSection.cs index cc73af6349..be8de18502 100644 --- a/osu.Game/Screens/Edit/Timing/SampleSection.cs +++ b/osu.Game/Screens/Edit/Timing/SampleSection.cs @@ -1,6 +1,7 @@ // 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 osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -42,15 +43,6 @@ namespace osu.Game.Screens.Edit.Timing } } - protected override SampleControlPoint CreatePoint() - { - var reference = Beatmap.ControlPointInfo.SamplePointAt(SelectedGroup.Value.Time); - - return new SampleControlPoint - { - SampleBank = reference.SampleBank, - SampleVolume = reference.SampleVolume, - }; - } + protected override SampleControlPoint CreatePoint() => new SampleControlPoint(); // TODO: remove } } From ccacf56dd843352743d589adfe98c6c0c48a54db Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 30 Aug 2021 14:12:30 +0900 Subject: [PATCH 1566/2442] Move to legacy namespace --- .../Formats/LegacyBeatmapDecoderTest.cs | 12 ++-- .../NonVisual/ControlPointInfoTest.cs | 1 + .../ControlPoints/ControlPointInfo.cs | 58 ---------------- .../Beatmaps/Legacy/LegacyControlPointInfo.cs | 68 +++++++++++++++++++ osu.Game/Rulesets/Objects/HitObject.cs | 1 + osu.Game/Screens/Edit/Timing/SampleSection.cs | 1 - 6 files changed, 76 insertions(+), 65 deletions(-) create mode 100644 osu.Game/Beatmaps/Legacy/LegacyControlPointInfo.cs diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 24410c886f..8560a36fb4 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -3,16 +3,12 @@ using System; using System.IO; -using NUnit.Framework; -using osuTK; -using osuTK.Graphics; -using osu.Game.Tests.Resources; using System.Linq; +using NUnit.Framework; using osu.Game.Audio; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Beatmaps.Formats; +using osu.Game.Beatmaps.Legacy; using osu.Game.Beatmaps.Timing; using osu.Game.IO; using osu.Game.Rulesets.Catch; @@ -20,9 +16,13 @@ using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Legacy; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Skinning; +using osu.Game.Tests.Resources; +using osuTK; +using osuTK.Graphics; namespace osu.Game.Tests.Beatmaps.Formats { diff --git a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs index e1d6df814e..fabb016d5f 100644 --- a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs +++ b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs @@ -4,6 +4,7 @@ using System.Linq; using NUnit.Framework; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Beatmaps.Legacy; namespace osu.Game.Tests.NonVisual { diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index 7cc4fa6dc4..d2a3b2fc8b 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -14,64 +14,6 @@ using osu.Game.Utils; namespace osu.Game.Beatmaps.ControlPoints { - public class LegacyControlPointInfo : ControlPointInfo - { - /// - /// All sound points. - /// - [JsonProperty] - public IBindableList SamplePoints => samplePoints; - - private readonly BindableList samplePoints = new BindableList(); - - /// - /// Finds the sound control point that is active at . - /// - /// The time to find the sound control point at. - /// The sound control point. - [NotNull] - public SampleControlPoint SamplePointAt(double time) => BinarySearchWithFallback(SamplePoints, time, SamplePoints.Count > 0 ? SamplePoints[0] : SampleControlPoint.DEFAULT); - - public override void Clear() - { - base.Clear(); - samplePoints.Clear(); - } - - protected override bool CheckAlreadyExisting(double time, ControlPoint newPoint) - { - if (newPoint is SampleControlPoint _) - { - var existing = BinarySearch(SamplePoints, time); - return newPoint?.IsRedundant(existing) == true; - } - - return base.CheckAlreadyExisting(time, newPoint); - } - - protected override void GroupItemAdded(ControlPoint controlPoint) - { - if (controlPoint is SampleControlPoint typed) - { - samplePoints.Add(typed); - return; - } - - base.GroupItemAdded(controlPoint); - } - - protected override void GroupItemRemoved(ControlPoint controlPoint) - { - if (controlPoint is SampleControlPoint typed) - { - samplePoints.Remove(typed); - return; - } - - base.GroupItemRemoved(controlPoint); - } - } - [Serializable] public class ControlPointInfo : IDeepCloneable { diff --git a/osu.Game/Beatmaps/Legacy/LegacyControlPointInfo.cs b/osu.Game/Beatmaps/Legacy/LegacyControlPointInfo.cs new file mode 100644 index 0000000000..db9ff27f73 --- /dev/null +++ b/osu.Game/Beatmaps/Legacy/LegacyControlPointInfo.cs @@ -0,0 +1,68 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using JetBrains.Annotations; +using Newtonsoft.Json; +using osu.Framework.Bindables; +using osu.Game.Beatmaps.ControlPoints; + +namespace osu.Game.Beatmaps.Legacy +{ + public class LegacyControlPointInfo : ControlPointInfo + { + /// + /// All sound points. + /// + [JsonProperty] + public IBindableList SamplePoints => samplePoints; + + private readonly BindableList samplePoints = new BindableList(); + + /// + /// Finds the sound control point that is active at . + /// + /// The time to find the sound control point at. + /// The sound control point. + [NotNull] + public SampleControlPoint SamplePointAt(double time) => BinarySearchWithFallback(SamplePoints, time, SamplePoints.Count > 0 ? SamplePoints[0] : SampleControlPoint.DEFAULT); + + public override void Clear() + { + base.Clear(); + samplePoints.Clear(); + } + + protected override bool CheckAlreadyExisting(double time, ControlPoint newPoint) + { + if (newPoint is SampleControlPoint _) + { + var existing = BinarySearch(SamplePoints, time); + return newPoint?.IsRedundant(existing) == true; + } + + return base.CheckAlreadyExisting(time, newPoint); + } + + protected override void GroupItemAdded(ControlPoint controlPoint) + { + if (controlPoint is SampleControlPoint typed) + { + samplePoints.Add(typed); + return; + } + + base.GroupItemAdded(controlPoint); + } + + protected override void GroupItemRemoved(ControlPoint controlPoint) + { + if (controlPoint is SampleControlPoint typed) + { + samplePoints.Remove(typed); + return; + } + + base.GroupItemRemoved(controlPoint); + } + } +} diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index fd9f02604c..3e95659243 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -11,6 +11,7 @@ using osu.Framework.Bindables; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Beatmaps.Legacy; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; diff --git a/osu.Game/Screens/Edit/Timing/SampleSection.cs b/osu.Game/Screens/Edit/Timing/SampleSection.cs index be8de18502..52709a2bbe 100644 --- a/osu.Game/Screens/Edit/Timing/SampleSection.cs +++ b/osu.Game/Screens/Edit/Timing/SampleSection.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 osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; From 6fd24a5d92b933d45a78378ed509e6e444f24561 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 30 Aug 2021 15:17:20 +0900 Subject: [PATCH 1567/2442] Remove redundant null coalesce --- osu.Game/Beatmaps/Legacy/LegacyControlPointInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Legacy/LegacyControlPointInfo.cs b/osu.Game/Beatmaps/Legacy/LegacyControlPointInfo.cs index db9ff27f73..fdfc22700a 100644 --- a/osu.Game/Beatmaps/Legacy/LegacyControlPointInfo.cs +++ b/osu.Game/Beatmaps/Legacy/LegacyControlPointInfo.cs @@ -37,7 +37,7 @@ namespace osu.Game.Beatmaps.Legacy if (newPoint is SampleControlPoint _) { var existing = BinarySearch(SamplePoints, time); - return newPoint?.IsRedundant(existing) == true; + return newPoint.IsRedundant(existing); } return base.CheckAlreadyExisting(time, newPoint); From 6ee4a6526caf053c236984e0eb383576aabbefd7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 30 Aug 2021 16:58:03 +0900 Subject: [PATCH 1568/2442] Don't block sample points from still being added to `ControlPointInfo` --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 9bd76a9f2c..ec08c4a3f3 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -408,7 +408,6 @@ namespace osu.Game.Beatmaps.Formats if (point is SampleControlPoint) { controlPointInfo.Add(time, point); - return; } if (time != pendingControlPointsTime) From d35c4da90658b0fb7361e120dd3a001b2da7be04 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 30 Aug 2021 16:58:21 +0900 Subject: [PATCH 1569/2442] Add new control point to legacy regeneration logic --- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index a6c41a3cf6..574af848d0 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -166,6 +166,30 @@ namespace osu.Game.Beatmaps.Formats writer.WriteLine("[TimingPoints]"); + if (!(beatmap.ControlPointInfo is LegacyControlPointInfo)) // todo: always run this? probably no harm. + { + var legacyControlPoints = new LegacyControlPointInfo(); + + foreach (var point in beatmap.ControlPointInfo.AllControlPoints) + legacyControlPoints.Add(point.Time, point.DeepClone()); + + beatmap.ControlPointInfo = legacyControlPoints; + + SampleControlPoint lastRelevantSamplePoint = null; + + // iterate over hitobjects and pull out all required sample changes + foreach (var h in beatmap.HitObjects) + { + var hSamplePoint = h.SampleControlPoint; + + if (!hSamplePoint.IsRedundant(lastRelevantSamplePoint)) + { + legacyControlPoints.Add(hSamplePoint.Time, hSamplePoint); + lastRelevantSamplePoint = hSamplePoint; + } + } + } + foreach (var group in beatmap.ControlPointInfo.Groups) { var groupTimingPoint = group.ControlPoints.OfType().FirstOrDefault(); @@ -175,17 +199,17 @@ namespace osu.Game.Beatmaps.Formats { writer.Write(FormattableString.Invariant($"{groupTimingPoint.Time},")); writer.Write(FormattableString.Invariant($"{groupTimingPoint.BeatLength},")); - outputControlPointEffectsAt(groupTimingPoint.Time, true); + outputControlPointAt(groupTimingPoint.Time, true); } // Output any remaining effects as secondary non-timing control point. var difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(group.Time); writer.Write(FormattableString.Invariant($"{group.Time},")); writer.Write(FormattableString.Invariant($"{-100 / difficultyPoint.SpeedMultiplier},")); - outputControlPointEffectsAt(group.Time, false); + outputControlPointAt(group.Time, false); } - void outputControlPointEffectsAt(double time, bool isTimingPoint) + void outputControlPointAt(double time, bool isTimingPoint) { var samplePoint = ((LegacyControlPointInfo)beatmap.ControlPointInfo).SamplePointAt(time); var effectPoint = beatmap.ControlPointInfo.EffectPointAt(time); From 2115d6f93e8d2bf844ca1f69c85f7658167663a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 30 Aug 2021 16:57:49 +0900 Subject: [PATCH 1570/2442] Add test coverage of legacy sample point recreation --- .../Formats/LegacyBeatmapEncoderTest.cs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index 855a75117d..812b20d447 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -14,6 +14,7 @@ using osu.Framework.IO.Stores; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Formats; +using osu.Game.Beatmaps.Legacy; using osu.Game.IO; using osu.Game.IO.Serialization; using osu.Game.Rulesets.Catch; @@ -76,6 +77,32 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.That(decodedSlider.Path.ControlPoints.Count, Is.EqualTo(5)); } + [TestCaseSource(nameof(allBeatmaps))] + public void TestEncodeDecodeStabilityWithNonLegacyControlPoints(string name) + { + var decoded = decodeFromLegacy(beatmaps_resource_store.GetStream(name), name); + + sort(decoded.beatmap); + + var originalSerialized = decoded.beatmap.Serialize(); + var encoded = encodeToLegacy(decoded); + + Assert.AreEqual(typeof(LegacyControlPointInfo), decoded.beatmap.ControlPointInfo.GetType()); + + // emulate non-legacy control points by cloning the non-legacy portion. + // the assertion is that the encoder can recreate this losslessly from hitobject data. + decoded.beatmap.ControlPointInfo = decoded.beatmap.ControlPointInfo.DeepClone(); + + Assert.AreNotEqual(typeof(LegacyControlPointInfo), decoded.beatmap.ControlPointInfo.GetType()); + + var decodedAfterEncode = decodeFromLegacy(encoded, name); + + sort(decodedAfterEncode.beatmap); + + Assert.That(decodedAfterEncode.beatmap.Serialize(), Is.EqualTo(originalSerialized)); + Assert.IsTrue(areComboColoursEqual(decodedAfterEncode.beatmapSkin.Configuration, decoded.beatmapSkin.Configuration)); + } + private bool areComboColoursEqual(IHasComboColours a, IHasComboColours b) { // equal to null, no need to SequenceEqual @@ -116,7 +143,7 @@ namespace osu.Game.Tests.Beatmaps.Formats } } - private Stream encodeToLegacy((IBeatmap beatmap, ISkin beatmapSkin) fullBeatmap) + private MemoryStream encodeToLegacy((IBeatmap beatmap, ISkin beatmapSkin) fullBeatmap) { var (beatmap, beatmapSkin) = fullBeatmap; var stream = new MemoryStream(); From 4da2dca33918ebfc5b57e9f0c30ddd6f06927fd5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 30 Aug 2021 17:21:05 +0900 Subject: [PATCH 1571/2442] Apply the default `SampleControlPoint` if not externally provided This is mostly to handle tests for now, as generally this should be provided by an external source in all other cases. --- osu.Game/Rulesets/Objects/HitObject.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index 3e95659243..fb1b6cd267 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -112,6 +112,10 @@ namespace osu.Game.Rulesets.Objects // This is done here since ApplyDefaultsToSelf may be used to determine the end time SampleControlPoint = legacyInfo.SamplePointAt(this.GetEndTime() + control_point_leniency); } + else + { + SampleControlPoint ??= SampleControlPoint.DEFAULT; + } nestedHitObjects.Clear(); From 9fae2c350d0eef1ae7e365e9bc636a76a672135f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 30 Aug 2021 17:25:36 +0900 Subject: [PATCH 1572/2442] Fix test regressions --- osu.Game.Tests/Editing/Checks/CheckMutedObjectsTest.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Editing/Checks/CheckMutedObjectsTest.cs b/osu.Game.Tests/Editing/Checks/CheckMutedObjectsTest.cs index 41a8f72305..4ab6e5cef6 100644 --- a/osu.Game.Tests/Editing/Checks/CheckMutedObjectsTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckMutedObjectsTest.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Beatmaps.Legacy; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Checks; using osu.Game.Rulesets.Objects; @@ -30,7 +31,7 @@ namespace osu.Game.Tests.Editing.Checks { check = new CheckMutedObjects(); - cpi = new ControlPointInfo(); + cpi = new LegacyControlPointInfo(); cpi.Add(0, new SampleControlPoint { SampleVolume = volume_regular }); cpi.Add(1000, new SampleControlPoint { SampleVolume = volume_low }); cpi.Add(2000, new SampleControlPoint { SampleVolume = volume_muted }); From 1aeae2b8c8537927bc9c93553abddf3c1c935b24 Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Mon, 30 Aug 2021 10:11:41 +0100 Subject: [PATCH 1573/2442] reverse ternary operator --- osu.Game/Overlays/UserProfileOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index 6e74acc96a..b0327987f2 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -118,7 +118,7 @@ namespace osu.Game.Overlays if (fetchOnline) { - userReq = user.Username != null ? new GetUserRequest(user.Username) : new GetUserRequest(user.Id); + userReq = user.Id > 1 ? new GetUserRequest(user.Id) : new GetUserRequest(user.Username); userReq.Success += userLoadComplete; API.Queue(userReq); } From 015df282fe61d700aa1ecb8bc41c8200021d3d67 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 30 Aug 2021 18:32:55 +0900 Subject: [PATCH 1574/2442] Simplify copy operations --- .../Visual/Editing/TestSceneEditorSeeking.cs | 2 +- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 11 +---------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorSeeking.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSeeking.cs index 96ce418851..ff741a8ed5 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorSeeking.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSeeking.cs @@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Editing beatmap.BeatmapInfo.BeatDivisor = 1; - beatmap.ControlPointInfo = new ControlPointInfo(); + beatmap.ControlPointInfo.Clear(); beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 1000 }); beatmap.ControlPointInfo.Add(2000, new TimingControlPoint { BeatLength = 500 }); diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index ec08c4a3f3..accefb2583 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -401,15 +401,8 @@ namespace osu.Game.Beatmaps.Formats private readonly HashSet pendingControlPointTypes = new HashSet(); private double pendingControlPointsTime; - private readonly LegacyControlPointInfo controlPointInfo = new LegacyControlPointInfo(); - private void addControlPoint(double time, ControlPoint point, bool timingChange) { - if (point is SampleControlPoint) - { - controlPointInfo.Add(time, point); - } - if (time != pendingControlPointsTime) flushPendingPoints(); @@ -447,9 +440,7 @@ namespace osu.Game.Beatmaps.Formats if (obj != null) { - // assign legacy control points directly to hitobject - //obj.SampleControlPoint = controlPointInfo.SamplePointAt(obj.StartTime); - obj.ApplyDefaults(controlPointInfo, beatmap.BeatmapInfo.BaseDifficulty); + obj.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty); beatmap.HitObjects.Add(obj); } From 1aaea7011ae2bc308913a59523503f549b67deee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 30 Aug 2021 18:33:05 +0900 Subject: [PATCH 1575/2442] Fix early return causing event loss in case of multiple control points in group --- osu.Game/Beatmaps/Legacy/LegacyControlPointInfo.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game/Beatmaps/Legacy/LegacyControlPointInfo.cs b/osu.Game/Beatmaps/Legacy/LegacyControlPointInfo.cs index fdfc22700a..5152549bed 100644 --- a/osu.Game/Beatmaps/Legacy/LegacyControlPointInfo.cs +++ b/osu.Game/Beatmaps/Legacy/LegacyControlPointInfo.cs @@ -46,10 +46,7 @@ namespace osu.Game.Beatmaps.Legacy protected override void GroupItemAdded(ControlPoint controlPoint) { if (controlPoint is SampleControlPoint typed) - { samplePoints.Add(typed); - return; - } base.GroupItemAdded(controlPoint); } @@ -57,10 +54,7 @@ namespace osu.Game.Beatmaps.Legacy protected override void GroupItemRemoved(ControlPoint controlPoint) { if (controlPoint is SampleControlPoint typed) - { samplePoints.Remove(typed); - return; - } base.GroupItemRemoved(controlPoint); } From 04bf667d0db6de78dfb8e3e17a0f3d371d42023b Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Mon, 30 Aug 2021 17:49:18 +0800 Subject: [PATCH 1576/2442] Parse partially typed enum names in filter query --- osu.Game/Screens/Select/FilterQueryParser.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/FilterQueryParser.cs b/osu.Game/Screens/Select/FilterQueryParser.cs index 72d10019b2..591a632a7c 100644 --- a/osu.Game/Screens/Select/FilterQueryParser.cs +++ b/osu.Game/Screens/Select/FilterQueryParser.cs @@ -3,8 +3,8 @@ using System; using System.Globalization; +using System.Linq; using System.Text.RegularExpressions; -using osu.Game.Beatmaps; using osu.Game.Screens.Select.Filter; namespace osu.Game.Screens.Select @@ -64,8 +64,7 @@ namespace osu.Game.Screens.Select return TryUpdateCriteriaRange(ref criteria.BeatDivisor, op, value, tryParseInt); case "status": - return TryUpdateCriteriaRange(ref criteria.OnlineStatus, op, value, - (string s, out BeatmapSetOnlineStatus val) => Enum.TryParse(value, true, out val)); + return TryUpdateCriteriaRange(ref criteria.OnlineStatus, op, value, tryParseEnum); case "creator": return TryUpdateCriteriaText(ref criteria.Creator, op, value); @@ -120,6 +119,14 @@ namespace osu.Game.Screens.Select private static bool tryParseInt(string value, out int result) => int.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out result); + private static bool tryParseEnum(string value, out TEnum result) where TEnum : struct + { + if (Enum.TryParse(value, true, out result)) return true; + + string status = Enum.GetNames(typeof(TEnum)).FirstOrDefault(name => name.StartsWith(value, true, CultureInfo.InvariantCulture)); + return Enum.TryParse(status, true, out result); + } + /// /// Attempts to parse a keyword filter with the specified and textual . /// If the value indicates a valid textual filter, the function returns true and the resulting data is stored into From 47061c0210ad37b80ba5778bf205ca91dcc0b132 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 30 Aug 2021 18:57:30 +0900 Subject: [PATCH 1577/2442] Trigger refresh on scoring mode change --- .../BeatmapSet/Scores/ScoresContainer.cs | 71 ++++++++++++------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index aff48919b4..bfdb90c36a 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -12,8 +12,11 @@ using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Framework.Bindables; +using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; using osu.Game.Screens.Select.Leaderboards; using osu.Game.Users; @@ -27,6 +30,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private readonly Bindable ruleset = new Bindable(); private readonly Bindable scope = new Bindable(BeatmapLeaderboardScope.Global); private readonly IBindable user = new Bindable(); + private readonly Bindable scoringMode = new Bindable(); private readonly Box background; private readonly ScoreTable scoreTable; @@ -42,35 +46,20 @@ namespace osu.Game.Overlays.BeatmapSet.Scores [Resolved] private RulesetStore rulesets { get; set; } + [Resolved] + private ScoreManager scoreManager { get; set; } + private GetScoresRequest getScoresRequest; + private APILegacyScores scores; + protected APILegacyScores Scores { - set => Schedule(() => + set { - topScoresContainer.Clear(); - - if (value?.Scores.Any() != true) - { - scoreTable.ClearScores(); - scoreTable.Hide(); - return; - } - - var scoreInfos = value.Scores.Select(s => s.CreateScoreInfo(rulesets)).ToList(); - var topScore = scoreInfos.First(); - - scoreTable.DisplayScores(scoreInfos, topScore.Beatmap?.Status.GrantsPerformancePoints() == true); - scoreTable.Show(); - - var userScore = value.UserScore; - var userScoreInfo = userScore?.Score.CreateScoreInfo(rulesets); - - topScoresContainer.Add(new DrawableTopScore(topScore)); - - if (userScoreInfo != null && userScoreInfo.OnlineScoreID != topScore.OnlineScoreID) - topScoresContainer.Add(new DrawableTopScore(userScoreInfo, userScore.Position)); - }); + scores = value; + displayScores(value); + } } public ScoresContainer() @@ -166,11 +155,12 @@ namespace osu.Game.Overlays.BeatmapSet.Scores } [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) + private void load(OverlayColourProvider colourProvider, OsuConfigManager config) { background.Colour = colourProvider.Background5; user.BindTo(api.LocalUser); + config.BindWith(OsuSetting.ScoreDisplayMode, scoringMode); } protected override void LoadComplete() @@ -183,6 +173,8 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Beatmap.BindValueChanged(onBeatmapChanged); user.BindValueChanged(onUserChanged, true); + + scoringMode.BindValueChanged(_ => displayScores(scores)); } private void onBeatmapChanged(ValueChangedEvent beatmap) @@ -254,6 +246,35 @@ namespace osu.Game.Overlays.BeatmapSet.Scores api.Queue(getScoresRequest); } + private void displayScores(APILegacyScores newScores) + { + Schedule(() => + { + topScoresContainer.Clear(); + + if (newScores?.Scores.Any() != true) + { + scoreTable.ClearScores(); + scoreTable.Hide(); + return; + } + + var scoreInfos = newScores.Scores.Select(s => s.CreateScoreInfo(rulesets)).ToList(); + var topScore = scoreInfos.First(); + + scoreTable.DisplayScores(scoreInfos, topScore.Beatmap?.Status.GrantsPerformancePoints() == true); + scoreTable.Show(); + + var userScore = newScores.UserScore; + var userScoreInfo = userScore?.Score.CreateScoreInfo(rulesets); + + topScoresContainer.Add(new DrawableTopScore(topScore)); + + if (userScoreInfo != null && userScoreInfo.OnlineScoreID != topScore.OnlineScoreID) + topScoresContainer.Add(new DrawableTopScore(userScoreInfo, userScore.Position)); + }); + } + private bool userIsSupporter => api.IsLoggedIn && api.LocalUser.Value.IsSupporter; } } From b217dd1a658b590625a913f361e596ea8587a82c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 30 Aug 2021 19:03:16 +0900 Subject: [PATCH 1578/2442] Order scores by score --- osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index bfdb90c36a..1e5f7c3f72 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -259,7 +259,10 @@ namespace osu.Game.Overlays.BeatmapSet.Scores return; } - var scoreInfos = newScores.Scores.Select(s => s.CreateScoreInfo(rulesets)).ToList(); + var scoreInfos = newScores.Scores.Select(s => s.CreateScoreInfo(rulesets)) + .OrderByDescending(s => scoreManager.GetBindableTotalScore(s).Value) + .ToList(); + var topScore = scoreInfos.First(); scoreTable.DisplayScores(scoreInfos, topScore.Beatmap?.Status.GrantsPerformancePoints() == true); From 8137eee527e93bc5963533b0ea3404cde110c4b1 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Mon, 30 Aug 2021 18:05:47 +0800 Subject: [PATCH 1579/2442] Reuse `value` to save enum name Co-authored-by: Salman Ahmed --- osu.Game/Screens/Select/FilterQueryParser.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/FilterQueryParser.cs b/osu.Game/Screens/Select/FilterQueryParser.cs index 591a632a7c..a882148392 100644 --- a/osu.Game/Screens/Select/FilterQueryParser.cs +++ b/osu.Game/Screens/Select/FilterQueryParser.cs @@ -123,8 +123,8 @@ namespace osu.Game.Screens.Select { if (Enum.TryParse(value, true, out result)) return true; - string status = Enum.GetNames(typeof(TEnum)).FirstOrDefault(name => name.StartsWith(value, true, CultureInfo.InvariantCulture)); - return Enum.TryParse(status, true, out result); + value = Enum.GetNames(typeof(TEnum)).FirstOrDefault(name => name.StartsWith(value, true, CultureInfo.InvariantCulture)); + return Enum.TryParse(value, true, out result); } /// From d03950fb370869e18f2e9050b2148a06b0393084 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 30 Aug 2021 19:33:09 +0900 Subject: [PATCH 1580/2442] Move score calculation to ScoreManager --- osu.Game/Scoring/ScoreManager.cs | 144 ++++++++++++++----------------- 1 file changed, 67 insertions(+), 77 deletions(-) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 83bcac01ac..f310462d6e 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -34,6 +34,7 @@ namespace osu.Game.Scoring protected override string ImportFromStablePath => Path.Combine("Data", "r"); + private readonly Bindable scoringMode = new Bindable(); private readonly RulesetStore rulesets; private readonly Func beatmaps; @@ -51,6 +52,8 @@ namespace osu.Game.Scoring this.beatmaps = beatmaps; this.difficulties = difficulties; this.configManager = configManager; + + configManager?.BindWith(OsuSetting.ScoreDisplayMode, scoringMode); } protected override ScoreInfo CreateModel(ArchiveReader archive) @@ -113,7 +116,7 @@ namespace osu.Game.Scoring /// The bindable containing the total score. public Bindable GetBindableTotalScore(ScoreInfo score) { - var bindable = new TotalScoreBindable(score, difficulties); + var bindable = new TotalScoreBindable(score, this); configManager?.BindWith(OsuSetting.ScoreDisplayMode, bindable.ScoringMode); return bindable; } @@ -128,6 +131,63 @@ namespace osu.Game.Scoring /// The bindable containing the formatted total score string. public Bindable GetBindableTotalScoreString(ScoreInfo score) => new TotalScoreStringBindable(GetBindableTotalScore(score)); + public long GetTotalScore(ScoreInfo score) => GetTotalScoreAsync(score).Result; + + public async Task GetTotalScoreAsync(ScoreInfo score, CancellationToken cancellationToken = default) + { + if (score.Beatmap == null) + return score.TotalScore; + + int beatmapMaxCombo; + double accuracy = score.Accuracy; + + if (score.IsLegacyScore) + { + if (score.RulesetID == 3) + { + // In osu!stable, a full-GREAT score has 100% accuracy in mania. Along with a full combo, the score becomes indistinguishable from a full-PERFECT score. + // To get around this, recalculate accuracy based on the hit statistics. + // Note: This cannot be applied universally to all legacy scores, as some rulesets (e.g. catch) group multiple judgements together. + double maxBaseScore = score.Statistics.Select(kvp => kvp.Value).Sum() * Judgement.ToNumericResult(HitResult.Perfect); + double baseScore = score.Statistics.Select(kvp => Judgement.ToNumericResult(kvp.Key) * kvp.Value).Sum(); + if (maxBaseScore > 0) + accuracy = baseScore / maxBaseScore; + } + + // This score is guaranteed to be an osu!stable score. + // The combo must be determined through either the beatmap's max combo value or the difficulty calculator, as lazer's scoring has changed and the score statistics cannot be used. + if (score.Beatmap.MaxCombo != null) + beatmapMaxCombo = score.Beatmap.MaxCombo.Value; + else + { + if (score.Beatmap.ID == 0 || difficulties == null) + { + // We don't have enough information (max combo) to compute the score, so use the provided score. + return score.TotalScore; + } + + // We can compute the max combo locally after the async beatmap difficulty computation. + var difficulty = await difficulties().GetDifficultyAsync(score.Beatmap, score.Ruleset, score.Mods, cancellationToken).ConfigureAwait(false); + beatmapMaxCombo = difficulty.MaxCombo; + } + } + else + { + // This is guaranteed to be a non-legacy score. + // The combo must be determined through the score's statistics, as both the beatmap's max combo and the difficulty calculator will provide osu!stable combo values. + beatmapMaxCombo = Enum.GetValues(typeof(HitResult)).OfType().Where(r => r.AffectsCombo()).Select(r => score.Statistics.GetValueOrDefault(r)).Sum(); + } + + if (beatmapMaxCombo == 0) + return 0; + + var ruleset = score.Ruleset.CreateInstance(); + var scoreProcessor = ruleset.CreateScoreProcessor(); + scoreProcessor.Mods.Value = score.Mods; + + return (long)Math.Round(scoreProcessor.GetScore(scoringMode.Value, beatmapMaxCombo, accuracy, (double)score.MaxCombo / beatmapMaxCombo, score.Statistics)); + } + /// /// Provides the total score of a . Responds to changes in the currently-selected . /// @@ -136,99 +196,29 @@ namespace osu.Game.Scoring public readonly Bindable ScoringMode = new Bindable(); private readonly ScoreInfo score; - private readonly Func difficulties; + private readonly ScoreManager scoreManager; /// /// Creates a new . /// /// The to provide the total score of. - /// A function to retrieve the . - public TotalScoreBindable(ScoreInfo score, Func difficulties) + /// The . + public TotalScoreBindable(ScoreInfo score, ScoreManager scoreManager) { this.score = score; - this.difficulties = difficulties; + this.scoreManager = scoreManager; ScoringMode.BindValueChanged(onScoringModeChanged, true); } - private IBindable difficultyBindable; private CancellationTokenSource difficultyCancellationSource; private void onScoringModeChanged(ValueChangedEvent mode) { difficultyCancellationSource?.Cancel(); - difficultyCancellationSource = null; + difficultyCancellationSource = new CancellationTokenSource(); - if (score.Beatmap == null) - { - Value = score.TotalScore; - return; - } - - int beatmapMaxCombo; - double accuracy = score.Accuracy; - - if (score.IsLegacyScore) - { - if (score.RulesetID == 3) - { - // In osu!stable, a full-GREAT score has 100% accuracy in mania. Along with a full combo, the score becomes indistinguishable from a full-PERFECT score. - // To get around this, recalculate accuracy based on the hit statistics. - // Note: This cannot be applied universally to all legacy scores, as some rulesets (e.g. catch) group multiple judgements together. - double maxBaseScore = score.Statistics.Select(kvp => kvp.Value).Sum() * Judgement.ToNumericResult(HitResult.Perfect); - double baseScore = score.Statistics.Select(kvp => Judgement.ToNumericResult(kvp.Key) * kvp.Value).Sum(); - if (maxBaseScore > 0) - accuracy = baseScore / maxBaseScore; - } - - // This score is guaranteed to be an osu!stable score. - // The combo must be determined through either the beatmap's max combo value or the difficulty calculator, as lazer's scoring has changed and the score statistics cannot be used. - if (score.Beatmap.MaxCombo == null) - { - if (score.Beatmap.ID == 0 || difficulties == null) - { - // We don't have enough information (max combo) to compute the score, so use the provided score. - Value = score.TotalScore; - return; - } - - // We can compute the max combo locally after the async beatmap difficulty computation. - difficultyBindable = difficulties().GetBindableDifficulty(score.Beatmap, score.Ruleset, score.Mods, (difficultyCancellationSource = new CancellationTokenSource()).Token); - difficultyBindable.BindValueChanged(d => - { - if (d.NewValue is StarDifficulty diff) - updateScore(diff.MaxCombo, accuracy); - }, true); - - return; - } - - beatmapMaxCombo = score.Beatmap.MaxCombo.Value; - } - else - { - // This is guaranteed to be a non-legacy score. - // The combo must be determined through the score's statistics, as both the beatmap's max combo and the difficulty calculator will provide osu!stable combo values. - beatmapMaxCombo = Enum.GetValues(typeof(HitResult)).OfType().Where(r => r.AffectsCombo()).Select(r => score.Statistics.GetValueOrDefault(r)).Sum(); - } - - updateScore(beatmapMaxCombo, accuracy); - } - - private void updateScore(int beatmapMaxCombo, double accuracy) - { - if (beatmapMaxCombo == 0) - { - Value = 0; - return; - } - - var ruleset = score.Ruleset.CreateInstance(); - var scoreProcessor = ruleset.CreateScoreProcessor(); - - scoreProcessor.Mods.Value = score.Mods; - - Value = (long)Math.Round(scoreProcessor.GetScore(ScoringMode.Value, beatmapMaxCombo, accuracy, (double)score.MaxCombo / beatmapMaxCombo, score.Statistics)); + scoreManager.GetTotalScoreAsync(score, difficultyCancellationSource.Token).ContinueWith(s => Value = s.Result, TaskContinuationOptions.OnlyOnRanToCompletion); } } From 458ce250f01c7d81f05c78d2382d1151726cc82a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 30 Aug 2021 19:34:12 +0900 Subject: [PATCH 1581/2442] Use new ScoreManager method in ScoreTable --- osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs | 4 ++-- osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index a154016824..847df53edc 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -151,8 +151,8 @@ namespace osu.Game.Overlays.BeatmapSet.Scores new OsuSpriteText { Margin = new MarginPadding { Right = horizontal_inset }, - Current = scoreManager.GetBindableTotalScoreString(score), - Font = OsuFont.GetFont(size: text_size, weight: index == 0 ? FontWeight.Bold : FontWeight.Medium) + Font = OsuFont.GetFont(size: text_size, weight: index == 0 ? FontWeight.Bold : FontWeight.Medium), + Text = scoreManager.GetTotalScore(score).ToLocalisableString(@"N0"), }, new OsuSpriteText { diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 1e5f7c3f72..a0ccb5a393 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -260,7 +260,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores } var scoreInfos = newScores.Scores.Select(s => s.CreateScoreInfo(rulesets)) - .OrderByDescending(s => scoreManager.GetBindableTotalScore(s).Value) + .OrderByDescending(s => scoreManager.GetTotalScore(s)) .ToList(); var topScore = scoreInfos.First(); From 4ebb11472df20b65efc4e0f13cfb4bac6f7a6878 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 30 Aug 2021 19:34:34 +0900 Subject: [PATCH 1582/2442] Update Leaderboard to reorder scores based on scoring mode --- osu.Game/Online/Leaderboards/Leaderboard.cs | 8 +++++++- osu.Game/Online/Leaderboards/LeaderboardScore.cs | 2 +- .../Screens/Select/Leaderboards/BeatmapLeaderboard.cs | 4 ++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index 4f8b27602b..f3a06994ee 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -13,11 +13,13 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Threading; +using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.Placeholders; +using osu.Game.Rulesets.Scoring; using osuTK; using osuTK.Graphics; @@ -27,6 +29,7 @@ namespace osu.Game.Online.Leaderboards { private const double fade_duration = 300; + private readonly Bindable scoringMode = new Bindable(); private readonly OsuScrollContainer scrollContainer; private readonly Container placeholderContainer; private readonly UserTopScoreContainer topScoreContainer; @@ -246,12 +249,15 @@ namespace osu.Game.Online.Leaderboards private readonly IBindable apiState = new Bindable(); [BackgroundDependencyLoader] - private void load() + private void load(OsuConfigManager configManager) { if (api != null) apiState.BindTo(api.State); apiState.BindValueChanged(onlineStateChanged, true); + + configManager.BindWith(OsuSetting.ScoreDisplayMode, scoringMode); + scoringMode.BindValueChanged(_ => RefreshScores(), true); } private APIRequest getScoresRequest; diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index 934b905a1a..206b0e7936 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -198,8 +198,8 @@ namespace osu.Game.Online.Leaderboards { TextColour = Color4.White, GlowColour = Color4Extensions.FromHex(@"83ccfa"), - Current = scoreManager.GetBindableTotalScoreString(score), Font = OsuFont.Numeric.With(size: 23), + Text = scoreManager.GetTotalScore(score).ToLocalisableString(@"N0"), }, RankContainer = new Container { diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index a86a614a05..04e7172519 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -146,7 +146,7 @@ namespace osu.Game.Screens.Select.Leaderboards scores = scores.Where(s => s.Mods.Any(m => selectedMods.Contains(m.Acronym))); } - Scores = scores.OrderByDescending(s => s.TotalScore).ToArray(); + Scores = scores.OrderByDescending(s => scoreManager.GetTotalScore(s)).ToArray(); PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores; return null; @@ -182,7 +182,7 @@ namespace osu.Game.Screens.Select.Leaderboards req.Success += r => { - scoresCallback?.Invoke(r.Scores.Select(s => s.CreateScoreInfo(rulesets))); + scoresCallback?.Invoke(r.Scores.Select(s => s.CreateScoreInfo(rulesets)).OrderByDescending(s => scoreManager.GetTotalScore(s))); TopScore = r.UserScore?.CreateScoreInfo(rulesets); }; From e19d81c88ce21aba8cf19219142a60a9ff356236 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 30 Aug 2021 19:41:44 +0900 Subject: [PATCH 1583/2442] Fix potential incorrect ordering --- .../Overlays/BeatmapSet/Scores/ScoresContainer.cs | 1 + .../Screens/Select/Leaderboards/BeatmapLeaderboard.cs | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index a0ccb5a393..91c3dfad65 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -261,6 +261,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores var scoreInfos = newScores.Scores.Select(s => s.CreateScoreInfo(rulesets)) .OrderByDescending(s => scoreManager.GetTotalScore(s)) + .ThenBy(s => s.OnlineScoreID) .ToList(); var topScore = scoreInfos.First(); diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 04e7172519..0276ae8b2a 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -146,7 +146,10 @@ namespace osu.Game.Screens.Select.Leaderboards scores = scores.Where(s => s.Mods.Any(m => selectedMods.Contains(m.Acronym))); } - Scores = scores.OrderByDescending(s => scoreManager.GetTotalScore(s)).ToArray(); + Scores = scores.OrderByDescending(s => scoreManager.GetTotalScore(s)) + .ThenBy(s => s.OnlineScoreID) + .ToArray(); + PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores; return null; @@ -182,7 +185,11 @@ namespace osu.Game.Screens.Select.Leaderboards req.Success += r => { - scoresCallback?.Invoke(r.Scores.Select(s => s.CreateScoreInfo(rulesets)).OrderByDescending(s => scoreManager.GetTotalScore(s))); + scoresCallback?.Invoke( + r.Scores.Select(s => s.CreateScoreInfo(rulesets)) + .OrderByDescending(s => scoreManager.GetTotalScore(s)) + .ThenBy(s => s.OnlineScoreID)); + TopScore = r.UserScore?.CreateScoreInfo(rulesets); }; From caa797cbf46ad46d45832a14b709a8b9641fea6f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 30 Aug 2021 19:58:35 +0900 Subject: [PATCH 1584/2442] Attempt to reorder score panel list --- osu.Game/Screens/Ranking/ScorePanelList.cs | 48 +++++++++++++++------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index e170241ede..c9ddfd27d3 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -5,11 +5,14 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; +using osu.Game.Configuration; using osu.Game.Graphics.Containers; +using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osuTK; using osuTK.Input; @@ -289,27 +292,42 @@ namespace osu.Game.Screens.Ranking { public override IEnumerable FlowingChildren => applySorting(AliveInternalChildren); + private readonly Bindable scoringMode = new Bindable(); + + [Resolved] + private ScoreManager scoreManager { get; set; } + + [BackgroundDependencyLoader] + private void load(OsuConfigManager configManager) + { + configManager.BindWith(OsuSetting.ScoreDisplayMode, scoringMode); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + scoringMode.BindValueChanged(mode => + { + foreach (var c in Children) + SetLayoutPosition(c, scoreManager.GetTotalScore(c.Panel.Score)); + }, true); + } + public int GetPanelIndex(ScoreInfo score) => applySorting(Children).TakeWhile(s => s.Panel.Score != score).Count(); - private IEnumerable applySorting(IEnumerable drawables) => drawables.OfType() - .OrderByDescending(s => s.Panel.Score.TotalScore) - .ThenBy(s => s.Panel.Score.OnlineScoreID); - - protected override int Compare(Drawable x, Drawable y) + public override void Add(ScorePanelTrackingContainer drawable) { - var tX = (ScorePanelTrackingContainer)x; - var tY = (ScorePanelTrackingContainer)y; + Debug.Assert(drawable != null); - int result = tY.Panel.Score.TotalScore.CompareTo(tX.Panel.Score.TotalScore); + base.Add(drawable); - if (result != 0) - return result; - - if (tX.Panel.Score.OnlineScoreID == null || tY.Panel.Score.OnlineScoreID == null) - return base.Compare(x, y); - - return tX.Panel.Score.OnlineScoreID.Value.CompareTo(tY.Panel.Score.OnlineScoreID.Value); + SetLayoutPosition(drawable, scoreManager?.GetTotalScore(drawable.Panel.Score) ?? 0); } + + private IEnumerable applySorting(IEnumerable drawables) => drawables.OfType() + .OrderByDescending(GetLayoutPosition) + .ThenBy(s => s.Panel.Score.OnlineScoreID); } private class Scroll : OsuScrollContainer From c789163d01e367f867e8f4a754516c6e895ade24 Mon Sep 17 00:00:00 2001 From: rednir Date: Mon, 30 Aug 2021 13:22:12 +0100 Subject: [PATCH 1585/2442] use user ID overload when its supposed to be used Co-authored-by: Salman Ahmed --- osu.Game/OsuGame.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 26fa1d5a4c..1e6e1e0ead 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -329,7 +329,11 @@ namespace osu.Game break; case LinkAction.OpenUserProfile: - ShowUser(link.Argument); + if (int.TryParse(link.Argument, out var userId)) + ShowUser(userId); + else + ShowUser(link.Argument); + break; case LinkAction.OpenWiki: From 8104b15874c5b358c22f7d3f8d6fb9ac42b09b94 Mon Sep 17 00:00:00 2001 From: rednir Date: Mon, 30 Aug 2021 13:23:33 +0100 Subject: [PATCH 1586/2442] remove braces Co-authored-by: Salman Ahmed --- osu.Game/Online/API/Requests/GetUserRequest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/GetUserRequest.cs b/osu.Game/Online/API/Requests/GetUserRequest.cs index 48041cd40c..0c8a4a3578 100644 --- a/osu.Game/Online/API/Requests/GetUserRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRequest.cs @@ -27,6 +27,6 @@ namespace osu.Game.Online.API.Requests Ruleset = ruleset; } - protected override string Target => (userIdentifier != null) ? $@"users/{userIdentifier}/{Ruleset?.ShortName}" : $@"me/{Ruleset?.ShortName}"; + protected override string Target => userIdentifier != null ? $@"users/{userIdentifier}/{Ruleset?.ShortName}" : $@"me/{Ruleset?.ShortName}"; } } From a2cff75fc0183f0c0e899f2fc0cba05c1898779e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 30 Aug 2021 21:55:08 +0900 Subject: [PATCH 1587/2442] Fix editor not cloning control points as expected --- .../Beatmaps/Formats/LegacyBeatmapEncoderTest.cs | 7 ++++++- osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index 812b20d447..e66221514c 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -91,7 +91,12 @@ namespace osu.Game.Tests.Beatmaps.Formats // emulate non-legacy control points by cloning the non-legacy portion. // the assertion is that the encoder can recreate this losslessly from hitobject data. - decoded.beatmap.ControlPointInfo = decoded.beatmap.ControlPointInfo.DeepClone(); + var controlPointInfo = new ControlPointInfo(); + + foreach (var point in decoded.beatmap.ControlPointInfo.AllControlPoints) + controlPointInfo.Add(point.Time, point.DeepClone()); + + decoded.beatmap.ControlPointInfo = controlPointInfo; Assert.AreNotEqual(typeof(LegacyControlPointInfo), decoded.beatmap.ControlPointInfo.GetType()); diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index d2a3b2fc8b..3ff40fe194 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -324,7 +324,7 @@ namespace osu.Game.Beatmaps.ControlPoints public ControlPointInfo DeepClone() { - var controlPointInfo = new ControlPointInfo(); + var controlPointInfo = (ControlPointInfo)Activator.CreateInstance(GetType()); foreach (var point in AllControlPoints) controlPointInfo.Add(point.Time, point.DeepClone()); From acf38c723a5fd681ba62c83cf1629f4d26783629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 22 Aug 2021 14:11:57 +0200 Subject: [PATCH 1588/2442] Move labelled dropdown from tournament to main game --- .../Screens/Setup/SetupScreen.cs | 22 ------------- .../UserInterfaceV2/LabelledDropdown.cs | 31 +++++++++++++++++++ 2 files changed, 31 insertions(+), 22 deletions(-) create mode 100644 osu.Game/Graphics/UserInterfaceV2/LabelledDropdown.cs diff --git a/osu.Game.Tournament/Screens/Setup/SetupScreen.cs b/osu.Game.Tournament/Screens/Setup/SetupScreen.cs index 5d8f0405ca..f6d28c15e0 100644 --- a/osu.Game.Tournament/Screens/Setup/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/Setup/SetupScreen.cs @@ -1,14 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; using System.Drawing; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Online.API; using osu.Game.Overlays; @@ -131,25 +129,5 @@ namespace osu.Game.Tournament.Screens.Setup resolution.Value = $"{ScreenSpaceDrawQuad.Width:N0}x{ScreenSpaceDrawQuad.Height:N0}"; } - - public class LabelledDropdown : LabelledComponent, T> - { - public LabelledDropdown() - : base(true) - { - } - - public IEnumerable Items - { - get => Component.Items; - set => Component.Items = value; - } - - protected override OsuDropdown CreateComponent() => new OsuDropdown - { - RelativeSizeAxes = Axes.X, - Width = 0.5f, - }; - } } } diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledDropdown.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledDropdown.cs new file mode 100644 index 0000000000..44f09f13eb --- /dev/null +++ b/osu.Game/Graphics/UserInterfaceV2/LabelledDropdown.cs @@ -0,0 +1,31 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Graphics.UserInterfaceV2 +{ + public class LabelledDropdown : LabelledComponent, TItem> + { + public LabelledDropdown() + : base(true) + { + } + + public IEnumerable Items + { + get => Component.Items; + set => Component.Items = value; + } + + protected sealed override OsuDropdown CreateComponent() => CreateDropdown().With(d => + { + d.RelativeSizeAxes = Axes.X; + d.Width = 0.5f; + }); + + protected virtual OsuDropdown CreateDropdown() => new OsuDropdown(); + } +} From a6d09b0bb0d4f05533baf2b658b22076e4791c11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 22 Aug 2021 14:12:10 +0200 Subject: [PATCH 1589/2442] Add labelled enum dropdown variant --- .../UserInterfaceV2/LabelledEnumDropdown.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 osu.Game/Graphics/UserInterfaceV2/LabelledEnumDropdown.cs diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledEnumDropdown.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledEnumDropdown.cs new file mode 100644 index 0000000000..b818c394ae --- /dev/null +++ b/osu.Game/Graphics/UserInterfaceV2/LabelledEnumDropdown.cs @@ -0,0 +1,14 @@ +// 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 osu.Game.Graphics.UserInterface; + +namespace osu.Game.Graphics.UserInterfaceV2 +{ + public class LabelledEnumDropdown : LabelledDropdown + where TEnum : struct, Enum + { + protected override OsuDropdown CreateDropdown() => new OsuEnumDropdown(); + } +} From 89429021c999913a057c73d8d3eb263c02ff6bdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 22 Aug 2021 14:12:17 +0200 Subject: [PATCH 1590/2442] Add test scene for labelled dropdowns --- .../TestSceneLabelledDropdown.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneLabelledDropdown.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledDropdown.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledDropdown.cs new file mode 100644 index 0000000000..4b74e37ec4 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledDropdown.cs @@ -0,0 +1,34 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Graphics.UserInterfaceV2; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneLabelledDropdown : OsuTestScene + { + [Test] + public void TestLabelledDropdown() + => AddStep(@"create dropdown", () => Child = new LabelledDropdown + { + Label = @"Countdown speed", + Items = new[] + { + @"Half", + @"Normal", + @"Double" + }, + Description = @"This is a description" + }); + + [Test] + public void TestLabelledEnumDropdown() + => AddStep(@"create dropdown", () => Child = new LabelledEnumDropdown + { + Label = @"Beatmap status", + Description = @"This is a description" + }); + } +} From 48e56adcfe500c6f72d653aa170ea1dbcd2f817a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 22 Aug 2021 15:53:30 +0200 Subject: [PATCH 1591/2442] Add labelled number box control --- .../Graphics/UserInterfaceV2/LabelledNumberBox.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 osu.Game/Graphics/UserInterfaceV2/LabelledNumberBox.cs diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledNumberBox.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledNumberBox.cs new file mode 100644 index 0000000000..ca247ab679 --- /dev/null +++ b/osu.Game/Graphics/UserInterfaceV2/LabelledNumberBox.cs @@ -0,0 +1,12 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Graphics.UserInterfaceV2 +{ + public class LabelledNumberBox : LabelledTextBox + { + protected override OsuTextBox CreateTextBox() => new OsuNumberBox(); + } +} From eec9f6d19168adbc563b443ab0429c8aa7bdb2ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 22 Aug 2021 15:53:48 +0200 Subject: [PATCH 1592/2442] Add countdown settings to design section --- osu.Game/Screens/Edit/Setup/DesignSection.cs | 64 ++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/osu.Game/Screens/Edit/Setup/DesignSection.cs b/osu.Game/Screens/Edit/Setup/DesignSection.cs index 68aaf3dd76..633196ff2e 100644 --- a/osu.Game/Screens/Edit/Setup/DesignSection.cs +++ b/osu.Game/Screens/Edit/Setup/DesignSection.cs @@ -1,14 +1,28 @@ // 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.Globalization; +using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; +using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterfaceV2; +using osuTK; namespace osu.Game.Screens.Edit.Setup { internal class DesignSection : SetupSection { + private const float fade_duration = 250; + + private LabelledSwitchButton enableCountdown; + private FillFlowContainer countdownSettings; + private LabelledEnumDropdown countdownSpeed; + private LabelledNumberBox countdownOffset; + private LabelledSwitchButton widescreenSupport; private LabelledSwitchButton epilepsyWarning; private LabelledSwitchButton letterboxDuringBreaks; @@ -20,6 +34,35 @@ namespace osu.Game.Screens.Edit.Setup { Children = new[] { + enableCountdown = new LabelledSwitchButton + { + Label = "Enable countdown", + Current = { Value = Beatmap.BeatmapInfo.Countdown != CountdownType.None }, + Description = "If enabled, an \"Are you ready? 3, 2, 1, GO!\" countdown will be inserted at the beginning of the beatmap, assuming there is enough time to do so." + }, + countdownSettings = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(10), + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + countdownSpeed = new LabelledEnumDropdown + { + Label = "Countdown speed", + Current = { Value = Beatmap.BeatmapInfo.Countdown != CountdownType.None ? Beatmap.BeatmapInfo.Countdown : CountdownType.Normal }, + Items = Enum.GetValues(typeof(CountdownType)).Cast().Where(type => type != CountdownType.None) + }, + countdownOffset = new LabelledNumberBox + { + Label = "Countdown offset", + Current = { Value = Beatmap.BeatmapInfo.CountdownOffset.ToString() }, + Description = "If the countdown sounds off-time, use this to make it appear one or more beats early.", + } + } + }, + Empty(), widescreenSupport = new LabelledSwitchButton { Label = "Widescreen support", @@ -45,13 +88,34 @@ namespace osu.Game.Screens.Edit.Setup { base.LoadComplete(); + enableCountdown.Current.BindValueChanged(_ => updateCountdownSettingsVisibility(), true); + countdownSettings.FinishTransforms(true); + + enableCountdown.Current.BindValueChanged(_ => updateBeatmap()); + countdownSpeed.Current.BindValueChanged(_ => updateBeatmap()); + countdownOffset.OnCommit += (_, __) => updateBeatmap(); + widescreenSupport.Current.BindValueChanged(_ => updateBeatmap()); epilepsyWarning.Current.BindValueChanged(_ => updateBeatmap()); letterboxDuringBreaks.Current.BindValueChanged(_ => updateBeatmap()); } + private void updateCountdownSettingsVisibility() + { + bool countdownEnabled = enableCountdown.Current.Value; + + foreach (var child in countdownSettings) + { + child.ScaleTo(new Vector2(1, countdownEnabled ? 1 : 0), fade_duration, Easing.OutQuint) + .FadeTo(countdownEnabled ? 1 : 0, fade_duration, Easing.OutQuint); + } + } + private void updateBeatmap() { + Beatmap.BeatmapInfo.Countdown = enableCountdown.Current.Value ? countdownSpeed.Current.Value : CountdownType.None; + Beatmap.BeatmapInfo.CountdownOffset = int.TryParse(countdownOffset.Current.Value, NumberStyles.None, CultureInfo.InvariantCulture, out int offset) ? offset : 0; + Beatmap.BeatmapInfo.WidescreenStoryboard = widescreenSupport.Current.Value; Beatmap.BeatmapInfo.EpilepsyWarning = epilepsyWarning.Current.Value; Beatmap.BeatmapInfo.LetterboxInBreaks = letterboxDuringBreaks.Current.Value; From b43ee2d61c20a20c9ff464b2a2a15c9ff0e23579 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 29 Aug 2021 17:20:25 +0200 Subject: [PATCH 1593/2442] Add descriptions to enum members --- osu.Game/Beatmaps/CountdownType.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Beatmaps/CountdownType.cs b/osu.Game/Beatmaps/CountdownType.cs index 1831b4576b..73f85bf701 100644 --- a/osu.Game/Beatmaps/CountdownType.cs +++ b/osu.Game/Beatmaps/CountdownType.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.Beatmaps { /// @@ -9,8 +11,14 @@ namespace osu.Game.Beatmaps public enum CountdownType { None = 0, + + [Description("Normal")] Normal = 1, + + [Description("Half speed")] HalfSpeed = 2, + + [Description("Double speed")] DoubleSpeed = 3 } } From ddf9d2aa6ca18d1212e8bd97c8743aacac026d88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 29 Aug 2021 18:01:40 +0200 Subject: [PATCH 1594/2442] Add test coverage --- .../Visual/Editing/TestSceneDesignSection.cs | 97 +++++++++++++++++++ osu.Game/Screens/Edit/Setup/DesignSection.cs | 36 ++++--- 2 files changed, 119 insertions(+), 14 deletions(-) create mode 100644 osu.Game.Tests/Visual/Editing/TestSceneDesignSection.cs diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDesignSection.cs b/osu.Game.Tests/Visual/Editing/TestSceneDesignSection.cs new file mode 100644 index 0000000000..d84ffa1052 --- /dev/null +++ b/osu.Game.Tests/Visual/Editing/TestSceneDesignSection.cs @@ -0,0 +1,97 @@ +// 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.Globalization; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Screens.Edit; +using osu.Game.Screens.Edit.Setup; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Editing +{ + public class TestSceneDesignSection : OsuManualInputManagerTestScene + { + private TestDesignSection designSection; + private EditorBeatmap editorBeatmap { get; set; } + + [SetUpSteps] + public void SetUp() + { + AddStep("create blank beatmap", () => editorBeatmap = new EditorBeatmap(new Beatmap())); + AddStep("create section", () => Child = new DependencyProvidingContainer + { + RelativeSizeAxes = Axes.Both, + CachedDependencies = new (Type, object)[] + { + (typeof(EditorBeatmap), editorBeatmap) + }, + Child = designSection = new TestDesignSection() + }); + } + + [Test] + public void TestCountdownOff() + { + AddStep("turn countdown off", () => designSection.EnableCountdown.Current.Value = false); + + AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.None); + AddUntilStep("other controls hidden", () => !designSection.CountdownSpeed.IsPresent && !designSection.CountdownOffset.IsPresent); + } + + [Test] + public void TestCountdownOn() + { + AddStep("turn countdown on", () => designSection.EnableCountdown.Current.Value = true); + + AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.Normal); + AddUntilStep("other controls shown", () => designSection.CountdownSpeed.IsPresent && designSection.CountdownOffset.IsPresent); + + AddStep("change countdown speed", () => designSection.CountdownSpeed.Current.Value = CountdownType.DoubleSpeed); + + AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.DoubleSpeed); + AddUntilStep("other controls still shown", () => designSection.CountdownSpeed.IsPresent && designSection.CountdownOffset.IsPresent); + } + + [Test] + public void TestCountdownOffset() + { + AddStep("turn countdown on", () => designSection.EnableCountdown.Current.Value = true); + + AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.Normal); + + checkOffsetAfter("1", 1); + checkOffsetAfter(string.Empty, 0); + checkOffsetAfter("123", 123); + checkOffsetAfter("0", 0); + } + + private void checkOffsetAfter(string userInput, int expectedFinalValue) + { + AddStep("click text box", () => + { + var textBox = designSection.CountdownOffset.ChildrenOfType().Single(); + InputManager.MoveMouseTo(textBox); + InputManager.Click(MouseButton.Left); + }); + AddStep("set offset text", () => designSection.CountdownOffset.Current.Value = userInput); + AddStep("commit text", () => InputManager.Key(Key.Enter)); + + AddAssert($"displayed value is {expectedFinalValue}", () => designSection.CountdownOffset.Current.Value == expectedFinalValue.ToString(CultureInfo.InvariantCulture)); + AddAssert($"beatmap value is {expectedFinalValue}", () => editorBeatmap.BeatmapInfo.CountdownOffset == expectedFinalValue); + } + + private class TestDesignSection : DesignSection + { + public new LabelledSwitchButton EnableCountdown => base.EnableCountdown; + public new LabelledEnumDropdown CountdownSpeed => base.CountdownSpeed; + public new LabelledNumberBox CountdownOffset => base.CountdownOffset; + } + } +} diff --git a/osu.Game/Screens/Edit/Setup/DesignSection.cs b/osu.Game/Screens/Edit/Setup/DesignSection.cs index 633196ff2e..d030c2a894 100644 --- a/osu.Game/Screens/Edit/Setup/DesignSection.cs +++ b/osu.Game/Screens/Edit/Setup/DesignSection.cs @@ -18,15 +18,16 @@ namespace osu.Game.Screens.Edit.Setup { private const float fade_duration = 250; - private LabelledSwitchButton enableCountdown; - private FillFlowContainer countdownSettings; - private LabelledEnumDropdown countdownSpeed; - private LabelledNumberBox countdownOffset; + protected LabelledSwitchButton EnableCountdown; + protected LabelledEnumDropdown CountdownSpeed; + protected LabelledNumberBox CountdownOffset; private LabelledSwitchButton widescreenSupport; private LabelledSwitchButton epilepsyWarning; private LabelledSwitchButton letterboxDuringBreaks; + private FillFlowContainer countdownSettings; + public override LocalisableString Title => "Design"; [BackgroundDependencyLoader] @@ -34,7 +35,7 @@ namespace osu.Game.Screens.Edit.Setup { Children = new[] { - enableCountdown = new LabelledSwitchButton + EnableCountdown = new LabelledSwitchButton { Label = "Enable countdown", Current = { Value = Beatmap.BeatmapInfo.Countdown != CountdownType.None }, @@ -48,13 +49,13 @@ namespace osu.Game.Screens.Edit.Setup Direction = FillDirection.Vertical, Children = new Drawable[] { - countdownSpeed = new LabelledEnumDropdown + CountdownSpeed = new LabelledEnumDropdown { Label = "Countdown speed", Current = { Value = Beatmap.BeatmapInfo.Countdown != CountdownType.None ? Beatmap.BeatmapInfo.Countdown : CountdownType.Normal }, Items = Enum.GetValues(typeof(CountdownType)).Cast().Where(type => type != CountdownType.None) }, - countdownOffset = new LabelledNumberBox + CountdownOffset = new LabelledNumberBox { Label = "Countdown offset", Current = { Value = Beatmap.BeatmapInfo.CountdownOffset.ToString() }, @@ -88,12 +89,12 @@ namespace osu.Game.Screens.Edit.Setup { base.LoadComplete(); - enableCountdown.Current.BindValueChanged(_ => updateCountdownSettingsVisibility(), true); + EnableCountdown.Current.BindValueChanged(_ => updateCountdownSettingsVisibility(), true); countdownSettings.FinishTransforms(true); - enableCountdown.Current.BindValueChanged(_ => updateBeatmap()); - countdownSpeed.Current.BindValueChanged(_ => updateBeatmap()); - countdownOffset.OnCommit += (_, __) => updateBeatmap(); + EnableCountdown.Current.BindValueChanged(_ => updateBeatmap()); + CountdownSpeed.Current.BindValueChanged(_ => updateBeatmap()); + CountdownOffset.OnCommit += (_, __) => onOffsetCommitted(); widescreenSupport.Current.BindValueChanged(_ => updateBeatmap()); epilepsyWarning.Current.BindValueChanged(_ => updateBeatmap()); @@ -102,7 +103,7 @@ namespace osu.Game.Screens.Edit.Setup private void updateCountdownSettingsVisibility() { - bool countdownEnabled = enableCountdown.Current.Value; + bool countdownEnabled = EnableCountdown.Current.Value; foreach (var child in countdownSettings) { @@ -111,10 +112,17 @@ namespace osu.Game.Screens.Edit.Setup } } + private void onOffsetCommitted() + { + updateBeatmap(); + // update displayed text to ensure parsed value matches display (i.e. if empty string was provided). + CountdownOffset.Current.Value = Beatmap.BeatmapInfo.CountdownOffset.ToString(CultureInfo.InvariantCulture); + } + private void updateBeatmap() { - Beatmap.BeatmapInfo.Countdown = enableCountdown.Current.Value ? countdownSpeed.Current.Value : CountdownType.None; - Beatmap.BeatmapInfo.CountdownOffset = int.TryParse(countdownOffset.Current.Value, NumberStyles.None, CultureInfo.InvariantCulture, out int offset) ? offset : 0; + Beatmap.BeatmapInfo.Countdown = EnableCountdown.Current.Value ? CountdownSpeed.Current.Value : CountdownType.None; + Beatmap.BeatmapInfo.CountdownOffset = int.TryParse(CountdownOffset.Current.Value, NumberStyles.None, CultureInfo.InvariantCulture, out int offset) ? offset : 0; Beatmap.BeatmapInfo.WidescreenStoryboard = widescreenSupport.Current.Value; Beatmap.BeatmapInfo.EpilepsyWarning = epilepsyWarning.Current.Value; From 570d36fde79c9a0d8d0aa950a1e990fad40b96fa Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Mon, 30 Aug 2021 20:53:43 -0700 Subject: [PATCH 1595/2442] Make toolbar handle mouse events instead --- .../Visual/Navigation/TestSceneScreenNavigation.cs | 4 +++- osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs | 4 ++-- osu.Game/Overlays/Toolbar/Toolbar.cs | 6 ++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index a112534837..b536233ff0 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -345,7 +345,9 @@ namespace osu.Game.Tests.Visual.Navigation AddStep("press left mouse button", () => InputManager.PressButton(MouseButton.Left)); AddStep("move cursor to toolbar", () => InputManager.MoveMouseTo(Game.Toolbar.ScreenSpaceDrawQuad.Centre)); AddStep("release left mouse button", () => InputManager.ReleaseButton(MouseButton.Left)); - AddAssert("now playing is still visible", () => nowPlayingOverlay.State.Value == Visibility.Visible); + AddAssert("now playing is hidden", () => nowPlayingOverlay.State.Value == Visibility.Hidden); + + AddStep("press now playing hotkey", () => InputManager.Key(Key.F6)); // toolbar -> background AddStep("press left mouse button", () => InputManager.PressButton(MouseButton.Left)); diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index 0e635d26c2..b9b098df80 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -75,14 +75,14 @@ namespace osu.Game.Graphics.Containers protected override bool OnMouseDown(MouseDownEvent e) { - closeOnMouseUp = !base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition) && (game?.Toolbar.IsHovered == false); + closeOnMouseUp = !base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition); return base.OnMouseDown(e); } protected override void OnMouseUp(MouseUpEvent e) { - if (closeOnMouseUp && !base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition) && (game?.Toolbar.IsHovered == false)) + if (closeOnMouseUp && !base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition)) Hide(); base.OnMouseUp(e); diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 918e3b7105..2664301a0c 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -41,8 +41,7 @@ namespace osu.Game.Overlays.Toolbar // Toolbar and its components need keyboard input even when hidden. public override bool PropagateNonPositionalInputSubTree => true; - // IsHovered is used - public override bool HandlePositionalInput => true; + protected override bool Handle(UIEvent e) => e is MouseEvent; public Toolbar() { @@ -143,13 +142,12 @@ namespace osu.Game.Overlays.Toolbar protected override bool OnHover(HoverEvent e) { gradientBackground.FadeIn(transition_time, Easing.OutQuint); - return base.OnHover(e); + return true; } protected override void OnHoverLost(HoverLostEvent e) { gradientBackground.FadeOut(transition_time, Easing.OutQuint); - base.OnHoverLost(e); } } From 529a9a6ff87eb5a121911502765fb83e515e7462 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 31 Aug 2021 14:08:21 +0900 Subject: [PATCH 1596/2442] Adjust minimum triangle movement speed to avoid "static" triangles in logo Closes #14584. --- .../Visual/UserInterface/TestSceneOsuLogo.cs | 25 +++++++++++++++++++ osu.Game/Graphics/Backgrounds/Triangles.cs | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneOsuLogo.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuLogo.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuLogo.cs new file mode 100644 index 0000000000..8b91339479 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuLogo.cs @@ -0,0 +1,25 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Game.Screens.Menu; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneOsuLogo : OsuTestScene + { + [Test] + public void TestBasic() + { + AddStep("Add logo", () => + { + Child = new OsuLogo + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + }); + } + } +} diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 269360c492..35c48a50d0 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -153,7 +153,7 @@ namespace osu.Game.Graphics.Backgrounds TriangleParticle newParticle = parts[i]; // Scale moved distance by the size of the triangle. Smaller triangles should move more slowly. - newParticle.Position.Y += parts[i].Scale * movedDistance; + newParticle.Position.Y += Math.Max(0.5f, parts[i].Scale) * movedDistance; newParticle.Colour.A = adjustedAlpha; parts[i] = newParticle; From c25ab6835caf9aaf1708807b42afeeb6b140b270 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 31 Aug 2021 14:38:35 +0900 Subject: [PATCH 1597/2442] Remove IJsonSerializable interface Was pretty pointless and made it hard to use the custom serialisation terms arbitrarily in tests. --- osu.Game/Beatmaps/BeatmapInfo.cs | 3 +-- osu.Game/Beatmaps/IBeatmap.cs | 3 +-- ...JsonSerializable.cs => JsonSerializableExtensions.cs} | 9 +-------- osu.Game/Rulesets/Mods/Mod.cs | 3 +-- osu.Game/Rulesets/Timing/MultiplierControlPoint.cs | 3 +-- osu.Game/Screens/Edit/ClipboardContent.cs | 3 +-- osu.Game/Screens/Play/HUD/SkinnableInfo.cs | 3 +-- 7 files changed, 7 insertions(+), 20 deletions(-) rename osu.Game/IO/Serialization/{IJsonSerializable.cs => JsonSerializableExtensions.cs} (76%) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 353636c8af..3eb766a667 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -10,7 +10,6 @@ using Newtonsoft.Json; using osu.Framework.Localisation; using osu.Framework.Testing; using osu.Game.Database; -using osu.Game.IO.Serialization; using osu.Game.Rulesets; using osu.Game.Scoring; @@ -18,7 +17,7 @@ namespace osu.Game.Beatmaps { [ExcludeFromDynamicCompile] [Serializable] - public class BeatmapInfo : IEquatable, IJsonSerializable, IHasPrimaryKey + public class BeatmapInfo : IEquatable, IHasPrimaryKey { public int ID { get; set; } diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs index 769b33009a..f61dd269e1 100644 --- a/osu.Game/Beatmaps/IBeatmap.cs +++ b/osu.Game/Beatmaps/IBeatmap.cs @@ -4,12 +4,11 @@ using System.Collections.Generic; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Timing; -using osu.Game.IO.Serialization; using osu.Game.Rulesets.Objects; namespace osu.Game.Beatmaps { - public interface IBeatmap : IJsonSerializable + public interface IBeatmap { /// /// This beatmap's info. diff --git a/osu.Game/IO/Serialization/IJsonSerializable.cs b/osu.Game/IO/Serialization/JsonSerializableExtensions.cs similarity index 76% rename from osu.Game/IO/Serialization/IJsonSerializable.cs rename to osu.Game/IO/Serialization/JsonSerializableExtensions.cs index c8d5ce39a6..5b47d0bad1 100644 --- a/osu.Game/IO/Serialization/IJsonSerializable.cs +++ b/osu.Game/IO/Serialization/JsonSerializableExtensions.cs @@ -7,21 +7,14 @@ using osu.Framework.IO.Serialization; namespace osu.Game.IO.Serialization { - public interface IJsonSerializable - { - } - public static class JsonSerializableExtensions { - public static string Serialize(this IJsonSerializable obj) => JsonConvert.SerializeObject(obj, CreateGlobalSettings()); + public static string Serialize(this object obj) => JsonConvert.SerializeObject(obj, CreateGlobalSettings()); public static T Deserialize(this string objString) => JsonConvert.DeserializeObject(objString, CreateGlobalSettings()); public static void DeserializeInto(this string objString, T target) => JsonConvert.PopulateObject(objString, target, CreateGlobalSettings()); - /// - /// Creates the default that should be used for all s. - /// public static JsonSerializerSettings CreateGlobalSettings() => new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 9f3b5eaf5b..1199d8a956 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -11,7 +11,6 @@ using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics.Sprites; using osu.Framework.Testing; using osu.Game.Configuration; -using osu.Game.IO.Serialization; using osu.Game.Rulesets.UI; using osu.Game.Utils; @@ -21,7 +20,7 @@ namespace osu.Game.Rulesets.Mods /// The base class for gameplay modifiers. /// [ExcludeFromDynamicCompile] - public abstract class Mod : IMod, IEquatable, IJsonSerializable, IDeepCloneable + public abstract class Mod : IMod, IEquatable, IDeepCloneable { /// /// The name of this mod. diff --git a/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs b/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs index 4b3c3f90f0..dcd2cc8b55 100644 --- a/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs +++ b/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs @@ -3,14 +3,13 @@ using System; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.IO.Serialization; namespace osu.Game.Rulesets.Timing { /// /// A control point which adds an aggregated multiplier based on the provided 's BeatLength and 's SpeedMultiplier. /// - public class MultiplierControlPoint : IJsonSerializable, IComparable + public class MultiplierControlPoint : IComparable { /// /// The time in milliseconds at which this starts. diff --git a/osu.Game/Screens/Edit/ClipboardContent.cs b/osu.Game/Screens/Edit/ClipboardContent.cs index b2edbedccc..0348a7c15d 100644 --- a/osu.Game/Screens/Edit/ClipboardContent.cs +++ b/osu.Game/Screens/Edit/ClipboardContent.cs @@ -4,13 +4,12 @@ using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; -using osu.Game.IO.Serialization; using osu.Game.IO.Serialization.Converters; using osu.Game.Rulesets.Objects; namespace osu.Game.Screens.Edit { - public class ClipboardContent : IJsonSerializable + public class ClipboardContent { [JsonConverter(typeof(TypedListConverter))] public IList HitObjects; diff --git a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs index b64e5ca98f..a2b84c79af 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs @@ -8,7 +8,6 @@ using Newtonsoft.Json; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Extensions; -using osu.Game.IO.Serialization; using osu.Game.Skinning; using osuTK; @@ -18,7 +17,7 @@ namespace osu.Game.Screens.Play.HUD /// Serialised information governing custom changes to an . /// [Serializable] - public class SkinnableInfo : IJsonSerializable + public class SkinnableInfo { public Type Type { get; set; } From eb21ed08f89a06b76de8b45a45886d80365bf9c2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 31 Aug 2021 14:51:14 +0900 Subject: [PATCH 1598/2442] Update test to only compare `HitObject`s --- .../Formats/LegacyBeatmapEncoderTest.cs | 72 +++++++++++-------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index 85b8a3d190..896aa53f82 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -66,6 +66,47 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.IsTrue(areComboColoursEqual(decodedAfterEncode.beatmapSkin.Configuration, decoded.beatmapSkin.Configuration)); } + [TestCaseSource(nameof(allBeatmaps))] + public void TestEncodeDecodeStabilityWithNonLegacyControlPoints(string name) + { + var decoded = decodeFromLegacy(beatmaps_resource_store.GetStream(name), name); + + // we are testing that the transfer of relevant data to hitobjects (from legacy control points) sticks through encode/decode. + // before the encode step, the legacy information is removed here. + decoded.beatmap.ControlPointInfo = removeLegacyControlPointTypes(decoded.beatmap.ControlPointInfo); + + var decodedAfterEncode = decodeFromLegacy(encodeToLegacy(decoded), name); + + // in this process, we may lose some detail in the control points section. + // let's focus on only the hitobjects. + var originalHitObjects = decoded.beatmap.HitObjects.Serialize(); + var newHitObjects = decodedAfterEncode.beatmap.HitObjects.Serialize(); + + Assert.That(newHitObjects, Is.EqualTo(originalHitObjects)); + + ControlPointInfo removeLegacyControlPointTypes(ControlPointInfo controlPointInfo) + { + // emulate non-legacy control points by cloning the non-legacy portion. + // the assertion is that the encoder can recreate this losslessly from hitobject data. + Assert.IsInstanceOf(controlPointInfo); + + var newControlPoints = new ControlPointInfo(); + + foreach (var point in controlPointInfo.AllControlPoints) + { + // completely ignore "legacy" types, which have been moved to HitObjects. + // even though these would mostly be ignored by the Add call, they will still be available in groups, + // which isn't what we want to be testing here. + if (point is SampleControlPoint) + continue; + + newControlPoints.Add(point.Time, point.DeepClone()); + } + + return newControlPoints; + } + } + [Test] public void TestEncodeMultiSegmentSliderWithFloatingPointError() { @@ -93,37 +134,6 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.That(decodedSlider.Path.ControlPoints.Count, Is.EqualTo(5)); } - [TestCaseSource(nameof(allBeatmaps))] - public void TestEncodeDecodeStabilityWithNonLegacyControlPoints(string name) - { - var decoded = decodeFromLegacy(beatmaps_resource_store.GetStream(name), name); - - sort(decoded.beatmap); - - var originalSerialized = decoded.beatmap.Serialize(); - var encoded = encodeToLegacy(decoded); - - Assert.AreEqual(typeof(LegacyControlPointInfo), decoded.beatmap.ControlPointInfo.GetType()); - - // emulate non-legacy control points by cloning the non-legacy portion. - // the assertion is that the encoder can recreate this losslessly from hitobject data. - var controlPointInfo = new ControlPointInfo(); - - foreach (var point in decoded.beatmap.ControlPointInfo.AllControlPoints) - controlPointInfo.Add(point.Time, point.DeepClone()); - - decoded.beatmap.ControlPointInfo = controlPointInfo; - - Assert.AreNotEqual(typeof(LegacyControlPointInfo), decoded.beatmap.ControlPointInfo.GetType()); - - var decodedAfterEncode = decodeFromLegacy(encoded, name); - - sort(decodedAfterEncode.beatmap); - - Assert.That(decodedAfterEncode.beatmap.Serialize(), Is.EqualTo(originalSerialized)); - Assert.IsTrue(areComboColoursEqual(decodedAfterEncode.beatmapSkin.Configuration, decoded.beatmapSkin.Configuration)); - } - private bool areComboColoursEqual(IHasComboColours a, IHasComboColours b) { // equal to null, no need to SequenceEqual From 9fa8bee094cb24fe5656193d0d60a5b5172c6844 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 31 Aug 2021 14:51:19 +0900 Subject: [PATCH 1599/2442] Remove outdated TODO --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 461611da3b..75d9a56f3e 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -172,7 +172,7 @@ namespace osu.Game.Beatmaps.Formats writer.WriteLine("[TimingPoints]"); - if (!(beatmap.ControlPointInfo is LegacyControlPointInfo)) // todo: always run this? probably no harm. + if (!(beatmap.ControlPointInfo is LegacyControlPointInfo)) { var legacyControlPoints = new LegacyControlPointInfo(); From 448c58c35d0aec96e9a3c91323f69564ddcb8c89 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 31 Aug 2021 15:08:07 +0900 Subject: [PATCH 1600/2442] Remove unnecessary variable discard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Beatmaps/Legacy/LegacyControlPointInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Legacy/LegacyControlPointInfo.cs b/osu.Game/Beatmaps/Legacy/LegacyControlPointInfo.cs index 5152549bed..ff0ca5ebe1 100644 --- a/osu.Game/Beatmaps/Legacy/LegacyControlPointInfo.cs +++ b/osu.Game/Beatmaps/Legacy/LegacyControlPointInfo.cs @@ -34,7 +34,7 @@ namespace osu.Game.Beatmaps.Legacy protected override bool CheckAlreadyExisting(double time, ControlPoint newPoint) { - if (newPoint is SampleControlPoint _) + if (newPoint is SampleControlPoint) { var existing = BinarySearch(SamplePoints, time); return newPoint.IsRedundant(existing); From d988aa168001290604dcb0f1951886b5012c55ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 31 Aug 2021 15:05:10 +0900 Subject: [PATCH 1601/2442] Actually serialise `SampleControlPoint`s along with `HitObject`s --- osu.Game/Beatmaps/ControlPoints/ControlPoint.cs | 2 ++ osu.Game/Rulesets/Objects/HitObject.cs | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs index 643c5d9adb..8203f2e968 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using Newtonsoft.Json; using osu.Game.Graphics; using osu.Game.Utils; using osuTK.Graphics; @@ -13,6 +14,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// /// The time at which the control point takes effect. /// + [JsonIgnore] public double Time => controlPointGroup?.Time ?? 0; private ControlPointGroup controlPointGroup; diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index fb1b6cd267..c4b9e6e1ad 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -66,7 +66,6 @@ namespace osu.Game.Rulesets.Objects } } - [JsonIgnore] public SampleControlPoint SampleControlPoint; /// From a2546243737d3b4d9f4f10f8af215a4e42645fb1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 31 Aug 2021 17:18:04 +0900 Subject: [PATCH 1602/2442] Avoid performing beatmap metadata lookups when entering the editor If none of the lookup parameters are available, skip the lookup completely. --- osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs index 7824205257..6ae7f7481e 100644 --- a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs +++ b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs @@ -153,6 +153,11 @@ namespace osu.Game.Beatmaps if (!storage.Exists(cache_database_name)) return false; + if (string.IsNullOrEmpty(beatmap.MD5Hash) + && string.IsNullOrEmpty(beatmap.Path) + && beatmap.OnlineBeatmapID == null) + return false; + try { using (var db = new SqliteConnection(storage.GetDatabaseConnectionString("online"))) From 90768a86a65db383aa2de2d58756c2a59bc80139 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 31 Aug 2021 17:55:13 +0900 Subject: [PATCH 1603/2442] Adjust classic scoring as a constant multiple over standardised scoring --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 16f2607bad..3073ec9340 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -226,8 +226,9 @@ namespace osu.Game.Rulesets.Scoring return (max_score * (accuracyScore + comboScore) + getBonusScore(statistics)) * scoreMultiplier; case ScoringMode.Classic: - // should emulate osu-stable's scoring as closely as we can (https://osu.ppy.sh/help/wiki/Score/ScoreV1) - return getBonusScore(statistics) + (accuracyRatio * Math.Max(1, maxCombo) * 300) * (1 + Math.Max(0, (comboRatio * maxCombo) - 1) * scoreMultiplier / 25); + // This feels very similar to osu!stable scoring (ScoreV1) while maintaining that classic scoring is only a constant multiple of standardised scoring. + // The invariant is important to ensure that scores don't get re-ordered on leaderboards between the two systems. + return GetScore(ScoringMode.Standardised, maxCombo, accuracyRatio, comboRatio, statistics) / max_score * Math.Pow(maxCombo, 2) * 25; } } From bfcadcc4ac591b8128e26ff80083c478075e367f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 31 Aug 2021 18:56:26 +0900 Subject: [PATCH 1604/2442] Revert some changes --- osu.Game/Online/Leaderboards/Leaderboard.cs | 5 -- .../Online/Leaderboards/LeaderboardScore.cs | 2 +- .../Overlays/BeatmapSet/Scores/ScoreTable.cs | 2 +- .../BeatmapSet/Scores/ScoresContainer.cs | 72 ++++++++----------- osu.Game/Screens/Ranking/ScorePanelList.cs | 17 +---- 5 files changed, 32 insertions(+), 66 deletions(-) diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index f3a06994ee..1d9d2e6b14 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -19,7 +19,6 @@ using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.Placeholders; -using osu.Game.Rulesets.Scoring; using osuTK; using osuTK.Graphics; @@ -29,7 +28,6 @@ namespace osu.Game.Online.Leaderboards { private const double fade_duration = 300; - private readonly Bindable scoringMode = new Bindable(); private readonly OsuScrollContainer scrollContainer; private readonly Container placeholderContainer; private readonly UserTopScoreContainer topScoreContainer; @@ -255,9 +253,6 @@ namespace osu.Game.Online.Leaderboards apiState.BindTo(api.State); apiState.BindValueChanged(onlineStateChanged, true); - - configManager.BindWith(OsuSetting.ScoreDisplayMode, scoringMode); - scoringMode.BindValueChanged(_ => RefreshScores(), true); } private APIRequest getScoresRequest; diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index 206b0e7936..9a0bc35bb3 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -199,7 +199,7 @@ namespace osu.Game.Online.Leaderboards TextColour = Color4.White, GlowColour = Color4Extensions.FromHex(@"83ccfa"), Font = OsuFont.Numeric.With(size: 23), - Text = scoreManager.GetTotalScore(score).ToLocalisableString(@"N0"), + Current = scoreManager.GetBindableTotalScoreString(score) }, RankContainer = new Container { diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index 847df53edc..9497ed9d35 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -152,7 +152,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { Margin = new MarginPadding { Right = horizontal_inset }, Font = OsuFont.GetFont(size: text_size, weight: index == 0 ? FontWeight.Bold : FontWeight.Medium), - Text = scoreManager.GetTotalScore(score).ToLocalisableString(@"N0"), + Current = scoreManager.GetBindableTotalScoreString(score), }, new OsuSpriteText { diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 91c3dfad65..1b7e152c07 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -15,7 +15,6 @@ using osu.Framework.Bindables; using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets; -using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Select.Leaderboards; using osu.Game.Users; @@ -30,7 +29,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private readonly Bindable ruleset = new Bindable(); private readonly Bindable scope = new Bindable(BeatmapLeaderboardScope.Global); private readonly IBindable user = new Bindable(); - private readonly Bindable scoringMode = new Bindable(); private readonly Box background; private readonly ScoreTable scoreTable; @@ -51,15 +49,37 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private GetScoresRequest getScoresRequest; - private APILegacyScores scores; - protected APILegacyScores Scores { - set + set => Schedule(() => { - scores = value; - displayScores(value); - } + topScoresContainer.Clear(); + + if (value?.Scores.Any() != true) + { + scoreTable.ClearScores(); + scoreTable.Hide(); + return; + } + + var scoreInfos = value.Scores.Select(s => s.CreateScoreInfo(rulesets)) + .OrderByDescending(s => scoreManager.GetTotalScore(s)) + .ThenBy(s => s.OnlineScoreID) + .ToList(); + + var topScore = scoreInfos.First(); + + scoreTable.DisplayScores(scoreInfos, topScore.Beatmap?.Status.GrantsPerformancePoints() == true); + scoreTable.Show(); + + var userScore = value.UserScore; + var userScoreInfo = userScore?.Score.CreateScoreInfo(rulesets); + + topScoresContainer.Add(new DrawableTopScore(topScore)); + + if (userScoreInfo != null && userScoreInfo.OnlineScoreID != topScore.OnlineScoreID) + topScoresContainer.Add(new DrawableTopScore(userScoreInfo, userScore.Position)); + }); } public ScoresContainer() @@ -160,7 +180,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores background.Colour = colourProvider.Background5; user.BindTo(api.LocalUser); - config.BindWith(OsuSetting.ScoreDisplayMode, scoringMode); } protected override void LoadComplete() @@ -173,8 +192,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Beatmap.BindValueChanged(onBeatmapChanged); user.BindValueChanged(onUserChanged, true); - - scoringMode.BindValueChanged(_ => displayScores(scores)); } private void onBeatmapChanged(ValueChangedEvent beatmap) @@ -246,39 +263,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores api.Queue(getScoresRequest); } - private void displayScores(APILegacyScores newScores) - { - Schedule(() => - { - topScoresContainer.Clear(); - - if (newScores?.Scores.Any() != true) - { - scoreTable.ClearScores(); - scoreTable.Hide(); - return; - } - - var scoreInfos = newScores.Scores.Select(s => s.CreateScoreInfo(rulesets)) - .OrderByDescending(s => scoreManager.GetTotalScore(s)) - .ThenBy(s => s.OnlineScoreID) - .ToList(); - - var topScore = scoreInfos.First(); - - scoreTable.DisplayScores(scoreInfos, topScore.Beatmap?.Status.GrantsPerformancePoints() == true); - scoreTable.Show(); - - var userScore = newScores.UserScore; - var userScoreInfo = userScore?.Score.CreateScoreInfo(rulesets); - - topScoresContainer.Add(new DrawableTopScore(topScore)); - - if (userScoreInfo != null && userScoreInfo.OnlineScoreID != topScore.OnlineScoreID) - topScoresContainer.Add(new DrawableTopScore(userScoreInfo, userScore.Position)); - }); - } - private bool userIsSupporter => api.IsLoggedIn && api.LocalUser.Value.IsSupporter; } } diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index c9ddfd27d3..ba90585ab5 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -10,9 +10,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; -using osu.Game.Configuration; using osu.Game.Graphics.Containers; -using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osuTK; using osuTK.Input; @@ -292,26 +290,15 @@ namespace osu.Game.Screens.Ranking { public override IEnumerable FlowingChildren => applySorting(AliveInternalChildren); - private readonly Bindable scoringMode = new Bindable(); - [Resolved] private ScoreManager scoreManager { get; set; } - [BackgroundDependencyLoader] - private void load(OsuConfigManager configManager) - { - configManager.BindWith(OsuSetting.ScoreDisplayMode, scoringMode); - } - protected override void LoadComplete() { base.LoadComplete(); - scoringMode.BindValueChanged(mode => - { - foreach (var c in Children) - SetLayoutPosition(c, scoreManager.GetTotalScore(c.Panel.Score)); - }, true); + foreach (var c in Children) + SetLayoutPosition(c, scoreManager.GetTotalScore(c.Panel.Score)); } public int GetPanelIndex(ScoreInfo score) => applySorting(Children).TakeWhile(s => s.Panel.Score != score).Count(); From 3f93aa15079c6f593d2ad436e09e8536bf074a84 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 31 Aug 2021 20:12:40 +0900 Subject: [PATCH 1605/2442] Fix traceable sliders incorrectly being opaque Closes https://github.com/ppy/osu/issues/14449. Regressed in https://github.com/ppy/osu/pull/14205. --- .../Skinning/Legacy/LegacySliderBody.cs | 2 -- .../Legacy/OsuLegacySkinTransformer.cs | 21 +++++++++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs index 1c8dfeac52..7d69e5ecdc 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs @@ -22,8 +22,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy // Roughly matches osu!stable's slider border portions. => base.CalculatedBorderPortion * 0.77f; - public new Color4 AccentColour => new Color4(base.AccentColour.R, base.AccentColour.G, base.AccentColour.B, 0.7f); - protected override Color4 ColourAt(float position) { float realBorderPortion = shadow_portion + CalculatedBorderPortion; diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index 41b0a88f11..16c770706d 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -3,9 +3,11 @@ using System; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Game.Skinning; using osuTK; +using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Skinning.Legacy { @@ -118,8 +120,23 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { switch (lookup) { - case OsuSkinColour colour: - return base.GetConfig(new SkinCustomColourLookup(colour)); + case OsuSkinColour colourLookup: + var colour = base.GetConfig(new SkinCustomColourLookup(colourLookup)); + + if (colour == null) + return null; + + switch (colourLookup) + { + case OsuSkinColour.SliderTrackOverride: + var bindableColour = ((Bindable)colour); + + // legacy skins use a constant value for slider track alpha, regardless of the source colour. + bindableColour.Value = bindableColour.Value.Opacity(0.7f); + break; + } + + return colour; case OsuSkinConfiguration osuLookup: switch (osuLookup) From cfcf3d7507b8b80b89d082e3027d354bcffa5220 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 31 Aug 2021 20:43:50 +0900 Subject: [PATCH 1606/2442] Use synchronous total score retrieval for bindable --- osu.Game/Scoring/ScoreManager.cs | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index f310462d6e..8cf1c85956 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -195,9 +195,6 @@ namespace osu.Game.Scoring { public readonly Bindable ScoringMode = new Bindable(); - private readonly ScoreInfo score; - private readonly ScoreManager scoreManager; - /// /// Creates a new . /// @@ -205,20 +202,7 @@ namespace osu.Game.Scoring /// The . public TotalScoreBindable(ScoreInfo score, ScoreManager scoreManager) { - this.score = score; - this.scoreManager = scoreManager; - - ScoringMode.BindValueChanged(onScoringModeChanged, true); - } - - private CancellationTokenSource difficultyCancellationSource; - - private void onScoringModeChanged(ValueChangedEvent mode) - { - difficultyCancellationSource?.Cancel(); - difficultyCancellationSource = new CancellationTokenSource(); - - scoreManager.GetTotalScoreAsync(score, difficultyCancellationSource.Token).ContinueWith(s => Value = s.Result, TaskContinuationOptions.OnlyOnRanToCompletion); + ScoringMode.BindValueChanged(_ => Value = scoreManager.GetTotalScore(score), true); } } From fee94236de92815f9d9b29375b56c5b7604ec132 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 31 Aug 2021 21:36:31 +0900 Subject: [PATCH 1607/2442] Fix update-thread pauses --- .../BeatmapSet/Scores/ScoresContainer.cs | 39 +++++++----- osu.Game/Scoring/ScoreManager.cs | 19 ++++++ osu.Game/Screens/Ranking/ScorePanelList.cs | 62 +++++++++++-------- .../Select/Leaderboards/BeatmapLeaderboard.cs | 30 ++++++--- 4 files changed, 99 insertions(+), 51 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 1b7e152c07..8e9a9eb684 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -7,6 +7,8 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osuTK; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using osu.Game.Online.API.Requests.Responses; using osu.Game.Beatmaps; using osu.Game.Online.API; @@ -49,36 +51,41 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private GetScoresRequest getScoresRequest; + private CancellationTokenSource loadCancellationSource; + protected APILegacyScores Scores { set => Schedule(() => { + loadCancellationSource?.Cancel(); + loadCancellationSource = new CancellationTokenSource(); + topScoresContainer.Clear(); + scoreTable.ClearScores(); + scoreTable.Hide(); if (value?.Scores.Any() != true) - { - scoreTable.ClearScores(); - scoreTable.Hide(); return; - } - var scoreInfos = value.Scores.Select(s => s.CreateScoreInfo(rulesets)) - .OrderByDescending(s => scoreManager.GetTotalScore(s)) - .ThenBy(s => s.OnlineScoreID) - .ToList(); + scoreManager.GetOrderedScoresAsync(value.Scores.Select(s => s.CreateScoreInfo(rulesets)).ToArray(), loadCancellationSource.Token) + .ContinueWith(ordered => Schedule(() => + { + if (loadCancellationSource.IsCancellationRequested) + return; - var topScore = scoreInfos.First(); + var topScore = ordered.Result.First(); - scoreTable.DisplayScores(scoreInfos, topScore.Beatmap?.Status.GrantsPerformancePoints() == true); - scoreTable.Show(); + scoreTable.DisplayScores(ordered.Result, topScore.Beatmap?.Status.GrantsPerformancePoints() == true); + scoreTable.Show(); - var userScore = value.UserScore; - var userScoreInfo = userScore?.Score.CreateScoreInfo(rulesets); + var userScore = value.UserScore; + var userScoreInfo = userScore?.Score.CreateScoreInfo(rulesets); - topScoresContainer.Add(new DrawableTopScore(topScore)); + topScoresContainer.Add(new DrawableTopScore(topScore)); - if (userScoreInfo != null && userScoreInfo.OnlineScoreID != topScore.OnlineScoreID) - topScoresContainer.Add(new DrawableTopScore(userScoreInfo, userScore.Position)); + if (userScoreInfo != null && userScoreInfo.OnlineScoreID != topScore.OnlineScoreID) + topScoresContainer.Add(new DrawableTopScore(userScoreInfo, userScore.Position)); + }), TaskContinuationOptions.OnlyOnRanToCompletion); }); } diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 8cf1c85956..71edc65fcc 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -106,6 +106,25 @@ namespace osu.Game.Scoring => base.CheckLocalAvailability(model, items) || (model.OnlineScoreID != null && items.Any(i => i.OnlineScoreID == model.OnlineScoreID)); + public async Task GetOrderedScoresAsync(ScoreInfo[] scores, CancellationToken cancellationToken = default) + { + var difficultyCache = difficulties?.Invoke(); + + if (difficultyCache == null) + return orderByTotalScore(scores); + + // Compute difficulties asynchronously first to prevent blocks on the main thread. + foreach (var s in scores) + { + await difficultyCache.GetDifficultyAsync(s.Beatmap, s.Ruleset, s.Mods, cancellationToken).ConfigureAwait(false); + cancellationToken.ThrowIfCancellationRequested(); + } + + return orderByTotalScore(scores); + + ScoreInfo[] orderByTotalScore(IEnumerable incoming) => incoming.OrderByDescending(GetTotalScore).ThenBy(s => s.OnlineScoreID).ToArray(); + } + /// /// Retrieves a bindable that represents the total score of a . /// diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index ba90585ab5..e319e824a1 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Threading; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -61,6 +62,10 @@ namespace osu.Game.Screens.Ranking public readonly Bindable SelectedScore = new Bindable(); + [Resolved] + private ScoreManager scoreManager { get; set; } + + private readonly CancellationTokenSource loadCancellationSource = new CancellationTokenSource(); private readonly Flow flow; private readonly Scroll scroll; private ScorePanel expandedPanel; @@ -115,32 +120,33 @@ namespace osu.Game.Screens.Ranking }; }); - flow.Add(panel.CreateTrackingContainer().With(d => - { - d.Anchor = Anchor.Centre; - d.Origin = Anchor.Centre; - })); + scoreManager.GetOrderedScoresAsync(new[] { score }) + .ContinueWith(_ => Schedule(() => + { + flow.Add(panel.CreateTrackingContainer().With(d => + { + d.Anchor = Anchor.Centre; + d.Origin = Anchor.Centre; + })); - if (IsLoaded) - { - if (SelectedScore.Value == score) - { - SelectedScore.TriggerChange(); - } - else - { - // We want the scroll position to remain relative to the expanded panel. When a new panel is added after the expanded panel, nothing needs to be done. - // But when a panel is added before the expanded panel, we need to offset the scroll position by the width of the new panel. - if (expandedPanel != null && flow.GetPanelIndex(score) < flow.GetPanelIndex(expandedPanel.Score)) - { - // A somewhat hacky property is used here because we need to: - // 1) Scroll after the scroll container's visible range is updated. - // 2) Scroll before the scroll container's scroll position is updated. - // Without this, we would have a 1-frame positioning error which looks very jarring. - scroll.InstantScrollTarget = (scroll.InstantScrollTarget ?? scroll.Target) + ScorePanel.CONTRACTED_WIDTH + panel_spacing; - } - } - } + if (SelectedScore.Value == score) + { + SelectedScore.TriggerChange(); + } + else + { + // We want the scroll position to remain relative to the expanded panel. When a new panel is added after the expanded panel, nothing needs to be done. + // But when a panel is added before the expanded panel, we need to offset the scroll position by the width of the new panel. + if (expandedPanel != null && flow.GetPanelIndex(score) < flow.GetPanelIndex(expandedPanel.Score)) + { + // A somewhat hacky property is used here because we need to: + // 1) Scroll after the scroll container's visible range is updated. + // 2) Scroll before the scroll container's scroll position is updated. + // Without this, we would have a 1-frame positioning error which looks very jarring. + scroll.InstantScrollTarget = (scroll.InstantScrollTarget ?? scroll.Target) + ScorePanel.CONTRACTED_WIDTH + panel_spacing; + } + } + })); return panel; } @@ -286,6 +292,12 @@ namespace osu.Game.Screens.Ranking return base.OnKeyDown(e); } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + loadCancellationSource?.Cancel(); + } + private class Flow : FillFlowContainer { public override IEnumerable FlowingChildren => applySorting(AliveInternalChildren); diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 0276ae8b2a..54ec42127d 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -4,6 +4,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Beatmaps; @@ -66,6 +68,9 @@ namespace osu.Game.Screens.Select.Leaderboards [Resolved] private ScoreManager scoreManager { get; set; } + [Resolved] + private BeatmapDifficultyCache difficultyCache { get; set; } + [Resolved] private IBindable ruleset { get; set; } @@ -120,8 +125,13 @@ namespace osu.Game.Screens.Select.Leaderboards protected override bool IsOnlineScope => Scope != BeatmapLeaderboardScope.Local; + private CancellationTokenSource loadCancellationSource; + protected override APIRequest FetchScores(Action> scoresCallback) { + loadCancellationSource?.Cancel(); + loadCancellationSource = new CancellationTokenSource(); + if (Beatmap == null) { PlaceholderState = PlaceholderState.NoneSelected; @@ -146,11 +156,8 @@ namespace osu.Game.Screens.Select.Leaderboards scores = scores.Where(s => s.Mods.Any(m => selectedMods.Contains(m.Acronym))); } - Scores = scores.OrderByDescending(s => scoreManager.GetTotalScore(s)) - .ThenBy(s => s.OnlineScoreID) - .ToArray(); - - PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores; + scoreManager.GetOrderedScoresAsync(scores.ToArray(), loadCancellationSource.Token) + .ContinueWith(ordered => scoresCallback?.Invoke(ordered.Result), TaskContinuationOptions.OnlyOnRanToCompletion); return null; } @@ -185,12 +192,15 @@ namespace osu.Game.Screens.Select.Leaderboards req.Success += r => { - scoresCallback?.Invoke( - r.Scores.Select(s => s.CreateScoreInfo(rulesets)) - .OrderByDescending(s => scoreManager.GetTotalScore(s)) - .ThenBy(s => s.OnlineScoreID)); + scoreManager.GetOrderedScoresAsync(r.Scores.Select(s => s.CreateScoreInfo(rulesets)).ToArray(), loadCancellationSource.Token) + .ContinueWith(ordered => Schedule(() => + { + if (loadCancellationSource.IsCancellationRequested) + return; - TopScore = r.UserScore?.CreateScoreInfo(rulesets); + scoresCallback?.Invoke(ordered.Result); + TopScore = r.UserScore?.CreateScoreInfo(rulesets); + }), TaskContinuationOptions.OnlyOnRanToCompletion); }; return req; From 999386da2919a022132970344a2d089f192c9d2c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 31 Aug 2021 21:47:49 +0900 Subject: [PATCH 1608/2442] Cleanup --- osu.Game/Online/Leaderboards/Leaderboard.cs | 3 +-- osu.Game/Online/Leaderboards/LeaderboardScore.cs | 2 +- osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs | 2 +- osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index 1d9d2e6b14..4f8b27602b 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -13,7 +13,6 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Threading; -using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; @@ -247,7 +246,7 @@ namespace osu.Game.Online.Leaderboards private readonly IBindable apiState = new Bindable(); [BackgroundDependencyLoader] - private void load(OsuConfigManager configManager) + private void load() { if (api != null) apiState.BindTo(api.State); diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index 9a0bc35bb3..934b905a1a 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -198,8 +198,8 @@ namespace osu.Game.Online.Leaderboards { TextColour = Color4.White, GlowColour = Color4Extensions.FromHex(@"83ccfa"), + Current = scoreManager.GetBindableTotalScoreString(score), Font = OsuFont.Numeric.With(size: 23), - Current = scoreManager.GetBindableTotalScoreString(score) }, RankContainer = new Container { diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index 9497ed9d35..a154016824 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -151,8 +151,8 @@ namespace osu.Game.Overlays.BeatmapSet.Scores new OsuSpriteText { Margin = new MarginPadding { Right = horizontal_inset }, - Font = OsuFont.GetFont(size: text_size, weight: index == 0 ? FontWeight.Bold : FontWeight.Medium), Current = scoreManager.GetBindableTotalScoreString(score), + Font = OsuFont.GetFont(size: text_size, weight: index == 0 ? FontWeight.Bold : FontWeight.Medium) }, new OsuSpriteText { diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 8e9a9eb684..a5a373467e 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -182,7 +182,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores } [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider, OsuConfigManager config) + private void load(OverlayColourProvider colourProvider) { background.Colour = colourProvider.Background5; From 79f71e5181eb5410090769117175a03f094a265e Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Tue, 31 Aug 2021 13:56:44 +0100 Subject: [PATCH 1609/2442] get user id when importing scores --- osu.Game/Scoring/ScoreManager.cs | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 83bcac01ac..4cfcc00bb8 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -23,6 +23,7 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring.Legacy; +using osu.Game.Users; namespace osu.Game.Scoring { @@ -43,6 +44,8 @@ namespace osu.Game.Scoring [CanBeNull] private readonly OsuConfigManager configManager; + private IAPIProvider api { get; set; } + public ScoreManager(RulesetStore rulesets, Func beatmaps, Storage storage, IAPIProvider api, IDatabaseContextFactory contextFactory, IIpcHost importHost = null, Func difficulties = null, OsuConfigManager configManager = null) : base(storage, contextFactory, api, new ScoreStore(contextFactory, storage), importHost) @@ -51,6 +54,7 @@ namespace osu.Game.Scoring this.beatmaps = beatmaps; this.difficulties = difficulties; this.configManager = configManager; + this.api = api; } protected override ScoreInfo CreateModel(ArchiveReader archive) @@ -72,8 +76,31 @@ namespace osu.Game.Scoring } } + private Dictionary previouslyLookedUpUsernames = new Dictionary(); + protected override Task Populate(ScoreInfo model, ArchiveReader archive, CancellationToken cancellationToken = default) - => Task.CompletedTask; + { + // These scores only provide the user's username but we need the user's ID too. + if (model.UserID <= 1 && model.UserString != null) + { + if (previouslyLookedUpUsernames.TryGetValue(model.UserString, out User user)) + { + model.UserID = user.Id; + return Task.CompletedTask; + } + + var request = new GetUserRequest(model.UserString); + request.Success += user => + { + model.UserID = user.Id; + previouslyLookedUpUsernames.TryAdd(model.UserString, user); + }; + + api.Queue(request); + } + + return Task.CompletedTask; + } protected override void ExportModelTo(ScoreInfo model, Stream outputStream) { From 0a87b461d70ad61e423675d45f04e253bae73259 Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Tue, 31 Aug 2021 14:11:37 +0100 Subject: [PATCH 1610/2442] fix code quality issues --- osu.Game/Scoring/ScoreManager.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 4cfcc00bb8..d5d33283b3 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -76,7 +76,7 @@ namespace osu.Game.Scoring } } - private Dictionary previouslyLookedUpUsernames = new Dictionary(); + private readonly Dictionary previouslyLookedUpUsernames = new Dictionary(); protected override Task Populate(ScoreInfo model, ArchiveReader archive, CancellationToken cancellationToken = default) { @@ -90,10 +90,10 @@ namespace osu.Game.Scoring } var request = new GetUserRequest(model.UserString); - request.Success += user => + request.Success += u => { - model.UserID = user.Id; - previouslyLookedUpUsernames.TryAdd(model.UserString, user); + model.UserID = u.Id; + previouslyLookedUpUsernames.TryAdd(model.UserString, u); }; api.Queue(request); From 9288ca1191dfbbcf0099ff749890580cc050e27f Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Tue, 31 Aug 2021 14:34:45 +0100 Subject: [PATCH 1611/2442] handle api is null --- osu.Game/Scoring/ScoreManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index d5d33283b3..3c99dd6637 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -96,7 +96,7 @@ namespace osu.Game.Scoring previouslyLookedUpUsernames.TryAdd(model.UserString, u); }; - api.Queue(request); + api?.Queue(request); } return Task.CompletedTask; From a190801291d79d42115f8ed03805109228cda21e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 31 Aug 2021 19:36:27 +0300 Subject: [PATCH 1612/2442] Revert no longer required tooltip content changes --- osu.Game/Overlays/Mods/LocalPlayerModButton.cs | 3 +-- osu.Game/Overlays/Mods/ModButton.cs | 6 +++--- osu.Game/Overlays/Mods/ModButtonTooltip.cs | 10 ++++------ 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/Mods/LocalPlayerModButton.cs b/osu.Game/Overlays/Mods/LocalPlayerModButton.cs index 1d5ddfcf06..d26bbf344d 100644 --- a/osu.Game/Overlays/Mods/LocalPlayerModButton.cs +++ b/osu.Game/Overlays/Mods/LocalPlayerModButton.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 osu.Framework.Allocation; @@ -66,7 +65,7 @@ namespace osu.Game.Overlays.Mods incompatibleIcon.Hide(); } - public override ITooltip GetCustomTooltip() => new LocalPlayerModButtonTooltip(); + public override ITooltip GetCustomTooltip() => new LocalPlayerModButtonTooltip(); private class LocalPlayerModButtonTooltip : ModButtonTooltip { diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index cc8acb7513..979e2c8da3 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -23,7 +23,7 @@ namespace osu.Game.Overlays.Mods /// /// Represents a clickable button which can cycle through one of more mods. /// - public class ModButton : ModButtonEmpty, IHasCustomTooltip + public class ModButton : ModButtonEmpty, IHasCustomTooltip { private ModIcon foregroundIcon; private ModIcon backgroundIcon; @@ -312,8 +312,8 @@ namespace osu.Game.Overlays.Mods Mod = mod; } - public virtual ITooltip GetCustomTooltip() => new ModButtonTooltip(); + public virtual ITooltip GetCustomTooltip() => new ModButtonTooltip(); - public object TooltipContent => this; + public Mod TooltipContent => SelectedMod ?? Mods.FirstOrDefault(); } } diff --git a/osu.Game/Overlays/Mods/ModButtonTooltip.cs b/osu.Game/Overlays/Mods/ModButtonTooltip.cs index 0ad479670b..2f50e38a5a 100644 --- a/osu.Game/Overlays/Mods/ModButtonTooltip.cs +++ b/osu.Game/Overlays/Mods/ModButtonTooltip.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.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -14,7 +13,7 @@ using osuTK; namespace osu.Game.Overlays.Mods { - public class ModButtonTooltip : VisibilityContainer, ITooltip + public class ModButtonTooltip : VisibilityContainer, ITooltip { private readonly OsuSpriteText descriptionText; private readonly Box background; @@ -61,11 +60,10 @@ namespace osu.Game.Overlays.Mods private Mod lastMod; - public virtual void SetContent(ModButton button) + public void SetContent(Mod mod) { - var mod = button.SelectedMod ?? button.Mods.First(); - - if (mod.Equals(lastMod)) return; + if (mod.Equals(lastMod)) + return; lastMod = mod; From 208f66cc76ee102be8ae06397e11c7af4f894f89 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 28 Aug 2021 19:11:44 +0300 Subject: [PATCH 1613/2442] Simplify user graph tooltips logic The same tooltip can be used for the rank graph, the play history graph, and the replay history graph. The only difference between those three is the displayed label, which has now been included as part of the `TooltipContent`, rather than unnecessarily recreating tooltips just for different sprite texts. --- .../Profile/Header/Components/RankGraph.cs | 33 ++------------- .../Sections/Historical/UserHistoryGraph.cs | 42 +++---------------- osu.Game/Overlays/Profile/UserGraph.cs | 36 ++++++++++------ 3 files changed, 32 insertions(+), 79 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs index 7ba8ae7c80..3312cb2c4f 100644 --- a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs @@ -8,7 +8,6 @@ using Humanizer; using osu.Framework.Bindables; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; -using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Resources.Localisation.Web; @@ -61,40 +60,16 @@ namespace osu.Game.Overlays.Profile.Header.Components placeholder.FadeIn(FADE_DURATION, Easing.Out); } - protected override object GetTooltipContent(int index, int rank) + protected override UserGraphTooltipContent GetTooltipContent(int index, int rank) { var days = ranked_days - index + 1; - return new TooltipDisplayContent + return new UserGraphTooltipContent { - Rank = rank.ToLocalisableString("\\##,##0"), + Name = UsersStrings.ShowRankGlobalSimple, + Count = rank.ToLocalisableString("\\##,##0"), Time = days == 0 ? "now" : $"{"day".ToQuantity(days)} ago" }; } - - protected override UserGraphTooltip GetTooltip() => new RankGraphTooltip(); - - private class RankGraphTooltip : UserGraphTooltip - { - public RankGraphTooltip() - : base(UsersStrings.ShowRankGlobalSimple) - { - } - - public override void SetContent(object content) - { - if (!(content is TooltipDisplayContent info)) - return; - - Counter.Text = info.Rank; - BottomText.Text = info.Time; - } - } - - private class TooltipDisplayContent - { - public LocalisableString Rank; - public string Time; - } } } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs b/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs index 85287d2325..d86e976e70 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs @@ -28,43 +28,11 @@ namespace osu.Game.Overlays.Profile.Sections.Historical protected override float GetDataPointHeight(long playCount) => playCount; - protected override UserGraphTooltip GetTooltip() => new HistoryGraphTooltip(tooltipCounterName); - - protected override object GetTooltipContent(DateTime date, long playCount) + protected override UserGraphTooltipContent GetTooltipContent(DateTime date, long playCount) => new UserGraphTooltipContent { - return new TooltipDisplayContent - { - Name = tooltipCounterName, - Count = playCount.ToLocalisableString("N0"), - Date = date.ToLocalisableString("MMMM yyyy") - }; - } - - protected class HistoryGraphTooltip : UserGraphTooltip - { - private readonly LocalisableString tooltipCounterName; - - public HistoryGraphTooltip(LocalisableString tooltipCounterName) - : base(tooltipCounterName) - { - this.tooltipCounterName = tooltipCounterName; - } - - public override void SetContent(object content) - { - if (!(content is TooltipDisplayContent info) || info.Name != tooltipCounterName) - return; - - Counter.Text = info.Count; - BottomText.Text = info.Date; - } - } - - private class TooltipDisplayContent - { - public LocalisableString Name; - public LocalisableString Count; - public LocalisableString Date; - } + Name = tooltipCounterName, + Count = playCount.ToLocalisableString("N0"), + Time = date.ToLocalisableString("MMMM yyyy") + }; } } diff --git a/osu.Game/Overlays/Profile/UserGraph.cs b/osu.Game/Overlays/Profile/UserGraph.cs index b88cc32ff7..502bbbe1a6 100644 --- a/osu.Game/Overlays/Profile/UserGraph.cs +++ b/osu.Game/Overlays/Profile/UserGraph.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Profile /// /// Type of data to be used for X-axis of the graph. /// Type of data to be used for Y-axis of the graph. - public abstract class UserGraph : Container, IHasCustomTooltip + public abstract class UserGraph : Container, IHasCustomTooltip { protected const float FADE_DURATION = 150; @@ -118,23 +118,21 @@ namespace osu.Game.Overlays.Profile protected virtual void ShowGraph() => graph.FadeIn(FADE_DURATION, Easing.Out); protected virtual void HideGraph() => graph.FadeOut(FADE_DURATION, Easing.Out); - public ITooltip GetCustomTooltip() => GetTooltip(); + public ITooltip GetCustomTooltip() => new UserGraphTooltip(); - protected abstract UserGraphTooltip GetTooltip(); - - public object TooltipContent + public UserGraphTooltipContent TooltipContent { get { if (data == null || hoveredIndex == -1) - return null; + return default; var (key, value) = data[hoveredIndex]; return GetTooltipContent(key, value); } } - protected abstract object GetTooltipContent(TKey key, TValue value); + protected abstract UserGraphTooltipContent GetTooltipContent(TKey key, TValue value); protected class UserLineGraph : LineGraph { @@ -207,12 +205,12 @@ namespace osu.Game.Overlays.Profile } } - protected abstract class UserGraphTooltip : VisibilityContainer, ITooltip + private class UserGraphTooltip : VisibilityContainer, ITooltip { - protected readonly OsuSpriteText Counter, BottomText; + protected readonly OsuSpriteText Label, Counter, BottomText; private readonly Box background; - protected UserGraphTooltip(LocalisableString tooltipCounterName) + public UserGraphTooltip() { AutoSizeAxes = Axes.Both; Masking = true; @@ -238,10 +236,9 @@ namespace osu.Game.Overlays.Profile Spacing = new Vector2(3, 0), Children = new Drawable[] { - new OsuSpriteText + Label = new OsuSpriteText { Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), - Text = tooltipCounterName }, Counter = new OsuSpriteText { @@ -268,7 +265,12 @@ namespace osu.Game.Overlays.Profile background.Colour = colours.Gray1; } - public abstract void SetContent(object content); + public void SetContent(UserGraphTooltipContent content) + { + Label.Text = content.Name; + Counter.Text = content.Count; + BottomText.Text = content.Time; + } private bool instantMove = true; @@ -292,4 +294,12 @@ namespace osu.Game.Overlays.Profile protected override void PopOut() => this.FadeOut(200, Easing.OutQuint); } } + + public class UserGraphTooltipContent + { + // todo: change to init-only on C# 9 + public LocalisableString Name { get; set; } + public LocalisableString Count { get; set; } + public LocalisableString Time { get; set; } + } } From da7ff4b1601019a44c96f3ee0fcc0004d541d288 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 28 Aug 2021 19:09:37 +0300 Subject: [PATCH 1614/2442] Update remaining tooltip implementations to use generics --- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 115 +---------------- .../Drawables/DifficultyIconTooltip.cs | 121 ++++++++++++++++++ osu.Game/Graphics/DrawableDate.cs | 6 +- .../Home/News/FeaturedNewsItemPanel.cs | 14 +- .../Dashboard/Home/News/NewsGroupItem.cs | 14 +- osu.Game/Overlays/News/NewsCard.cs | 12 +- 6 files changed, 144 insertions(+), 138 deletions(-) create mode 100644 osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 199f719893..cda4377780 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -16,7 +16,6 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osuTK; @@ -24,7 +23,7 @@ using osuTK.Graphics; namespace osu.Game.Beatmaps.Drawables { - public class DifficultyIcon : CompositeDrawable, IHasCustomTooltip + public class DifficultyIcon : CompositeDrawable, IHasCustomTooltip { private readonly Container iconContainer; @@ -127,9 +126,9 @@ namespace osu.Game.Beatmaps.Drawables difficultyBindable.BindValueChanged(difficulty => background.Colour = colours.ForStarDifficulty(difficulty.NewValue.Stars)); } - public ITooltip GetCustomTooltip() => new DifficultyIconTooltip(); + public ITooltip GetCustomTooltip() => new DifficultyIconTooltip(); - public object TooltipContent => shouldShowTooltip ? new DifficultyIconTooltipContent(beatmap, difficultyBindable) : null; + public DifficultyIconTooltipContent TooltipContent => shouldShowTooltip ? new DifficultyIconTooltipContent(beatmap, difficultyBindable) : null; private class DifficultyRetriever : Component { @@ -173,113 +172,5 @@ namespace osu.Game.Beatmaps.Drawables difficultyCancellation?.Cancel(); } } - - private class DifficultyIconTooltipContent - { - public readonly BeatmapInfo Beatmap; - public readonly IBindable Difficulty; - - public DifficultyIconTooltipContent(BeatmapInfo beatmap, IBindable difficulty) - { - Beatmap = beatmap; - Difficulty = difficulty; - } - } - - private class DifficultyIconTooltip : VisibilityContainer, ITooltip - { - private readonly OsuSpriteText difficultyName, starRating; - private readonly Box background; - private readonly FillFlowContainer difficultyFlow; - - public DifficultyIconTooltip() - { - AutoSizeAxes = Axes.Both; - Masking = true; - CornerRadius = 5; - - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - AutoSizeDuration = 200, - AutoSizeEasing = Easing.OutQuint, - Direction = FillDirection.Vertical, - Padding = new MarginPadding(10), - Children = new Drawable[] - { - difficultyName = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.GetFont(size: 16, weight: FontWeight.Bold), - }, - difficultyFlow = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Direction = FillDirection.Horizontal, - Children = new Drawable[] - { - starRating = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.GetFont(size: 16, weight: FontWeight.Regular), - }, - new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Margin = new MarginPadding { Left = 4 }, - Icon = FontAwesome.Solid.Star, - Size = new Vector2(12), - }, - } - } - } - } - }; - } - - [Resolved] - private OsuColour colours { get; set; } - - [BackgroundDependencyLoader] - private void load() - { - background.Colour = colours.Gray3; - } - - private readonly IBindable starDifficulty = new Bindable(); - - public void SetContent(object content) - { - if (!(content is DifficultyIconTooltipContent iconContent)) - return; - - difficultyName.Text = iconContent.Beatmap.Version; - - starDifficulty.UnbindAll(); - starDifficulty.BindTo(iconContent.Difficulty); - starDifficulty.BindValueChanged(difficulty => - { - starRating.Text = $"{difficulty.NewValue.Stars:0.##}"; - difficultyFlow.Colour = colours.ForStarDifficulty(difficulty.NewValue.Stars); - }, true); - } - - public void Move(Vector2 pos) => Position = pos; - - protected override void PopIn() => this.FadeIn(200, Easing.OutQuint); - - protected override void PopOut() => this.FadeOut(200, Easing.OutQuint); - } } } diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs b/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs new file mode 100644 index 0000000000..5b05e39090 --- /dev/null +++ b/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs @@ -0,0 +1,121 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osuTK; + +namespace osu.Game.Beatmaps.Drawables +{ + public class DifficultyIconTooltip : VisibilityContainer, ITooltip + { + private readonly OsuSpriteText difficultyName, starRating; + private readonly Box background; + private readonly FillFlowContainer difficultyFlow; + + public DifficultyIconTooltip() + { + AutoSizeAxes = Axes.Both; + Masking = true; + CornerRadius = 5; + + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + AutoSizeDuration = 200, + AutoSizeEasing = Easing.OutQuint, + Direction = FillDirection.Vertical, + Padding = new MarginPadding(10), + Children = new Drawable[] + { + difficultyName = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.GetFont(size: 16, weight: FontWeight.Bold), + }, + difficultyFlow = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + starRating = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.GetFont(size: 16, weight: FontWeight.Regular), + }, + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Margin = new MarginPadding { Left = 4 }, + Icon = FontAwesome.Solid.Star, + Size = new Vector2(12), + }, + } + } + } + } + }; + } + + [Resolved] + private OsuColour colours { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + background.Colour = colours.Gray3; + } + + private readonly IBindable starDifficulty = new Bindable(); + + public void SetContent(DifficultyIconTooltipContent content) + { + difficultyName.Text = content.Beatmap.Version; + + starDifficulty.UnbindAll(); + starDifficulty.BindTo(content.Difficulty); + starDifficulty.BindValueChanged(difficulty => + { + starRating.Text = $"{difficulty.NewValue.Stars:0.##}"; + difficultyFlow.Colour = colours.ForStarDifficulty(difficulty.NewValue.Stars); + }, true); + } + + public void Move(Vector2 pos) => Position = pos; + + protected override void PopIn() => this.FadeIn(200, Easing.OutQuint); + + protected override void PopOut() => this.FadeOut(200, Easing.OutQuint); + } + + public class DifficultyIconTooltipContent + { + public readonly BeatmapInfo Beatmap; + public readonly IBindable Difficulty; + + public DifficultyIconTooltipContent(BeatmapInfo beatmap, IBindable difficulty) + { + Beatmap = beatmap; + Difficulty = difficulty; + } + } +} diff --git a/osu.Game/Graphics/DrawableDate.cs b/osu.Game/Graphics/DrawableDate.cs index 259d9c8d6e..567a39b4f4 100644 --- a/osu.Game/Graphics/DrawableDate.cs +++ b/osu.Game/Graphics/DrawableDate.cs @@ -10,7 +10,7 @@ using osu.Game.Utils; namespace osu.Game.Graphics { - public class DrawableDate : OsuSpriteText, IHasCustomTooltip + public class DrawableDate : OsuSpriteText, IHasCustomTooltip { private DateTimeOffset date; @@ -75,8 +75,8 @@ namespace osu.Game.Graphics private void updateTime() => Text = Format(); - public ITooltip GetCustomTooltip() => new DateTooltip(); + public ITooltip GetCustomTooltip() => new DateTooltip(); - public object TooltipContent => Date; + public DateTimeOffset TooltipContent => Date; } } diff --git a/osu.Game/Overlays/Dashboard/Home/News/FeaturedNewsItemPanel.cs b/osu.Game/Overlays/Dashboard/Home/News/FeaturedNewsItemPanel.cs index ee88469e2f..72a85bcb6c 100644 --- a/osu.Game/Overlays/Dashboard/Home/News/FeaturedNewsItemPanel.cs +++ b/osu.Game/Overlays/Dashboard/Home/News/FeaturedNewsItemPanel.cs @@ -140,17 +140,15 @@ namespace osu.Game.Overlays.Dashboard.Home.News } } - private class Date : CompositeDrawable, IHasCustomTooltip + private class Date : CompositeDrawable, IHasCustomTooltip { - public ITooltip GetCustomTooltip() => new DateTooltip(); + public ITooltip GetCustomTooltip() => new DateTooltip(); - public object TooltipContent => date; - - private readonly DateTimeOffset date; + public DateTimeOffset TooltipContent { get; } public Date(DateTimeOffset date) { - this.date = date; + TooltipContent = date; } [BackgroundDependencyLoader] @@ -174,7 +172,7 @@ namespace osu.Game.Overlays.Dashboard.Home.News Origin = Anchor.TopRight, Font = OsuFont.GetFont(weight: FontWeight.Bold), // using Bold since there is no 800 weight alternative Colour = colourProvider.Light1, - Text = $"{date:dd}" + Text = $"{TooltipContent:dd}" }, new TextFlowContainer(f => { @@ -185,7 +183,7 @@ namespace osu.Game.Overlays.Dashboard.Home.News Anchor = Anchor.TopRight, Origin = Anchor.TopRight, AutoSizeAxes = Axes.Both, - Text = $"{date:MMM yyyy}" + Text = $"{TooltipContent:MMM yyyy}" } } }; diff --git a/osu.Game/Overlays/Dashboard/Home/News/NewsGroupItem.cs b/osu.Game/Overlays/Dashboard/Home/News/NewsGroupItem.cs index dc4f3f8c92..a681536156 100644 --- a/osu.Game/Overlays/Dashboard/Home/News/NewsGroupItem.cs +++ b/osu.Game/Overlays/Dashboard/Home/News/NewsGroupItem.cs @@ -67,17 +67,15 @@ namespace osu.Game.Overlays.Dashboard.Home.News }; } - private class Date : CompositeDrawable, IHasCustomTooltip + private class Date : CompositeDrawable, IHasCustomTooltip { - public ITooltip GetCustomTooltip() => new DateTooltip(); + public ITooltip GetCustomTooltip() => new DateTooltip(); - public object TooltipContent => date; - - private readonly DateTimeOffset date; + public DateTimeOffset TooltipContent { get; } public Date(DateTimeOffset date) { - this.date = date; + TooltipContent = date; } [BackgroundDependencyLoader] @@ -100,12 +98,12 @@ namespace osu.Game.Overlays.Dashboard.Home.News Margin = new MarginPadding { Vertical = 5 } }; - textFlow.AddText($"{date:dd}", t => + textFlow.AddText($"{TooltipContent:dd}", t => { t.Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold); }); - textFlow.AddText($"{date: MMM}", t => + textFlow.AddText($"{TooltipContent: MMM}", t => { t.Font = OsuFont.GetFont(size: 14, weight: FontWeight.Regular); }); diff --git a/osu.Game/Overlays/News/NewsCard.cs b/osu.Game/Overlays/News/NewsCard.cs index cc2fa7e1e1..aee0a50de9 100644 --- a/osu.Game/Overlays/News/NewsCard.cs +++ b/osu.Game/Overlays/News/NewsCard.cs @@ -123,17 +123,15 @@ namespace osu.Game.Overlays.News main.AddText(post.Author, t => t.Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold)); } - private class DateContainer : CircularContainer, IHasCustomTooltip + private class DateContainer : CircularContainer, IHasCustomTooltip { - public ITooltip GetCustomTooltip() => new DateTooltip(); + public ITooltip GetCustomTooltip() => new DateTooltip(); - public object TooltipContent => date; - - private readonly DateTimeOffset date; + public DateTimeOffset TooltipContent { get; } public DateContainer(DateTimeOffset date) { - this.date = date; + TooltipContent = date; } [BackgroundDependencyLoader] @@ -150,7 +148,7 @@ namespace osu.Game.Overlays.News }, new OsuSpriteText { - Text = date.ToString("d MMM yyyy").ToUpper(), + Text = TooltipContent.ToString("d MMM yyyy").ToUpper(), Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), Margin = new MarginPadding { From 69c23a2371ea9f962981a671a710a763178ceee4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 31 Aug 2021 20:06:34 +0300 Subject: [PATCH 1615/2442] Explicitly implement tooltips on date drawables to avoid "convert to auto-property" inspections --- .../Dashboard/Home/News/FeaturedNewsItemPanel.cs | 14 ++++++++------ .../Overlays/Dashboard/Home/News/NewsGroupItem.cs | 14 ++++++++------ osu.Game/Overlays/News/NewsCard.cs | 12 +++++++----- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/osu.Game/Overlays/Dashboard/Home/News/FeaturedNewsItemPanel.cs b/osu.Game/Overlays/Dashboard/Home/News/FeaturedNewsItemPanel.cs index 72a85bcb6c..0d166eb858 100644 --- a/osu.Game/Overlays/Dashboard/Home/News/FeaturedNewsItemPanel.cs +++ b/osu.Game/Overlays/Dashboard/Home/News/FeaturedNewsItemPanel.cs @@ -142,13 +142,11 @@ namespace osu.Game.Overlays.Dashboard.Home.News private class Date : CompositeDrawable, IHasCustomTooltip { - public ITooltip GetCustomTooltip() => new DateTooltip(); - - public DateTimeOffset TooltipContent { get; } + private readonly DateTimeOffset date; public Date(DateTimeOffset date) { - TooltipContent = date; + this.date = date; } [BackgroundDependencyLoader] @@ -172,7 +170,7 @@ namespace osu.Game.Overlays.Dashboard.Home.News Origin = Anchor.TopRight, Font = OsuFont.GetFont(weight: FontWeight.Bold), // using Bold since there is no 800 weight alternative Colour = colourProvider.Light1, - Text = $"{TooltipContent:dd}" + Text = $"{date:dd}" }, new TextFlowContainer(f => { @@ -183,11 +181,15 @@ namespace osu.Game.Overlays.Dashboard.Home.News Anchor = Anchor.TopRight, Origin = Anchor.TopRight, AutoSizeAxes = Axes.Both, - Text = $"{TooltipContent:MMM yyyy}" + Text = $"{date:MMM yyyy}" } } }; } + + ITooltip IHasCustomTooltip.GetCustomTooltip() => new DateTooltip(); + + DateTimeOffset IHasCustomTooltip.TooltipContent => date; } } } diff --git a/osu.Game/Overlays/Dashboard/Home/News/NewsGroupItem.cs b/osu.Game/Overlays/Dashboard/Home/News/NewsGroupItem.cs index a681536156..77cfbc90b0 100644 --- a/osu.Game/Overlays/Dashboard/Home/News/NewsGroupItem.cs +++ b/osu.Game/Overlays/Dashboard/Home/News/NewsGroupItem.cs @@ -69,13 +69,11 @@ namespace osu.Game.Overlays.Dashboard.Home.News private class Date : CompositeDrawable, IHasCustomTooltip { - public ITooltip GetCustomTooltip() => new DateTooltip(); - - public DateTimeOffset TooltipContent { get; } + private readonly DateTimeOffset date; public Date(DateTimeOffset date) { - TooltipContent = date; + this.date = date; } [BackgroundDependencyLoader] @@ -98,16 +96,20 @@ namespace osu.Game.Overlays.Dashboard.Home.News Margin = new MarginPadding { Vertical = 5 } }; - textFlow.AddText($"{TooltipContent:dd}", t => + textFlow.AddText($"{date:dd}", t => { t.Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold); }); - textFlow.AddText($"{TooltipContent: MMM}", t => + textFlow.AddText($"{date: MMM}", t => { t.Font = OsuFont.GetFont(size: 14, weight: FontWeight.Regular); }); } + + ITooltip IHasCustomTooltip.GetCustomTooltip() => new DateTooltip(); + + DateTimeOffset IHasCustomTooltip.TooltipContent => date; } } } diff --git a/osu.Game/Overlays/News/NewsCard.cs b/osu.Game/Overlays/News/NewsCard.cs index aee0a50de9..68d0704825 100644 --- a/osu.Game/Overlays/News/NewsCard.cs +++ b/osu.Game/Overlays/News/NewsCard.cs @@ -125,13 +125,11 @@ namespace osu.Game.Overlays.News private class DateContainer : CircularContainer, IHasCustomTooltip { - public ITooltip GetCustomTooltip() => new DateTooltip(); - - public DateTimeOffset TooltipContent { get; } + private readonly DateTimeOffset date; public DateContainer(DateTimeOffset date) { - TooltipContent = date; + this.date = date; } [BackgroundDependencyLoader] @@ -148,7 +146,7 @@ namespace osu.Game.Overlays.News }, new OsuSpriteText { - Text = TooltipContent.ToString("d MMM yyyy").ToUpper(), + Text = date.ToString("d MMM yyyy").ToUpper(), Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), Margin = new MarginPadding { @@ -160,6 +158,10 @@ namespace osu.Game.Overlays.News } protected override bool OnClick(ClickEvent e) => true; // Protects the NewsCard from clicks while hovering DateContainer + + ITooltip IHasCustomTooltip.GetCustomTooltip() => new DateTooltip(); + + DateTimeOffset IHasCustomTooltip.TooltipContent => date; } } } From 3969350c9a38bd8ccf1408656c08dd280dbf0ec6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 31 Aug 2021 20:45:32 +0300 Subject: [PATCH 1616/2442] Convert to `readonly struct` and replace with constructor temporarily --- .../Profile/Header/Components/RankGraph.cs | 10 ++++------ .../Sections/Historical/UserHistoryGraph.cs | 11 ++++++----- osu.Game/Overlays/Profile/UserGraph.cs | 17 ++++++++++++----- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs index 3312cb2c4f..ca5f26e375 100644 --- a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs @@ -64,12 +64,10 @@ namespace osu.Game.Overlays.Profile.Header.Components { var days = ranked_days - index + 1; - return new UserGraphTooltipContent - { - Name = UsersStrings.ShowRankGlobalSimple, - Count = rank.ToLocalisableString("\\##,##0"), - Time = days == 0 ? "now" : $"{"day".ToQuantity(days)} ago" - }; + return new UserGraphTooltipContent( + UsersStrings.ShowRankGlobalSimple, + rank.ToLocalisableString("\\##,##0"), + days == 0 ? "now" : $"{"day".ToQuantity(days)} ago"); } } } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs b/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs index d86e976e70..738edb9310 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs @@ -28,11 +28,12 @@ namespace osu.Game.Overlays.Profile.Sections.Historical protected override float GetDataPointHeight(long playCount) => playCount; - protected override UserGraphTooltipContent GetTooltipContent(DateTime date, long playCount) => new UserGraphTooltipContent + protected override UserGraphTooltipContent GetTooltipContent(DateTime date, long playCount) { - Name = tooltipCounterName, - Count = playCount.ToLocalisableString("N0"), - Time = date.ToLocalisableString("MMMM yyyy") - }; + return new UserGraphTooltipContent( + tooltipCounterName, + playCount.ToLocalisableString("N0"), + date.ToLocalisableString("MMMM yyyy")); + } } } diff --git a/osu.Game/Overlays/Profile/UserGraph.cs b/osu.Game/Overlays/Profile/UserGraph.cs index 502bbbe1a6..f305e3afc3 100644 --- a/osu.Game/Overlays/Profile/UserGraph.cs +++ b/osu.Game/Overlays/Profile/UserGraph.cs @@ -295,11 +295,18 @@ namespace osu.Game.Overlays.Profile } } - public class UserGraphTooltipContent + public readonly struct UserGraphTooltipContent { - // todo: change to init-only on C# 9 - public LocalisableString Name { get; set; } - public LocalisableString Count { get; set; } - public LocalisableString Time { get; set; } + // todo: could use init-only properties on C# 9 which read better than a constructor. + public LocalisableString Name { get; } + public LocalisableString Count { get; } + public LocalisableString Time { get; } + + public UserGraphTooltipContent(LocalisableString name, LocalisableString count, LocalisableString time) + { + Name = name; + Count = count; + Time = time; + } } } From 4a590a041ccf6d755f81f8e052ac092d325ded05 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 31 Aug 2021 20:57:36 +0300 Subject: [PATCH 1617/2442] Constrain difficulty icon tooltip to `internal` accessibility --- osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs b/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs index 5b05e39090..0329e935bc 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs @@ -14,7 +14,7 @@ using osuTK; namespace osu.Game.Beatmaps.Drawables { - public class DifficultyIconTooltip : VisibilityContainer, ITooltip + internal class DifficultyIconTooltip : VisibilityContainer, ITooltip { private readonly OsuSpriteText difficultyName, starRating; private readonly Box background; @@ -107,7 +107,7 @@ namespace osu.Game.Beatmaps.Drawables protected override void PopOut() => this.FadeOut(200, Easing.OutQuint); } - public class DifficultyIconTooltipContent + internal class DifficultyIconTooltipContent { public readonly BeatmapInfo Beatmap; public readonly IBindable Difficulty; From cd356b8eae1500a2f2127058b7686537d1fc4796 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 31 Aug 2021 20:57:47 +0300 Subject: [PATCH 1618/2442] Revert "Constrain difficulty icon tooltip to `internal` accessibility" This reverts commit 4a590a041ccf6d755f81f8e052ac092d325ded05. --- osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs b/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs index 0329e935bc..5b05e39090 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs @@ -14,7 +14,7 @@ using osuTK; namespace osu.Game.Beatmaps.Drawables { - internal class DifficultyIconTooltip : VisibilityContainer, ITooltip + public class DifficultyIconTooltip : VisibilityContainer, ITooltip { private readonly OsuSpriteText difficultyName, starRating; private readonly Box background; @@ -107,7 +107,7 @@ namespace osu.Game.Beatmaps.Drawables protected override void PopOut() => this.FadeOut(200, Easing.OutQuint); } - internal class DifficultyIconTooltipContent + public class DifficultyIconTooltipContent { public readonly BeatmapInfo Beatmap; public readonly IBindable Difficulty; From b0d7104650a125093d8ab6d354c45bfa630f74ee Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 31 Aug 2021 21:13:20 +0300 Subject: [PATCH 1619/2442] Convert to `class` to allow not displaying tooltips With `struct` content, it is never possible to not show a tooltip. --- osu.Game/Overlays/Profile/UserGraph.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/UserGraph.cs b/osu.Game/Overlays/Profile/UserGraph.cs index f305e3afc3..182221eea7 100644 --- a/osu.Game/Overlays/Profile/UserGraph.cs +++ b/osu.Game/Overlays/Profile/UserGraph.cs @@ -125,7 +125,7 @@ namespace osu.Game.Overlays.Profile get { if (data == null || hoveredIndex == -1) - return default; + return null; var (key, value) = data[hoveredIndex]; return GetTooltipContent(key, value); @@ -295,7 +295,7 @@ namespace osu.Game.Overlays.Profile } } - public readonly struct UserGraphTooltipContent + public class UserGraphTooltipContent { // todo: could use init-only properties on C# 9 which read better than a constructor. public LocalisableString Name { get; } From 505824d8eaccfbd1f9e2550004ca65af7c49c8a4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 31 Aug 2021 21:16:02 +0300 Subject: [PATCH 1620/2442] Constrain difficulty icon tooltip to `internal` accessibility" This reverts the reverted commit cd356b8eae1500a2f2127058b7686537d1fc4796. Sorry for the revert-unrevert, rushly pushed without realizing it doesn't even build. --- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 4 ++-- osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index cda4377780..0751a777d8 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -126,9 +126,9 @@ namespace osu.Game.Beatmaps.Drawables difficultyBindable.BindValueChanged(difficulty => background.Colour = colours.ForStarDifficulty(difficulty.NewValue.Stars)); } - public ITooltip GetCustomTooltip() => new DifficultyIconTooltip(); + ITooltip IHasCustomTooltip.GetCustomTooltip() => new DifficultyIconTooltip(); - public DifficultyIconTooltipContent TooltipContent => shouldShowTooltip ? new DifficultyIconTooltipContent(beatmap, difficultyBindable) : null; + DifficultyIconTooltipContent IHasCustomTooltip.TooltipContent => shouldShowTooltip ? new DifficultyIconTooltipContent(beatmap, difficultyBindable) : null; private class DifficultyRetriever : Component { diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs b/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs index 5b05e39090..0329e935bc 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs @@ -14,7 +14,7 @@ using osuTK; namespace osu.Game.Beatmaps.Drawables { - public class DifficultyIconTooltip : VisibilityContainer, ITooltip + internal class DifficultyIconTooltip : VisibilityContainer, ITooltip { private readonly OsuSpriteText difficultyName, starRating; private readonly Box background; @@ -107,7 +107,7 @@ namespace osu.Game.Beatmaps.Drawables protected override void PopOut() => this.FadeOut(200, Easing.OutQuint); } - public class DifficultyIconTooltipContent + internal class DifficultyIconTooltipContent { public readonly BeatmapInfo Beatmap; public readonly IBindable Difficulty; From da3fa9304aef4533246bb4b35f358cd536e3bf08 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 31 Aug 2021 12:39:18 -0700 Subject: [PATCH 1621/2442] Make toolbar inherit overlay container --- osu.Game/Overlays/Toolbar/Toolbar.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 2664301a0c..8184954753 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -18,7 +18,7 @@ using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Toolbar { - public class Toolbar : VisibilityContainer, IKeyBindingHandler + public class Toolbar : OverlayContainer, IKeyBindingHandler { public const float HEIGHT = 40; public const float TOOLTIP_HEIGHT = 30; @@ -41,8 +41,6 @@ namespace osu.Game.Overlays.Toolbar // Toolbar and its components need keyboard input even when hidden. public override bool PropagateNonPositionalInputSubTree => true; - protected override bool Handle(UIEvent e) => e is MouseEvent; - public Toolbar() { RelativeSizeAxes = Axes.X; From 7e4ad7d7cfaf6e0dc435f157320807b26f12e184 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 31 Aug 2021 13:40:13 -0700 Subject: [PATCH 1622/2442] Fix toolbar blocking scroll input --- osu.Game/Overlays/Toolbar/Toolbar.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 8184954753..7481cfdbf5 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -41,6 +41,8 @@ namespace osu.Game.Overlays.Toolbar // Toolbar and its components need keyboard input even when hidden. public override bool PropagateNonPositionalInputSubTree => true; + protected override bool BlockScrollInput => false; + public Toolbar() { RelativeSizeAxes = Axes.X; From 04773b51bb6efa4964dc6df92158ea23aba679ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 31 Aug 2021 22:37:40 +0200 Subject: [PATCH 1623/2442] Remove countdown toggle transition for now Tricky to get right and the design isn't final as is anyway, so leaving *something* functioning as a best-effort for now. --- osu.Game/Screens/Edit/Setup/DesignSection.cs | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/osu.Game/Screens/Edit/Setup/DesignSection.cs b/osu.Game/Screens/Edit/Setup/DesignSection.cs index d030c2a894..ede6a52276 100644 --- a/osu.Game/Screens/Edit/Setup/DesignSection.cs +++ b/osu.Game/Screens/Edit/Setup/DesignSection.cs @@ -16,8 +16,6 @@ namespace osu.Game.Screens.Edit.Setup { internal class DesignSection : SetupSection { - private const float fade_duration = 250; - protected LabelledSwitchButton EnableCountdown; protected LabelledEnumDropdown CountdownSpeed; protected LabelledNumberBox CountdownOffset; @@ -90,7 +88,6 @@ namespace osu.Game.Screens.Edit.Setup base.LoadComplete(); EnableCountdown.Current.BindValueChanged(_ => updateCountdownSettingsVisibility(), true); - countdownSettings.FinishTransforms(true); EnableCountdown.Current.BindValueChanged(_ => updateBeatmap()); CountdownSpeed.Current.BindValueChanged(_ => updateBeatmap()); @@ -101,16 +98,7 @@ namespace osu.Game.Screens.Edit.Setup letterboxDuringBreaks.Current.BindValueChanged(_ => updateBeatmap()); } - private void updateCountdownSettingsVisibility() - { - bool countdownEnabled = EnableCountdown.Current.Value; - - foreach (var child in countdownSettings) - { - child.ScaleTo(new Vector2(1, countdownEnabled ? 1 : 0), fade_duration, Easing.OutQuint) - .FadeTo(countdownEnabled ? 1 : 0, fade_duration, Easing.OutQuint); - } - } + private void updateCountdownSettingsVisibility() => countdownSettings.FadeTo(EnableCountdown.Current.Value ? 1 : 0); private void onOffsetCommitted() { From 5dc938cc9f09fc523fd838b7bea7ce7da7e3a908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 31 Aug 2021 22:40:58 +0200 Subject: [PATCH 1624/2442] Update tests to match expectations --- osu.Game.Tests/Visual/Editing/TestSceneDesignSection.cs | 9 ++++++--- osu.Game/Screens/Edit/Setup/DesignSection.cs | 8 ++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDesignSection.cs b/osu.Game.Tests/Visual/Editing/TestSceneDesignSection.cs index d84ffa1052..00f2979691 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDesignSection.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDesignSection.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Framework.Testing; using osu.Game.Beatmaps; @@ -42,7 +43,7 @@ namespace osu.Game.Tests.Visual.Editing AddStep("turn countdown off", () => designSection.EnableCountdown.Current.Value = false); AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.None); - AddUntilStep("other controls hidden", () => !designSection.CountdownSpeed.IsPresent && !designSection.CountdownOffset.IsPresent); + AddUntilStep("other controls hidden", () => !designSection.CountdownSettings.IsPresent); } [Test] @@ -51,12 +52,12 @@ namespace osu.Game.Tests.Visual.Editing AddStep("turn countdown on", () => designSection.EnableCountdown.Current.Value = true); AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.Normal); - AddUntilStep("other controls shown", () => designSection.CountdownSpeed.IsPresent && designSection.CountdownOffset.IsPresent); + AddUntilStep("other controls shown", () => designSection.CountdownSettings.IsPresent); AddStep("change countdown speed", () => designSection.CountdownSpeed.Current.Value = CountdownType.DoubleSpeed); AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.DoubleSpeed); - AddUntilStep("other controls still shown", () => designSection.CountdownSpeed.IsPresent && designSection.CountdownOffset.IsPresent); + AddUntilStep("other controls still shown", () => designSection.CountdownSettings.IsPresent); } [Test] @@ -90,6 +91,8 @@ namespace osu.Game.Tests.Visual.Editing private class TestDesignSection : DesignSection { public new LabelledSwitchButton EnableCountdown => base.EnableCountdown; + + public new FillFlowContainer CountdownSettings => base.CountdownSettings; public new LabelledEnumDropdown CountdownSpeed => base.CountdownSpeed; public new LabelledNumberBox CountdownOffset => base.CountdownOffset; } diff --git a/osu.Game/Screens/Edit/Setup/DesignSection.cs b/osu.Game/Screens/Edit/Setup/DesignSection.cs index ede6a52276..90f95a668e 100644 --- a/osu.Game/Screens/Edit/Setup/DesignSection.cs +++ b/osu.Game/Screens/Edit/Setup/DesignSection.cs @@ -17,6 +17,8 @@ namespace osu.Game.Screens.Edit.Setup internal class DesignSection : SetupSection { protected LabelledSwitchButton EnableCountdown; + + protected FillFlowContainer CountdownSettings; protected LabelledEnumDropdown CountdownSpeed; protected LabelledNumberBox CountdownOffset; @@ -24,8 +26,6 @@ namespace osu.Game.Screens.Edit.Setup private LabelledSwitchButton epilepsyWarning; private LabelledSwitchButton letterboxDuringBreaks; - private FillFlowContainer countdownSettings; - public override LocalisableString Title => "Design"; [BackgroundDependencyLoader] @@ -39,7 +39,7 @@ namespace osu.Game.Screens.Edit.Setup Current = { Value = Beatmap.BeatmapInfo.Countdown != CountdownType.None }, Description = "If enabled, an \"Are you ready? 3, 2, 1, GO!\" countdown will be inserted at the beginning of the beatmap, assuming there is enough time to do so." }, - countdownSettings = new FillFlowContainer + CountdownSettings = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -98,7 +98,7 @@ namespace osu.Game.Screens.Edit.Setup letterboxDuringBreaks.Current.BindValueChanged(_ => updateBeatmap()); } - private void updateCountdownSettingsVisibility() => countdownSettings.FadeTo(EnableCountdown.Current.Value ? 1 : 0); + private void updateCountdownSettingsVisibility() => CountdownSettings.FadeTo(EnableCountdown.Current.Value ? 1 : 0); private void onOffsetCommitted() { From a773a22726c13aa0687c7630e91a312718c0fc1c Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 31 Aug 2021 14:29:16 -0700 Subject: [PATCH 1625/2442] Fix toolbar hiding when clicking home button --- osu.Game/OsuGame.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index a584644fc9..187669cbb4 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -160,7 +160,7 @@ namespace osu.Game private readonly string[] args; - private readonly List overlays = new List(); + private readonly List focusedOverlays = new List(); private readonly List visibleBlockingOverlays = new List(); @@ -195,7 +195,7 @@ namespace osu.Game /// Whether the toolbar should also be hidden. public void CloseAllOverlays(bool hideToolbar = true) { - foreach (var overlay in overlays) + foreach (var overlay in focusedOverlays) overlay.Hide(); if (hideToolbar) Toolbar.Hide(); @@ -910,8 +910,8 @@ namespace osu.Game if (cache) dependencies.CacheAs(component); - if (component is OverlayContainer overlay) - overlays.Add(overlay); + if (component is OsuFocusedOverlayContainer overlay) + focusedOverlays.Add(overlay); // schedule is here to ensure that all component loads are done after LoadComplete is run (and thus all dependencies are cached). // with some better organisation of LoadComplete to do construction and dependency caching in one step, followed by calls to loadComponentSingleFile, From bd0f385cdb5546f060dd73ee7f71e504475ac2a3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 1 Sep 2021 14:53:11 +0900 Subject: [PATCH 1626/2442] Make classic scoring a constant multiple of standardised scoring --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 16f2607bad..2a7691269d 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -222,12 +222,12 @@ namespace osu.Game.Rulesets.Scoring case ScoringMode.Standardised: double accuracyScore = accuracyPortion * accuracyRatio; double comboScore = comboPortion * comboRatio; - return (max_score * (accuracyScore + comboScore) + getBonusScore(statistics)) * scoreMultiplier; case ScoringMode.Classic: - // should emulate osu-stable's scoring as closely as we can (https://osu.ppy.sh/help/wiki/Score/ScoreV1) - return getBonusScore(statistics) + (accuracyRatio * Math.Max(1, maxCombo) * 300) * (1 + Math.Max(0, (comboRatio * maxCombo) - 1) * scoreMultiplier / 25); + // This gives a similar feeling to osu!stable scoring (ScoreV1) while keeping classic scoring as only a constant multiple of standardised scoring. + // The invariant is important to ensure that scores don't get re-ordered on leaderboards between the two scoring modes. + return GetScore(ScoringMode.Standardised, maxCombo, accuracyRatio, comboRatio, statistics) / max_score * Math.Pow(maxCombo, 2) * 25; } } From 7a447f5128e87f965093f04c1840f6e531fdea9f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 1 Sep 2021 15:10:24 +0900 Subject: [PATCH 1627/2442] Mark `SankingSliderBody` as `abstract` --- osu.Game.Rulesets.Osu/Skinning/Default/SnakingSliderBody.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/SnakingSliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Default/SnakingSliderBody.cs index ed4e04184b..7b7a89d5e2 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/SnakingSliderBody.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/SnakingSliderBody.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default /// /// A which changes its curve depending on the snaking progress. /// - public class SnakingSliderBody : SliderBody, ISliderProgress + public abstract class SnakingSliderBody : SliderBody, ISliderProgress { public readonly List CurrentCurve = new List(); From 4f9c3fde07a662ef43925aadb4cd562c2afed1ac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 1 Sep 2021 15:10:56 +0900 Subject: [PATCH 1628/2442] Move alpha adjustment back to `LegacySliderBody` to correctly handle default legacy skin --- .../Skinning/Default/PlaySliderBody.cs | 6 +++--- .../Skinning/Legacy/LegacySliderBody.cs | 7 +++++++ .../Legacy/OsuLegacySkinTransformer.cs | 21 ++----------------- 3 files changed, 12 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs index 4dd7b2d69c..8602ebc88b 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default pathVersion.BindValueChanged(_ => Refresh()); accentColour = drawableObject.AccentColour.GetBoundCopy(); - accentColour.BindValueChanged(accent => updateAccentColour(skin, accent.NewValue), true); + accentColour.BindValueChanged(accent => AccentColour = GetBodyAccentColour(skin, accent.NewValue), true); config?.BindWith(OsuRulesetSetting.SnakingInSliders, SnakingIn); config?.BindWith(OsuRulesetSetting.SnakingOutSliders, configSnakingOut); @@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default } } - private void updateAccentColour(ISkinSource skin, Color4 defaultAccentColour) - => AccentColour = skin.GetConfig(OsuSkinColour.SliderTrackOverride)?.Value ?? defaultAccentColour; + protected virtual Color4 GetBodyAccentColour(ISkinSource skin, Color4 hitObjectAccentColour) => + skin.GetConfig(OsuSkinColour.SliderTrackOverride)?.Value ?? hitObjectAccentColour; } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs index 7d69e5ecdc..29a0745193 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBody.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Extensions.Color4Extensions; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Skinning.Default; +using osu.Game.Skinning; using osu.Game.Utils; using osuTK.Graphics; @@ -14,6 +15,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { protected override DrawableSliderPath CreateSliderPath() => new LegacyDrawableSliderPath(); + protected override Color4 GetBodyAccentColour(ISkinSource skin, Color4 hitObjectAccentColour) + { + // legacy skins use a constant value for slider track alpha, regardless of the source colour. + return base.GetBodyAccentColour(skin, hitObjectAccentColour).Opacity(0.7f); + } + private class LegacyDrawableSliderPath : DrawableSliderPath { private const float shadow_portion = 1 - (OsuLegacySkinTransformer.LEGACY_CIRCLE_RADIUS / OsuHitObject.OBJECT_RADIUS); diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index 16c770706d..41b0a88f11 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -3,11 +3,9 @@ using System; using osu.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Game.Skinning; using osuTK; -using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Skinning.Legacy { @@ -120,23 +118,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { switch (lookup) { - case OsuSkinColour colourLookup: - var colour = base.GetConfig(new SkinCustomColourLookup(colourLookup)); - - if (colour == null) - return null; - - switch (colourLookup) - { - case OsuSkinColour.SliderTrackOverride: - var bindableColour = ((Bindable)colour); - - // legacy skins use a constant value for slider track alpha, regardless of the source colour. - bindableColour.Value = bindableColour.Value.Opacity(0.7f); - break; - } - - return colour; + case OsuSkinColour colour: + return base.GetConfig(new SkinCustomColourLookup(colour)); case OsuSkinConfiguration osuLookup: switch (osuLookup) From 88fc53200ed880b5836d1e6c1497e33dd8bb6316 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 1 Sep 2021 15:41:52 +0900 Subject: [PATCH 1629/2442] Refactor --- .../BeatmapSet/Scores/ScoresContainer.cs | 3 +- osu.Game/Scoring/ScoreManager.cs | 36 ++++++++---- osu.Game/Screens/Ranking/ScorePanelList.cs | 56 ++++++++++--------- .../Select/Leaderboards/BeatmapLeaderboard.cs | 4 +- 4 files changed, 59 insertions(+), 40 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index a5a373467e..fb1769fbe1 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -14,7 +14,6 @@ using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Framework.Bindables; -using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets; using osu.Game.Scoring; @@ -67,7 +66,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores if (value?.Scores.Any() != true) return; - scoreManager.GetOrderedScoresAsync(value.Scores.Select(s => s.CreateScoreInfo(rulesets)).ToArray(), loadCancellationSource.Token) + scoreManager.OrderByTotalScoreAsync(value.Scores.Select(s => s.CreateScoreInfo(rulesets)).ToArray(), loadCancellationSource.Token) .ContinueWith(ordered => Schedule(() => { if (loadCancellationSource.IsCancellationRequested) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 71edc65fcc..6fccf80b6c 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -34,7 +34,6 @@ namespace osu.Game.Scoring protected override string ImportFromStablePath => Path.Combine("Data", "r"); - private readonly Bindable scoringMode = new Bindable(); private readonly RulesetStore rulesets; private readonly Func beatmaps; @@ -52,8 +51,6 @@ namespace osu.Game.Scoring this.beatmaps = beatmaps; this.difficulties = difficulties; this.configManager = configManager; - - configManager?.BindWith(OsuSetting.ScoreDisplayMode, scoringMode); } protected override ScoreInfo CreateModel(ArchiveReader archive) @@ -106,14 +103,20 @@ namespace osu.Game.Scoring => base.CheckLocalAvailability(model, items) || (model.OnlineScoreID != null && items.Any(i => i.OnlineScoreID == model.OnlineScoreID)); - public async Task GetOrderedScoresAsync(ScoreInfo[] scores, CancellationToken cancellationToken = default) + /// + /// Orders an array of s by total score. + /// + /// The array of s to reorder. + /// A to cancel the process. + /// The given ordered by decreasing total score. + public async Task OrderByTotalScoreAsync(ScoreInfo[] scores, CancellationToken cancellationToken = default) { var difficultyCache = difficulties?.Invoke(); if (difficultyCache == null) return orderByTotalScore(scores); - // Compute difficulties asynchronously first to prevent blocks on the main thread. + // Compute difficulties asynchronously first to prevent blocking via the GetTotalScore() call below. foreach (var s in scores) { await difficultyCache.GetDifficultyAsync(s.Beatmap, s.Ruleset, s.Mods, cancellationToken).ConfigureAwait(false); @@ -122,7 +125,7 @@ namespace osu.Game.Scoring return orderByTotalScore(scores); - ScoreInfo[] orderByTotalScore(IEnumerable incoming) => incoming.OrderByDescending(GetTotalScore).ThenBy(s => s.OnlineScoreID).ToArray(); + ScoreInfo[] orderByTotalScore(IEnumerable incoming) => incoming.OrderByDescending(s => GetTotalScore(s)).ThenBy(s => s.OnlineScoreID).ToArray(); } /// @@ -150,9 +153,22 @@ namespace osu.Game.Scoring /// The bindable containing the formatted total score string. public Bindable GetBindableTotalScoreString(ScoreInfo score) => new TotalScoreStringBindable(GetBindableTotalScore(score)); - public long GetTotalScore(ScoreInfo score) => GetTotalScoreAsync(score).Result; + /// + /// Retrieves the total score of a in the given . + /// + /// The to calculate the total score of. + /// The to return the total score as. + /// The total score. + public long GetTotalScore(ScoreInfo score, ScoringMode mode = ScoringMode.Standardised) => GetTotalScoreAsync(score).Result; - public async Task GetTotalScoreAsync(ScoreInfo score, CancellationToken cancellationToken = default) + /// + /// Retrieves the total score of a in the given . + /// + /// The to calculate the total score of. + /// The to return the total score as. + /// A to cancel the process. + /// The total score. + public async Task GetTotalScoreAsync(ScoreInfo score, ScoringMode mode = ScoringMode.Standardised, CancellationToken cancellationToken = default) { if (score.Beatmap == null) return score.TotalScore; @@ -204,7 +220,7 @@ namespace osu.Game.Scoring var scoreProcessor = ruleset.CreateScoreProcessor(); scoreProcessor.Mods.Value = score.Mods; - return (long)Math.Round(scoreProcessor.GetScore(scoringMode.Value, beatmapMaxCombo, accuracy, (double)score.MaxCombo / beatmapMaxCombo, score.Statistics)); + return (long)Math.Round(scoreProcessor.GetScore(mode, beatmapMaxCombo, accuracy, (double)score.MaxCombo / beatmapMaxCombo, score.Statistics)); } /// @@ -221,7 +237,7 @@ namespace osu.Game.Scoring /// The . public TotalScoreBindable(ScoreInfo score, ScoreManager scoreManager) { - ScoringMode.BindValueChanged(_ => Value = scoreManager.GetTotalScore(score), true); + ScoringMode.BindValueChanged(mode => Value = scoreManager.GetTotalScore(score, mode.NewValue), true); } } diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index e319e824a1..b0c06ebe6a 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -11,6 +11,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; +using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; using osu.Game.Scoring; using osuTK; @@ -65,6 +66,9 @@ namespace osu.Game.Screens.Ranking [Resolved] private ScoreManager scoreManager { get; set; } + [Resolved] + private BeatmapDifficultyCache difficultyCache { get; set; } + private readonly CancellationTokenSource loadCancellationSource = new CancellationTokenSource(); private readonly Flow flow; private readonly Scroll scroll; @@ -120,33 +124,33 @@ namespace osu.Game.Screens.Ranking }; }); - scoreManager.GetOrderedScoresAsync(new[] { score }) - .ContinueWith(_ => Schedule(() => - { - flow.Add(panel.CreateTrackingContainer().With(d => - { - d.Anchor = Anchor.Centre; - d.Origin = Anchor.Centre; - })); + difficultyCache.GetDifficultyAsync(score.Beatmap, score.Ruleset, score.Mods) + .ContinueWith(_ => Schedule(() => + { + flow.Add(panel.CreateTrackingContainer().With(d => + { + d.Anchor = Anchor.Centre; + d.Origin = Anchor.Centre; + })); - if (SelectedScore.Value == score) - { - SelectedScore.TriggerChange(); - } - else - { - // We want the scroll position to remain relative to the expanded panel. When a new panel is added after the expanded panel, nothing needs to be done. - // But when a panel is added before the expanded panel, we need to offset the scroll position by the width of the new panel. - if (expandedPanel != null && flow.GetPanelIndex(score) < flow.GetPanelIndex(expandedPanel.Score)) - { - // A somewhat hacky property is used here because we need to: - // 1) Scroll after the scroll container's visible range is updated. - // 2) Scroll before the scroll container's scroll position is updated. - // Without this, we would have a 1-frame positioning error which looks very jarring. - scroll.InstantScrollTarget = (scroll.InstantScrollTarget ?? scroll.Target) + ScorePanel.CONTRACTED_WIDTH + panel_spacing; - } - } - })); + if (SelectedScore.Value == score) + { + SelectedScore.TriggerChange(); + } + else + { + // We want the scroll position to remain relative to the expanded panel. When a new panel is added after the expanded panel, nothing needs to be done. + // But when a panel is added before the expanded panel, we need to offset the scroll position by the width of the new panel. + if (expandedPanel != null && flow.GetPanelIndex(score) < flow.GetPanelIndex(expandedPanel.Score)) + { + // A somewhat hacky property is used here because we need to: + // 1) Scroll after the scroll container's visible range is updated. + // 2) Scroll before the scroll container's scroll position is updated. + // Without this, we would have a 1-frame positioning error which looks very jarring. + scroll.InstantScrollTarget = (scroll.InstantScrollTarget ?? scroll.Target) + ScorePanel.CONTRACTED_WIDTH + panel_spacing; + } + } + })); return panel; } diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 54ec42127d..7820264505 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -156,7 +156,7 @@ namespace osu.Game.Screens.Select.Leaderboards scores = scores.Where(s => s.Mods.Any(m => selectedMods.Contains(m.Acronym))); } - scoreManager.GetOrderedScoresAsync(scores.ToArray(), loadCancellationSource.Token) + scoreManager.OrderByTotalScoreAsync(scores.ToArray(), loadCancellationSource.Token) .ContinueWith(ordered => scoresCallback?.Invoke(ordered.Result), TaskContinuationOptions.OnlyOnRanToCompletion); return null; @@ -192,7 +192,7 @@ namespace osu.Game.Screens.Select.Leaderboards req.Success += r => { - scoreManager.GetOrderedScoresAsync(r.Scores.Select(s => s.CreateScoreInfo(rulesets)).ToArray(), loadCancellationSource.Token) + scoreManager.OrderByTotalScoreAsync(r.Scores.Select(s => s.CreateScoreInfo(rulesets)).ToArray(), loadCancellationSource.Token) .ContinueWith(ordered => Schedule(() => { if (loadCancellationSource.IsCancellationRequested) From 0319177c5c10b8eff6f71f202005a676731b3c3b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 1 Sep 2021 16:46:19 +0900 Subject: [PATCH 1630/2442] Fix pixels poking out of the top edge of editor setup screen --- osu.Game/Screens/Edit/Setup/SetupScreen.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Setup/SetupScreen.cs b/osu.Game/Screens/Edit/Setup/SetupScreen.cs index 72bf3ad67e..746cf38867 100644 --- a/osu.Game/Screens/Edit/Setup/SetupScreen.cs +++ b/osu.Game/Screens/Edit/Setup/SetupScreen.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.Edit.Setup { AddRange(new Drawable[] { - sections = new SectionsContainer + sections = new SetupScreenSectionsContainer { FixedHeader = header, RelativeSizeAxes = Axes.Both, @@ -40,5 +40,19 @@ namespace osu.Game.Screens.Edit.Setup }, }); } + + private class SetupScreenSectionsContainer : SectionsContainer + { + protected override UserTrackingScrollContainer CreateScrollContainer() + { + var scrollContainer = base.CreateScrollContainer(); + + // Workaround for masking issues (see https://github.com/ppy/osu-framework/issues/1675#issuecomment-910023157) + // Note that this actually causes the full scroll range to be reduced by 2px at the bottom, but it's not really noticeable. + scrollContainer.Margin = new MarginPadding { Top = 2 }; + + return scrollContainer; + } + } } } From 2251bf3bcbdb82f706ba81624faf6a943b60324a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 1 Sep 2021 17:08:20 +0900 Subject: [PATCH 1631/2442] Use lambda spec for method --- .../Profile/Sections/Historical/UserHistoryGraph.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs b/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs index 738edb9310..61f77cd6ff 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs @@ -28,12 +28,10 @@ namespace osu.Game.Overlays.Profile.Sections.Historical protected override float GetDataPointHeight(long playCount) => playCount; - protected override UserGraphTooltipContent GetTooltipContent(DateTime date, long playCount) - { - return new UserGraphTooltipContent( + protected override UserGraphTooltipContent GetTooltipContent(DateTime date, long playCount) => + new UserGraphTooltipContent( tooltipCounterName, playCount.ToLocalisableString("N0"), date.ToLocalisableString("MMMM yyyy")); - } } } From fb5f3fb9af60aa071992513eb6228a81a6a1bf1c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 1 Sep 2021 17:19:38 +0900 Subject: [PATCH 1632/2442] Rename button to be more descriptive of its purpose --- ...ayerModButton.cs => IncompatibilityDisplayingModButton.cs} | 4 ++-- osu.Game/Overlays/Mods/LocalPlayerModSelectOverlay.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename osu.Game/Overlays/Mods/{LocalPlayerModButton.cs => IncompatibilityDisplayingModButton.cs} (96%) diff --git a/osu.Game/Overlays/Mods/LocalPlayerModButton.cs b/osu.Game/Overlays/Mods/IncompatibilityDisplayingModButton.cs similarity index 96% rename from osu.Game/Overlays/Mods/LocalPlayerModButton.cs rename to osu.Game/Overlays/Mods/IncompatibilityDisplayingModButton.cs index d26bbf344d..e232081dde 100644 --- a/osu.Game/Overlays/Mods/LocalPlayerModButton.cs +++ b/osu.Game/Overlays/Mods/IncompatibilityDisplayingModButton.cs @@ -18,14 +18,14 @@ using osuTK; namespace osu.Game.Overlays.Mods { - public class LocalPlayerModButton : ModButton + public class IncompatibilityDisplayingModButton : ModButton { private readonly CompositeDrawable incompatibleIcon; [Resolved] private Bindable> selectedMods { get; set; } - public LocalPlayerModButton(Mod mod) + public IncompatibilityDisplayingModButton(Mod mod) : base(mod) { ButtonContent.Add(incompatibleIcon = new IncompatibleIcon diff --git a/osu.Game/Overlays/Mods/LocalPlayerModSelectOverlay.cs b/osu.Game/Overlays/Mods/LocalPlayerModSelectOverlay.cs index b8e0c27007..c872f2c79d 100644 --- a/osu.Game/Overlays/Mods/LocalPlayerModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/LocalPlayerModSelectOverlay.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Mods { } - protected override ModButton CreateModButton(Mod mod) => new LocalPlayerModButton(mod); + protected override ModButton CreateModButton(Mod mod) => new IncompatibilityDisplayingModButton(mod); } } } From 9e21f5a59c8d476af6f1d66334c9395ecd18dbe4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 1 Sep 2021 17:22:52 +0900 Subject: [PATCH 1633/2442] Rename `LocalPlayer` to `User` in mod select prefixes --- osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs | 2 +- .../Visual/UserInterface/TestSceneModSelectOverlay.cs | 2 +- .../Visual/UserInterface/TestSceneModSettings.cs | 2 +- .../Overlays/Mods/IncompatibilityDisplayingModButton.cs | 6 +++--- ...lPlayerModSelectOverlay.cs => UserModSelectOverlay.cs} | 8 ++++---- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 4 ---- osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 2 +- 8 files changed, 12 insertions(+), 16 deletions(-) rename osu.Game/Overlays/Mods/{LocalPlayerModSelectOverlay.cs => UserModSelectOverlay.cs} (77%) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 22338bad84..0b70703870 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -529,7 +529,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("invoke on back button", () => multiplayerScreen.OnBackButton()); - AddAssert("mod overlay is hidden", () => this.ChildrenOfType().Single().State.Value == Visibility.Hidden); + AddAssert("mod overlay is hidden", () => this.ChildrenOfType().Single().State.Value == Visibility.Hidden); AddAssert("dialog overlay is hidden", () => DialogOverlay.State.Value == Visibility.Hidden); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 32c1d262d5..9e253e089d 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -422,7 +422,7 @@ namespace osu.Game.Tests.Visual.UserInterface }; } - private class TestModSelectOverlay : LocalPlayerModSelectOverlay + private class TestModSelectOverlay : UserModSelectOverlay { public new Bindable> SelectedMods => base.SelectedMods; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index 84e2ebb6d8..da0fa5d76d 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs @@ -151,7 +151,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("wait for ready", () => modSelect.State.Value == Visibility.Visible && modSelect.ButtonsLoaded); } - private class TestModSelectOverlay : LocalPlayerModSelectOverlay + private class TestModSelectOverlay : UserModSelectOverlay { public new VisibilityContainer ModSettingsContainer => base.ModSettingsContainer; public new TriangleButton CustomiseButton => base.CustomiseButton; diff --git a/osu.Game/Overlays/Mods/IncompatibilityDisplayingModButton.cs b/osu.Game/Overlays/Mods/IncompatibilityDisplayingModButton.cs index e232081dde..c8e44ee159 100644 --- a/osu.Game/Overlays/Mods/IncompatibilityDisplayingModButton.cs +++ b/osu.Game/Overlays/Mods/IncompatibilityDisplayingModButton.cs @@ -65,9 +65,9 @@ namespace osu.Game.Overlays.Mods incompatibleIcon.Hide(); } - public override ITooltip GetCustomTooltip() => new LocalPlayerModButtonTooltip(); + public override ITooltip GetCustomTooltip() => new IncompatibilityDisplayingTooltip(); - private class LocalPlayerModButtonTooltip : ModButtonTooltip + private class IncompatibilityDisplayingTooltip : ModButtonTooltip { private readonly OsuSpriteText incompatibleText; @@ -76,7 +76,7 @@ namespace osu.Game.Overlays.Mods [Resolved] private Bindable ruleset { get; set; } - public LocalPlayerModButtonTooltip() + public IncompatibilityDisplayingTooltip() { AddRange(new Drawable[] { diff --git a/osu.Game/Overlays/Mods/LocalPlayerModSelectOverlay.cs b/osu.Game/Overlays/Mods/UserModSelectOverlay.cs similarity index 77% rename from osu.Game/Overlays/Mods/LocalPlayerModSelectOverlay.cs rename to osu.Game/Overlays/Mods/UserModSelectOverlay.cs index c872f2c79d..161f89c2eb 100644 --- a/osu.Game/Overlays/Mods/LocalPlayerModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/UserModSelectOverlay.cs @@ -5,7 +5,7 @@ using osu.Game.Rulesets.Mods; namespace osu.Game.Overlays.Mods { - public class LocalPlayerModSelectOverlay : ModSelectOverlay + public class UserModSelectOverlay : ModSelectOverlay { protected override void OnModSelected(Mod mod) { @@ -15,11 +15,11 @@ namespace osu.Game.Overlays.Mods section.DeselectTypes(mod.IncompatibleMods, true, mod); } - protected override ModSection CreateModSection(ModType type) => new LocalPlayerModSection(type); + protected override ModSection CreateModSection(ModType type) => new UserModSection(type); - private class LocalPlayerModSection : ModSection + private class UserModSection : ModSection { - public LocalPlayerModSection(ModType type) + public UserModSection(ModType type) : base(type) { } diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index c05022a903..9095b78eb4 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -429,10 +429,6 @@ namespace osu.Game.Screens.OnlinePlay.Match /// The room to change the settings of. protected abstract RoomSettingsOverlay CreateRoomSettingsOverlay(Room room); - private class UserModSelectOverlay : LocalPlayerModSelectOverlay - { - } - public class UserModSelectButton : PurpleTriangleButton { } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index 1a063fd6c6..4bc0b55433 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -152,7 +152,7 @@ namespace osu.Game.Screens.OnlinePlay return base.OnExiting(next); } - protected override ModSelectOverlay CreateModSelectOverlay() => new LocalPlayerModSelectOverlay + protected override ModSelectOverlay CreateModSelectOverlay() => new UserModSelectOverlay { IsValidMod = IsValidMod }; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index bb3df0d4e0..b3d715e580 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -315,7 +315,7 @@ namespace osu.Game.Screens.Select (new FooterButtonOptions(), BeatmapOptions) }; - protected virtual ModSelectOverlay CreateModSelectOverlay() => new LocalPlayerModSelectOverlay(); + protected virtual ModSelectOverlay CreateModSelectOverlay() => new UserModSelectOverlay(); protected virtual void ApplyFilterToCarousel(FilterCriteria criteria) { From 738ce0f6894953445cd9287ff00fbdde8a0454c4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 1 Sep 2021 19:34:57 +0900 Subject: [PATCH 1634/2442] Fix repeat arrows being hidden beneath head circles in legacy skins Aims to make minimal changes to `DrawableSlider` itself. I'm not super happy about the slider ball being moved above the head circle, but it *is* what people are used to so no one except for me is going to complain. Supersedes and closes https://github.com/ppy/osu/pull/14561. --- .../Objects/Drawables/DrawableSlider.cs | 11 ++++- .../Objects/Drawables/DrawableSliderHead.cs | 2 +- .../Objects/Drawables/DrawableSliderRepeat.cs | 2 +- .../Skinning/Legacy/LegacyMainCirclePiece.cs | 40 +++++++---------- .../Skinning/Legacy/LegacyReverseArrow.cs | 44 +++++++++++++++++++ .../Legacy/LegacySliderHeadHitCircle.cs | 30 +++++++++++++ .../Legacy/OsuLegacySkinTransformer.cs | 8 +++- 7 files changed, 110 insertions(+), 27 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs create mode 100644 osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderHeadHitCircle.cs diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 0bec33bf77..0e1d1043e3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -32,6 +32,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public SliderBall Ball { get; private set; } public SkinnableDrawable Body { get; private set; } + /// + /// A target container which can be used to add top level elements to the slider's display. + /// Intended to be used for proxy purposes only. + /// + public Container OverlayElementContainer { get; private set; } + public override bool DisplayResult => !HitObject.OnlyJudgeNestedObjects; [CanBeNull] @@ -65,6 +71,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables tailContainer = new Container { RelativeSizeAxes = Axes.Both }, tickContainer = new Container { RelativeSizeAxes = Axes.Both }, repeatContainer = new Container { RelativeSizeAxes = Axes.Both }, + headContainer = new Container { RelativeSizeAxes = Axes.Both }, + OverlayElementContainer = new Container { RelativeSizeAxes = Axes.Both, }, Ball = new SliderBall(this) { GetInitialHitAction = () => HeadCircle.HitAction, @@ -72,7 +80,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables AlwaysPresent = true, Alpha = 0 }, - headContainer = new Container { RelativeSizeAxes = Axes.Both }, slidingSample = new PausableSkinnableSound { Looping = true } }; @@ -179,6 +186,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables tailContainer.Clear(false); repeatContainer.Clear(false); tickContainer.Clear(false); + + OverlayElementContainer.Clear(); } protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs index 01c0d988ee..2b026e6840 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables [CanBeNull] public Slider Slider => DrawableSlider?.HitObject; - protected DrawableSlider DrawableSlider => (DrawableSlider)ParentHitObject; + public DrawableSlider DrawableSlider => (DrawableSlider)ParentHitObject; public override bool DisplayResult => HitObject?.JudgeAsNormalHitCircle ?? base.DisplayResult; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs index 4a2a18ffd6..673211ac6c 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables [CanBeNull] public Slider Slider => DrawableSlider?.HitObject; - protected DrawableSlider DrawableSlider => (DrawableSlider)ParentHitObject; + public DrawableSlider DrawableSlider => (DrawableSlider)ParentHitObject; private double animDuration; diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index 7a210324d7..3afd814174 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -33,9 +33,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); } - private Container circleSprites; private Drawable hitCircleSprite; - private Drawable hitCircleOverlay; + + protected Drawable HitCircleOverlay { get; private set; } private SkinnableSpriteText hitCircleText; @@ -70,28 +70,19 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy // expected behaviour in this scenario is not showing the overlay, rather than using hitcircleoverlay.png (potentially from the default/fall-through skin). Texture overlayTexture = getTextureWithFallback("overlay"); - InternalChildren = new Drawable[] + InternalChildren = new[] { - circleSprites = new Container + hitCircleSprite = new KiaiFlashingSprite { + Texture = baseTexture, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + HitCircleOverlay = new KiaiFlashingSprite + { + Texture = overlayTexture, Anchor = Anchor.Centre, Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Children = new[] - { - hitCircleSprite = new KiaiFlashingSprite - { - Texture = baseTexture, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - hitCircleOverlay = new KiaiFlashingSprite - { - Texture = overlayTexture, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - } - } }, }; @@ -111,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy bool overlayAboveNumber = skin.GetConfig(OsuSkinConfiguration.HitCircleOverlayAboveNumber)?.Value ?? true; if (overlayAboveNumber) - AddInternal(hitCircleOverlay.CreateProxy()); + ChangeInternalChildDepth(HitCircleOverlay, float.MinValue); accentColour.BindTo(drawableObject.AccentColour); indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable); @@ -153,8 +144,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy switch (state) { case ArmedState.Hit: - circleSprites.FadeOut(legacy_fade_duration, Easing.Out); - circleSprites.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); + hitCircleSprite.FadeOut(legacy_fade_duration, Easing.Out); + hitCircleSprite.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); + + HitCircleOverlay.FadeOut(legacy_fade_duration, Easing.Out); + HitCircleOverlay.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); if (hasNumber) { diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs new file mode 100644 index 0000000000..b6956693b6 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs @@ -0,0 +1,44 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Osu.Skinning.Legacy +{ + public class LegacyReverseArrow : CompositeDrawable + { + private ISkin skin { get; set; } + + [Resolved(canBeNull: true)] + private DrawableHitObject drawableHitObject { get; set; } + + public LegacyReverseArrow(ISkin skin) + { + this.skin = skin; + } + + [BackgroundDependencyLoader] + private void load() + { + AutoSizeAxes = Axes.Both; + + string lookupName = new OsuSkinComponent(OsuSkinComponents.ReverseArrow).LookupName; + + InternalChild = skin.GetAnimation(lookupName, true, true) ?? Drawable.Empty(); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + // see logic in LegacySliderHeadHitCircle. + (drawableHitObject as DrawableSliderRepeat)?.DrawableSlider + .OverlayElementContainer.Add(CreateProxy()); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderHeadHitCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderHeadHitCircle.cs new file mode 100644 index 0000000000..83ebdafa50 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderHeadHitCircle.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables; + +namespace osu.Game.Rulesets.Osu.Skinning.Legacy +{ + public class LegacySliderHeadHitCircle : LegacyMainCirclePiece + { + [Resolved(canBeNull: true)] + private DrawableHitObject drawableHitObject { get; set; } + + public LegacySliderHeadHitCircle() + : base("sliderstartcircle") + { + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + // see logic in LegacyReverseArrow. + (drawableHitObject as DrawableSliderHead)?.DrawableSlider + .OverlayElementContainer.Add(HitCircleOverlay.CreateProxy().With(d => d.Depth = float.MinValue)); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index 41b0a88f11..8df8001d42 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -67,7 +67,13 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy case OsuSkinComponents.SliderHeadHitCircle: if (hasHitCircle.Value) - return new LegacyMainCirclePiece("sliderstartcircle"); + return new LegacySliderHeadHitCircle(); + + return null; + + case OsuSkinComponents.ReverseArrow: + if (hasHitCircle.Value) + return new LegacyReverseArrow(this); return null; From ab538dc3dd4474961c2235c46d4223892e29cf60 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 1 Sep 2021 20:30:26 +0900 Subject: [PATCH 1635/2442] Fix param not passed through --- osu.Game/Scoring/ScoreManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 6fccf80b6c..9b94e34f75 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -159,7 +159,7 @@ namespace osu.Game.Scoring /// The to calculate the total score of. /// The to return the total score as. /// The total score. - public long GetTotalScore(ScoreInfo score, ScoringMode mode = ScoringMode.Standardised) => GetTotalScoreAsync(score).Result; + public long GetTotalScore(ScoreInfo score, ScoringMode mode = ScoringMode.Standardised) => GetTotalScoreAsync(score, mode).Result; /// /// Retrieves the total score of a in the given . From f7c1177cc9ec585583a549e81e834aa581819e64 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 1 Sep 2021 20:35:06 +0900 Subject: [PATCH 1636/2442] Fix ScorePanelList nullref when scores are added too soon --- .../Visual/Ranking/TestSceneScorePanelList.cs | 16 ++++ osu.Game/Screens/Ranking/ScorePanelList.cs | 95 ++++++++++--------- 2 files changed, 65 insertions(+), 46 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs index e65dcb19b1..f330e99d55 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs @@ -182,6 +182,22 @@ namespace osu.Game.Tests.Visual.Ranking assertExpandedPanelCentred(); } + [Test] + public void TestAddScoreImmediately() + { + var score = new TestScoreInfo(new OsuRuleset().RulesetInfo); + + createListStep(() => + { + var newList = new ScorePanelList { SelectedScore = { Value = score } }; + newList.AddScore(score); + return newList; + }); + + assertScoreState(score, true); + assertExpandedPanelCentred(); + } + private void createListStep(Func creationFunc) { AddStep("create list", () => Child = list = creationFunc().With(d => diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index b0c06ebe6a..711e0d60ec 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -100,6 +101,9 @@ namespace osu.Game.Screens.Ranking { base.LoadComplete(); + foreach (var d in flow) + displayScore(d); + SelectedScore.BindValueChanged(selectedScoreChanged, true); } @@ -124,37 +128,56 @@ namespace osu.Game.Screens.Ranking }; }); - difficultyCache.GetDifficultyAsync(score.Beatmap, score.Ruleset, score.Mods) - .ContinueWith(_ => Schedule(() => - { - flow.Add(panel.CreateTrackingContainer().With(d => - { - d.Anchor = Anchor.Centre; - d.Origin = Anchor.Centre; - })); + var trackingContainer = panel.CreateTrackingContainer().With(d => + { + d.Anchor = Anchor.Centre; + d.Origin = Anchor.Centre; + d.Hide(); + }); - if (SelectedScore.Value == score) - { - SelectedScore.TriggerChange(); - } - else - { - // We want the scroll position to remain relative to the expanded panel. When a new panel is added after the expanded panel, nothing needs to be done. - // But when a panel is added before the expanded panel, we need to offset the scroll position by the width of the new panel. - if (expandedPanel != null && flow.GetPanelIndex(score) < flow.GetPanelIndex(expandedPanel.Score)) - { - // A somewhat hacky property is used here because we need to: - // 1) Scroll after the scroll container's visible range is updated. - // 2) Scroll before the scroll container's scroll position is updated. - // Without this, we would have a 1-frame positioning error which looks very jarring. - scroll.InstantScrollTarget = (scroll.InstantScrollTarget ?? scroll.Target) + ScorePanel.CONTRACTED_WIDTH + panel_spacing; - } - } - })); + flow.Add(trackingContainer); + + if (IsLoaded) + displayScore(trackingContainer); return panel; } + private void displayScore(ScorePanelTrackingContainer trackingContainer) + { + if (!IsLoaded) + return; + + var score = trackingContainer.Panel.Score; + + // Calculating score can take a while in extreme scenarios, so only display scores after the process completes. + scoreManager.GetTotalScoreAsync(score) + .ContinueWith(totalScore => Schedule(() => + { + flow.SetLayoutPosition(trackingContainer, totalScore.Result); + + trackingContainer.Show(); + + if (SelectedScore.Value == score) + { + SelectedScore.TriggerChange(); + } + else + { + // We want the scroll position to remain relative to the expanded panel. When a new panel is added after the expanded panel, nothing needs to be done. + // But when a panel is added before the expanded panel, we need to offset the scroll position by the width of the new panel. + if (expandedPanel != null && flow.GetPanelIndex(score) < flow.GetPanelIndex(expandedPanel.Score)) + { + // A somewhat hacky property is used here because we need to: + // 1) Scroll after the scroll container's visible range is updated. + // 2) Scroll before the scroll container's scroll position is updated. + // Without this, we would have a 1-frame positioning error which looks very jarring. + scroll.InstantScrollTarget = (scroll.InstantScrollTarget ?? scroll.Target) + ScorePanel.CONTRACTED_WIDTH + panel_spacing; + } + } + }), TaskContinuationOptions.OnlyOnRanToCompletion); + } + /// /// Brings a to the centre of the screen and expands it. /// @@ -306,28 +329,8 @@ namespace osu.Game.Screens.Ranking { public override IEnumerable FlowingChildren => applySorting(AliveInternalChildren); - [Resolved] - private ScoreManager scoreManager { get; set; } - - protected override void LoadComplete() - { - base.LoadComplete(); - - foreach (var c in Children) - SetLayoutPosition(c, scoreManager.GetTotalScore(c.Panel.Score)); - } - public int GetPanelIndex(ScoreInfo score) => applySorting(Children).TakeWhile(s => s.Panel.Score != score).Count(); - public override void Add(ScorePanelTrackingContainer drawable) - { - Debug.Assert(drawable != null); - - base.Add(drawable); - - SetLayoutPosition(drawable, scoreManager?.GetTotalScore(drawable.Panel.Score) ?? 0); - } - private IEnumerable applySorting(IEnumerable drawables) => drawables.OfType() .OrderByDescending(GetLayoutPosition) .ThenBy(s => s.Panel.Score.OnlineScoreID); From df7480e68cc4d8d3f3fcec7a70e2b4c4db0b4854 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 1 Sep 2021 20:56:23 +0900 Subject: [PATCH 1637/2442] Fix bindable implementation being synchronous --- .../SongSelect/TestSceneBeatmapLeaderboard.cs | 2 +- .../TestSceneDeleteLocalScore.cs | 2 +- osu.Game/Scoring/ScoreManager.cs | 49 +++++++++++++++---- 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index 184a2e59da..29815ce9ff 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual.SongSelect dependencies.Cache(rulesetStore = new RulesetStore(ContextFactory)); dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get(), Resources, dependencies.Get(), Beatmap.Default)); - dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, null, ContextFactory)); + dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, null, ContextFactory, Scheduler)); return dependencies; } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index 3f9e0048dd..98482601ee 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -82,7 +82,7 @@ namespace osu.Game.Tests.Visual.UserInterface dependencies.Cache(rulesetStore = new RulesetStore(ContextFactory)); dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get(), Resources, dependencies.Get(), Beatmap.Default)); - dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, null, ContextFactory)); + dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, null, ContextFactory, Scheduler)); beatmap = beatmapManager.Import(new ImportTask(TestResources.GetQuickTestBeatmapForImport())).Result.Beatmaps[0]; diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 9b94e34f75..2cda8959f4 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -13,6 +13,7 @@ using Microsoft.EntityFrameworkCore; using osu.Framework.Bindables; using osu.Framework.Logging; using osu.Framework.Platform; +using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Database; @@ -36,6 +37,7 @@ namespace osu.Game.Scoring private readonly RulesetStore rulesets; private readonly Func beatmaps; + private readonly Scheduler scheduler; [CanBeNull] private readonly Func difficulties; @@ -43,12 +45,13 @@ namespace osu.Game.Scoring [CanBeNull] private readonly OsuConfigManager configManager; - public ScoreManager(RulesetStore rulesets, Func beatmaps, Storage storage, IAPIProvider api, IDatabaseContextFactory contextFactory, IIpcHost importHost = null, - Func difficulties = null, OsuConfigManager configManager = null) + public ScoreManager(RulesetStore rulesets, Func beatmaps, Storage storage, IAPIProvider api, IDatabaseContextFactory contextFactory, Scheduler scheduler, + IIpcHost importHost = null, Func difficulties = null, OsuConfigManager configManager = null) : base(storage, contextFactory, api, new ScoreStore(contextFactory, storage), importHost) { this.rulesets = rulesets; this.beatmaps = beatmaps; + this.scheduler = scheduler; this.difficulties = difficulties; this.configManager = configManager; } @@ -125,7 +128,13 @@ namespace osu.Game.Scoring return orderByTotalScore(scores); - ScoreInfo[] orderByTotalScore(IEnumerable incoming) => incoming.OrderByDescending(s => GetTotalScore(s)).ThenBy(s => s.OnlineScoreID).ToArray(); + ScoreInfo[] orderByTotalScore(IEnumerable incoming) + { + // We're calling .Result, but this should not be a blocking call due to the above GetDifficultyAsync() calls. + return incoming.OrderByDescending(s => GetTotalScoreAsync(s, cancellationToken: cancellationToken).Result) + .ThenBy(s => s.OnlineScoreID) + .ToArray(); + } } /// @@ -136,7 +145,7 @@ namespace osu.Game.Scoring /// /// The to retrieve the bindable for. /// The bindable containing the total score. - public Bindable GetBindableTotalScore(ScoreInfo score) + public Bindable GetBindableTotalScore([NotNull] ScoreInfo score) { var bindable = new TotalScoreBindable(score, this); configManager?.BindWith(OsuSetting.ScoreDisplayMode, bindable.ScoringMode); @@ -151,15 +160,21 @@ namespace osu.Game.Scoring /// /// The to retrieve the bindable for. /// The bindable containing the formatted total score string. - public Bindable GetBindableTotalScoreString(ScoreInfo score) => new TotalScoreStringBindable(GetBindableTotalScore(score)); + public Bindable GetBindableTotalScoreString([NotNull] ScoreInfo score) => new TotalScoreStringBindable(GetBindableTotalScore(score)); /// /// Retrieves the total score of a in the given . + /// The score is returned in a callback that is run on the update thread. /// /// The to calculate the total score of. + /// The callback to be invoked with the total score. /// The to return the total score as. - /// The total score. - public long GetTotalScore(ScoreInfo score, ScoringMode mode = ScoringMode.Standardised) => GetTotalScoreAsync(score, mode).Result; + /// A to cancel the process. + public void GetTotalScore([NotNull] ScoreInfo score, [NotNull] Action callback, ScoringMode mode = ScoringMode.Standardised, CancellationToken cancellationToken = default) + { + GetTotalScoreAsync(score, mode, cancellationToken) + .ContinueWith(s => scheduler.Add(() => callback(s.Result)), TaskContinuationOptions.OnlyOnRanToCompletion); + } /// /// Retrieves the total score of a in the given . @@ -168,7 +183,7 @@ namespace osu.Game.Scoring /// The to return the total score as. /// A to cancel the process. /// The total score. - public async Task GetTotalScoreAsync(ScoreInfo score, ScoringMode mode = ScoringMode.Standardised, CancellationToken cancellationToken = default) + public async Task GetTotalScoreAsync([NotNull] ScoreInfo score, ScoringMode mode = ScoringMode.Standardised, CancellationToken cancellationToken = default) { if (score.Beatmap == null) return score.TotalScore; @@ -230,6 +245,11 @@ namespace osu.Game.Scoring { public readonly Bindable ScoringMode = new Bindable(); + private readonly ScoreInfo score; + private readonly ScoreManager scoreManager; + + private CancellationTokenSource difficultyCalculationCancellationSource; + /// /// Creates a new . /// @@ -237,7 +257,18 @@ namespace osu.Game.Scoring /// The . public TotalScoreBindable(ScoreInfo score, ScoreManager scoreManager) { - ScoringMode.BindValueChanged(mode => Value = scoreManager.GetTotalScore(score, mode.NewValue), true); + this.score = score; + this.scoreManager = scoreManager; + + ScoringMode.BindValueChanged(onScoringModeChanged, true); + } + + private void onScoringModeChanged(ValueChangedEvent mode) + { + difficultyCalculationCancellationSource?.Cancel(); + difficultyCalculationCancellationSource = new CancellationTokenSource(); + + scoreManager.GetTotalScore(score, s => Value = s, mode.NewValue, difficultyCalculationCancellationSource.Token); } } From 492209fe13b80b94e7aa6602489ac0c00fa067a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 17:01:49 +0000 Subject: [PATCH 1638/2442] Bump Sentry from 3.8.3 to 3.9.0 Bumps [Sentry](https://github.com/getsentry/sentry-dotnet) from 3.8.3 to 3.9.0. - [Release notes](https://github.com/getsentry/sentry-dotnet/releases) - [Changelog](https://github.com/getsentry/sentry-dotnet/blob/main/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-dotnet/compare/3.8.3...3.9.0) --- updated-dependencies: - dependency-name: Sentry dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- 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 b4d4aa3070..75ba85e3ef 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -38,7 +38,7 @@ - + From 6f0d1b394d6d4699404c27b152e711b923d342fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 17:01:53 +0000 Subject: [PATCH 1639/2442] Bump Microsoft.AspNetCore.SignalR.Protocols.MessagePack Bumps [Microsoft.AspNetCore.SignalR.Protocols.MessagePack](https://github.com/dotnet/aspnetcore) from 5.0.8 to 5.0.9. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Commits](https://github.com/dotnet/aspnetcore/compare/v5.0.8...v5.0.9) --- updated-dependencies: - dependency-name: Microsoft.AspNetCore.SignalR.Protocols.MessagePack dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- 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 b4d4aa3070..bd42923413 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + From c5597b7d9ced6af9d81f5b2ad9b08809a6514b17 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 17:02:03 +0000 Subject: [PATCH 1640/2442] Bump BenchmarkDotNet from 0.13.0 to 0.13.1 Bumps [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) from 0.13.0 to 0.13.1. - [Release notes](https://github.com/dotnet/BenchmarkDotNet/releases) - [Commits](https://github.com/dotnet/BenchmarkDotNet/compare/v0.13.0...v0.13.1) --- updated-dependencies: - dependency-name: BenchmarkDotNet dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- osu.Game.Benchmarks/osu.Game.Benchmarks.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj index da8a0540f4..03f39f226c 100644 --- a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj +++ b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj @@ -7,7 +7,7 @@ - + From 860f04af0031d6fcd455489dfcd864fbdc3a62a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 17:02:11 +0000 Subject: [PATCH 1641/2442] Bump ppy.osu.Framework.NativeLibs from 2021.115.0 to 2021.805.0 Bumps [ppy.osu.Framework.NativeLibs](https://github.com/ppy/osu-framework) from 2021.115.0 to 2021.805.0. - [Release notes](https://github.com/ppy/osu-framework/releases) - [Commits](https://github.com/ppy/osu-framework/compare/2021.115.0...2021.805.0) --- updated-dependencies: - dependency-name: ppy.osu.Framework.NativeLibs dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- osu.iOS.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.iOS.props b/osu.iOS.props index 29e9b9fe20..1714bae53c 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -98,7 +98,7 @@ - + From 5a1eccd8e3a5fb8f5241abda8753e1a432d58649 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 18:17:37 +0000 Subject: [PATCH 1642/2442] Bump Microsoft.NET.Test.Sdk from 16.10.0 to 16.11.0 Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 16.10.0 to 16.11.0. - [Release notes](https://github.com/microsoft/vstest/releases) - [Commits](https://github.com/microsoft/vstest/compare/v16.10.0...v16.11.0) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .../osu.Game.Rulesets.EmptyFreeform.Tests.csproj | 2 +- .../osu.Game.Rulesets.Pippidon.Tests.csproj | 2 +- .../osu.Game.Rulesets.EmptyScrolling.Tests.csproj | 2 +- .../osu.Game.Rulesets.Pippidon.Tests.csproj | 2 +- .../osu.Game.Rulesets.Catch.Tests.csproj | 2 +- .../osu.Game.Rulesets.Mania.Tests.csproj | 2 +- osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj | 2 +- .../osu.Game.Rulesets.Taiko.Tests.csproj | 2 +- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj index 3dd6be7307..e28053d0ca 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj index 0c4bfe0ed7..027bd0b7e2 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj index bb0a487274..e2c715d385 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj index 0c4bfe0ed7..027bd0b7e2 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj index 484da8e22e..6457ec92da 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj index 6df555617b..674a22df98 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index 68be34d153..f5f1159542 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj index 532fdc5cb0..b9b295767e 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 161e248d96..696f930467 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -3,7 +3,7 @@ - + diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj index ba096abd36..2673c9ec9f 100644 --- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj +++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj @@ -5,7 +5,7 @@ - + From f14d66aafce778d86e2cc203afd0828d4c60293c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Sep 2021 10:35:00 +0900 Subject: [PATCH 1643/2442] Commit missed line --- 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 69f6bc1b7b..16e3cdbfda 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -262,7 +262,7 @@ namespace osu.Game dependencies.Cache(fileStore = new FileStore(contextFactory, Storage)); // ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup() - dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Host, () => difficultyCache, LocalConfig)); + dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Scheduler, Host, () => difficultyCache, LocalConfig)); dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Resources, Host, defaultBeatmap, true)); // this should likely be moved to ArchiveModelManager when another case appears where it is necessary From 8ba00f673764d916882665fd9461a9b43bde4a7a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Sep 2021 02:33:56 +0000 Subject: [PATCH 1644/2442] Bump HtmlAgilityPack from 1.11.34 to 1.11.36 Bumps [HtmlAgilityPack](https://github.com/zzzprojects/html-agility-pack) from 1.11.34 to 1.11.36. - [Release notes](https://github.com/zzzprojects/html-agility-pack/releases) - [Commits](https://github.com/zzzprojects/html-agility-pack/compare/v1.11.34...v1.11.36) --- updated-dependencies: - dependency-name: HtmlAgilityPack dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- 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 3ed8e31c85..36fa178189 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -20,7 +20,7 @@ - + From be379051f2684d033fd0e30416ecb1cdad225dc0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Sep 2021 02:34:19 +0000 Subject: [PATCH 1645/2442] Bump Microsoft.AspNetCore.SignalR.Client from 5.0.8 to 5.0.9 Bumps [Microsoft.AspNetCore.SignalR.Client](https://github.com/dotnet/aspnetcore) from 5.0.8 to 5.0.9. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Commits](https://github.com/dotnet/aspnetcore/compare/v5.0.8...v5.0.9) --- updated-dependencies: - dependency-name: Microsoft.AspNetCore.SignalR.Client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- 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 3ed8e31c85..710aad7336 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + From e176babb258578c76e0cd3573fe3c337de9c779a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Sep 2021 02:34:24 +0000 Subject: [PATCH 1646/2442] Bump Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson Bumps [Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson](https://github.com/dotnet/aspnetcore) from 5.0.8 to 5.0.9. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Commits](https://github.com/dotnet/aspnetcore/compare/v5.0.8...v5.0.9) --- updated-dependencies: - dependency-name: Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- 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 3ed8e31c85..1f2b78f5ba 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -25,7 +25,7 @@ - + From 31433c4b894c80063d78865e9e81658653d6b013 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Sep 2021 16:26:17 +0900 Subject: [PATCH 1647/2442] Apply @spaceman_atlas' quadratic factor --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 2a7691269d..e09225f967 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -227,7 +227,8 @@ namespace osu.Game.Rulesets.Scoring case ScoringMode.Classic: // This gives a similar feeling to osu!stable scoring (ScoreV1) while keeping classic scoring as only a constant multiple of standardised scoring. // The invariant is important to ensure that scores don't get re-ordered on leaderboards between the two scoring modes. - return GetScore(ScoringMode.Standardised, maxCombo, accuracyRatio, comboRatio, statistics) / max_score * Math.Pow(maxCombo, 2) * 25; + var scaledStandardised = GetScore(ScoringMode.Standardised, maxCombo, accuracyRatio, comboRatio, statistics) / max_score; + return Math.Pow(scaledStandardised * maxCombo, 2) * 18; } } From b907c2f4f6bb37c22428520c238b7d6861ec5fde Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 2 Sep 2021 16:31:43 +0900 Subject: [PATCH 1648/2442] Fix osu! judgements getting scaled twice over different durations --- .../UI/DrawableManiaJudgement.cs | 5 +++-- .../Objects/Drawables/DrawableOsuJudgement.cs | 10 ++++++--- .../UI/DrawableTaikoJudgement.cs | 22 +++++++++++++++++++ .../Judgements/DefaultJudgementPiece.cs | 13 ++++++----- 4 files changed, 39 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs index 34d972e60f..8581f016b1 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs @@ -37,12 +37,11 @@ namespace osu.Game.Rulesets.Mania.UI public override void PlayAnimation() { - base.PlayAnimation(); - switch (Result) { case HitResult.None: case HitResult.Miss: + base.PlayAnimation(); break; default: @@ -52,6 +51,8 @@ namespace osu.Game.Rulesets.Mania.UI this.Delay(50) .ScaleTo(0.75f, 250) .FadeOut(200); + + // osu!mania uses a custom fade length, so the base call is intentionally omitted. break; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs index b23087a1f3..e4df41a4fe 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs @@ -74,10 +74,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public override void PlayAnimation() { - base.PlayAnimation(); - if (Result != HitResult.Miss) - JudgementText.ScaleTo(new Vector2(0.8f, 1)).Then().ScaleTo(new Vector2(1.2f, 1), 1800, Easing.OutQuint); + { + JudgementText + .ScaleTo(new Vector2(0.8f, 1)) + .ScaleTo(new Vector2(1.2f, 1), 1800, Easing.OutQuint); + } + + base.PlayAnimation(); } } } diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs index 1ad1e4495c..876fa207bf 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Taiko.UI { @@ -16,5 +17,26 @@ namespace osu.Game.Rulesets.Taiko.UI this.MoveToY(-100, 500); base.ApplyHitAnimations(); } + + protected override Drawable CreateDefaultJudgement(HitResult result) => new TaikoJudgementPiece(result); + + private class TaikoJudgementPiece : DefaultJudgementPiece + { + public TaikoJudgementPiece(HitResult result) + : base(result) + { + } + + public override void PlayAnimation() + { + if (Result != HitResult.Miss) + { + JudgementText.ScaleTo(0.9f); + JudgementText.ScaleTo(1, 500, Easing.OutElastic); + } + + base.PlayAnimation(); + } + } } } diff --git a/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs b/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs index 21ac017685..29b771a81d 100644 --- a/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs +++ b/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs @@ -47,6 +47,13 @@ namespace osu.Game.Rulesets.Judgements }; } + /// + /// Plays the default animation for this judgement piece. + /// + /// + /// The base implementation only handles fade (for all result types) and misses. + /// Individual rulesets are recommended to implement their appropriate hit animations. + /// public virtual void PlayAnimation() { switch (Result) @@ -60,12 +67,6 @@ namespace osu.Game.Rulesets.Judgements this.RotateTo(0); this.RotateTo(40, 800, Easing.InQuint); - - break; - - default: - this.ScaleTo(0.9f); - this.ScaleTo(1, 500, Easing.OutElastic); break; } From e2f7aaeb71bbe841e91e61f0433ed40d3886c265 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Sep 2021 17:00:13 +0900 Subject: [PATCH 1649/2442] Fix 0 score with bonus-only maps --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index e09225f967..c1234f8fb3 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -227,8 +227,8 @@ namespace osu.Game.Rulesets.Scoring case ScoringMode.Classic: // This gives a similar feeling to osu!stable scoring (ScoreV1) while keeping classic scoring as only a constant multiple of standardised scoring. // The invariant is important to ensure that scores don't get re-ordered on leaderboards between the two scoring modes. - var scaledStandardised = GetScore(ScoringMode.Standardised, maxCombo, accuracyRatio, comboRatio, statistics) / max_score; - return Math.Pow(scaledStandardised * maxCombo, 2) * 18; + double scaledStandardised = GetScore(ScoringMode.Standardised, maxCombo, accuracyRatio, comboRatio, statistics) / max_score; + return Math.Pow(scaledStandardised * (maxCombo + 1), 2) * 18; } } From e3ec7e3ddc7972b0bad19d1750ea0faffd4edc99 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Sep 2021 17:01:09 +0900 Subject: [PATCH 1650/2442] Adjust test values --- .../Rulesets/Scoring/ScoreProcessorTest.cs | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs index 184a94912a..f0d9ece06f 100644 --- a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs +++ b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs @@ -36,9 +36,9 @@ namespace osu.Game.Tests.Rulesets.Scoring [TestCase(ScoringMode.Standardised, HitResult.Meh, 750_000)] [TestCase(ScoringMode.Standardised, HitResult.Ok, 800_000)] [TestCase(ScoringMode.Standardised, HitResult.Great, 1_000_000)] - [TestCase(ScoringMode.Classic, HitResult.Meh, 50)] - [TestCase(ScoringMode.Classic, HitResult.Ok, 100)] - [TestCase(ScoringMode.Classic, HitResult.Great, 300)] + [TestCase(ScoringMode.Classic, HitResult.Meh, 41)] + [TestCase(ScoringMode.Classic, HitResult.Ok, 46)] + [TestCase(ScoringMode.Classic, HitResult.Great, 72)] public void TestSingleOsuHit(ScoringMode scoringMode, HitResult hitResult, int expectedScore) { scoreProcessor.Mode.Value = scoringMode; @@ -85,19 +85,18 @@ namespace osu.Game.Tests.Rulesets.Scoring [TestCase(ScoringMode.Standardised, HitResult.LargeTickHit, HitResult.LargeTickHit, 575_000)] // (3 * 30) / (4 * 30) * 300_000 + (0 / 4) * 700_000 [TestCase(ScoringMode.Standardised, HitResult.SmallBonus, HitResult.SmallBonus, 1_000_030)] // 1 * 300_000 + 700_000 (max combo 0) + 3 * 10 (bonus points) [TestCase(ScoringMode.Standardised, HitResult.LargeBonus, HitResult.LargeBonus, 1_000_150)] // 1 * 300_000 + 700_000 (max combo 0) + 3 * 50 (bonus points) - [TestCase(ScoringMode.Classic, HitResult.Miss, HitResult.Great, 0)] // (0 * 4 * 300) * (1 + 0 / 25) - [TestCase(ScoringMode.Classic, HitResult.Meh, HitResult.Great, 156)] // (((3 * 50) / (4 * 300)) * 4 * 300) * (1 + 1 / 25) - [TestCase(ScoringMode.Classic, HitResult.Ok, HitResult.Great, 312)] // (((3 * 100) / (4 * 300)) * 4 * 300) * (1 + 1 / 25) - [TestCase(ScoringMode.Classic, HitResult.Good, HitResult.Perfect, 594)] // (((3 * 200) / (4 * 350)) * 4 * 300) * (1 + 1 / 25) - [TestCase(ScoringMode.Classic, HitResult.Great, HitResult.Great, 936)] // (((3 * 300) / (4 * 300)) * 4 * 300) * (1 + 1 / 25) - [TestCase(ScoringMode.Classic, HitResult.Perfect, HitResult.Perfect, 936)] // (((3 * 350) / (4 * 350)) * 4 * 300) * (1 + 1 / 25) - [TestCase(ScoringMode.Classic, HitResult.SmallTickMiss, HitResult.SmallTickHit, 0)] // (0 * 1 * 300) * (1 + 0 / 25) - [TestCase(ScoringMode.Classic, HitResult.SmallTickHit, HitResult.SmallTickHit, 225)] // (((3 * 10) / (4 * 10)) * 1 * 300) * (1 + 0 / 25) - [TestCase(ScoringMode.Classic, HitResult.LargeTickMiss, HitResult.LargeTickHit, 0)] // (0 * 4 * 300) * (1 + 0 / 25) - [TestCase(ScoringMode.Classic, HitResult.LargeTickHit, HitResult.LargeTickHit, 936)] // (((3 * 50) / (4 * 50)) * 4 * 300) * (1 + 1 / 25) - // TODO: The following two cases don't match expectations currently (a single hit is registered in acc portion when it shouldn't be). See https://github.com/ppy/osu/issues/12604. - [TestCase(ScoringMode.Classic, HitResult.SmallBonus, HitResult.SmallBonus, 330)] // (1 * 1 * 300) * (1 + 0 / 25) + 3 * 10 (bonus points) - [TestCase(ScoringMode.Classic, HitResult.LargeBonus, HitResult.LargeBonus, 450)] // (1 * 1 * 300) * (1 + 0 / 25) + 3 * 50 (bonus points) + [TestCase(ScoringMode.Classic, HitResult.Miss, HitResult.Great, 0)] + [TestCase(ScoringMode.Classic, HitResult.Meh, HitResult.Great, 68)] + [TestCase(ScoringMode.Classic, HitResult.Ok, HitResult.Great, 81)] + [TestCase(ScoringMode.Classic, HitResult.Good, HitResult.Perfect, 109)] + [TestCase(ScoringMode.Classic, HitResult.Great, HitResult.Great, 149)] + [TestCase(ScoringMode.Classic, HitResult.Perfect, HitResult.Perfect, 149)] + [TestCase(ScoringMode.Classic, HitResult.SmallTickMiss, HitResult.SmallTickHit, 9)] + [TestCase(ScoringMode.Classic, HitResult.SmallTickHit, HitResult.SmallTickHit, 15)] + [TestCase(ScoringMode.Classic, HitResult.LargeTickMiss, HitResult.LargeTickHit, 0)] + [TestCase(ScoringMode.Classic, HitResult.LargeTickHit, HitResult.LargeTickHit, 149)] + [TestCase(ScoringMode.Classic, HitResult.SmallBonus, HitResult.SmallBonus, 18)] + [TestCase(ScoringMode.Classic, HitResult.LargeBonus, HitResult.LargeBonus, 18)] public void TestFourVariousResultsOneMiss(ScoringMode scoringMode, HitResult hitResult, HitResult maxResult, int expectedScore) { var minResult = new TestJudgement(hitResult).MinResult; @@ -129,8 +128,8 @@ namespace osu.Game.Tests.Rulesets.Scoring /// [TestCase(ScoringMode.Standardised, HitResult.SmallTickHit, 978_571)] // (3 * 10 + 100) / (4 * 10 + 100) * 300_000 + (1 / 1) * 700_000 [TestCase(ScoringMode.Standardised, HitResult.SmallTickMiss, 914_286)] // (3 * 0 + 100) / (4 * 10 + 100) * 300_000 + (1 / 1) * 700_000 - [TestCase(ScoringMode.Classic, HitResult.SmallTickHit, 279)] // (((3 * 10 + 100) / (4 * 10 + 100)) * 1 * 300) * (1 + 0 / 25) - [TestCase(ScoringMode.Classic, HitResult.SmallTickMiss, 214)] // (((3 * 0 + 100) / (4 * 10 + 100)) * 1 * 300) * (1 + 0 / 25) + [TestCase(ScoringMode.Classic, HitResult.SmallTickHit, 69)] // (((3 * 10 + 100) / (4 * 10 + 100)) * 1 * 300) * (1 + 0 / 25) + [TestCase(ScoringMode.Classic, HitResult.SmallTickMiss, 60)] // (((3 * 0 + 100) / (4 * 10 + 100)) * 1 * 300) * (1 + 0 / 25) public void TestSmallTicksAccuracy(ScoringMode scoringMode, HitResult hitResult, int expectedScore) { IEnumerable hitObjects = Enumerable From 711baa12bafc7bfa3186b545b421ee760dbcc653 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Thu, 2 Sep 2021 16:31:31 +0100 Subject: [PATCH 1651/2442] emu's doubletap cheese nerf --- .../Difficulty/Skills/Speed.cs | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 7cc5447888..8beef524de 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills public Speed(Mod[] mods, IBeatmap beatmap, double clockRate) : base(mods) { - greatWindow = (79 - (beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty * 6) + 0.5) / clockRate; + //greatWindow = (79 - (beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty * 6) + 0.5) / clockRate; } protected override double StrainValueOf(DifficultyHitObject current) @@ -46,20 +46,16 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double distance = Math.Min(single_spacing_threshold, osuCurrent.TravelDistance + osuCurrent.JumpDistance); double deltaTime = current.DeltaTime; + // Aim to nerf cheesy rhythms (Very fast consecutive doubles with large deltatimes between) + if (Previous.Count > 0) + { + deltaTime = Math.Max(Previous[0].DeltaTime, deltaTime); + } + double speedBonus = 1.0; if (deltaTime < min_speed_bonus) speedBonus = 1 + Math.Pow((min_speed_bonus - deltaTime) / speed_balancing_factor, 2); - // Doubletap detection - if (Previous.Count > 0) - { - var osuPrevious = (OsuDifficultyHitObject)Previous[0]; - if ( (osuPrevious.DeltaTime / osuCurrent.DeltaTime) >= 3 && osuCurrent.DeltaTime <= (2 * greatWindow)) - { - return 0; - } - } - double angleBonus = 1.0; if (osuCurrent.Angle != null && osuCurrent.Angle.Value < angle_bonus_begin) @@ -76,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } } - return (1 + (speedBonus - 1) * 0.75) * angleBonus * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / osuCurrent.StrainTime; + return (1 + (speedBonus - 1) * 0.75) * angleBonus * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / deltaTime; } } } From 3e98c71ece670f63e7522a6deaf5740681d548d5 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Thu, 2 Sep 2021 16:48:34 +0100 Subject: [PATCH 1652/2442] cap deltatime to hitwindow sorta --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 8beef524de..3ce0d050d7 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills public Speed(Mod[] mods, IBeatmap beatmap, double clockRate) : base(mods) { - //greatWindow = (79 - (beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty * 6) + 0.5) / clockRate; + greatWindow = (79 - (beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty * 6) + 0.5) / clockRate; } protected override double StrainValueOf(DifficultyHitObject current) @@ -52,6 +52,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills deltaTime = Math.Max(Previous[0].DeltaTime, deltaTime); } + // Cap deltatime to the OD 300 hitwindow. + // 0.77 is derived from making sure 260bpm OD8 streams aren't nerfed + var hitWindowNerfRaw = deltaTime / (greatWindow * 2 * 0.77); + var hitWindowNerf = Math.Clamp(hitWindowNerfRaw, 0.85, 1); + double speedBonus = 1.0; if (deltaTime < min_speed_bonus) speedBonus = 1 + Math.Pow((min_speed_bonus - deltaTime) / speed_balancing_factor, 2); @@ -72,7 +77,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } } - return (1 + (speedBonus - 1) * 0.75) * angleBonus * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / deltaTime; + return (1 + (speedBonus - 1) * 0.75) * angleBonus * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / (deltaTime / hitWindowNerf); } } } From d9cc497801fb37f45644f058805a05f51626a59b Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Thu, 2 Sep 2021 17:02:23 +0100 Subject: [PATCH 1653/2442] refactoring --- .../Difficulty/OsuDifficultyCalculator.cs | 2 +- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index cfd74a4174..74ede287b0 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -82,7 +82,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[] { new Aim(mods), - new Speed(mods, beatmap, clockRate) + new Speed(mods, beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, clockRate), }; protected override Mod[] DifficultyAdjustmentMods => new Mod[] diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 3ce0d050d7..76803cbdf8 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -30,10 +30,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private const double speed_balancing_factor = 40; private double greatWindow; - public Speed(Mod[] mods, IBeatmap beatmap, double clockRate) + public Speed(Mod[] mods, float od, double clockRate) : base(mods) { - greatWindow = (79 - (beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty * 6) + 0.5) / clockRate; + greatWindow = (79 - (od * 6) + 0.5) / clockRate; } protected override double StrainValueOf(DifficultyHitObject current) @@ -54,8 +54,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills // Cap deltatime to the OD 300 hitwindow. // 0.77 is derived from making sure 260bpm OD8 streams aren't nerfed - var hitWindowNerfRaw = deltaTime / (greatWindow * 2 * 0.77); - var hitWindowNerf = Math.Clamp(hitWindowNerfRaw, 0.85, 1); + var hitWindowNerf = deltaTime / (greatWindow * 2 * 0.77); + deltaTime /= Math.Clamp(hitWindowNerf, 0.92, 1); double speedBonus = 1.0; if (deltaTime < min_speed_bonus) @@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } } - return (1 + (speedBonus - 1) * 0.75) * angleBonus * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / (deltaTime / hitWindowNerf); + return (1 + (speedBonus - 1) * 0.75) * angleBonus * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / deltaTime; } } } From 0d60076f3411d03937f9a1b3bcaa170f5ae30730 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Thu, 2 Sep 2021 17:14:23 +0100 Subject: [PATCH 1654/2442] fix doubletap cheese detect (base on hitwindow) --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 76803cbdf8..6cd32c798d 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double deltaTime = current.DeltaTime; // Aim to nerf cheesy rhythms (Very fast consecutive doubles with large deltatimes between) - if (Previous.Count > 0) + if (Previous.Count > 0 && deltaTime <= greatWindow * 2) { deltaTime = Math.Max(Previous[0].DeltaTime, deltaTime); } From 57a2ba9aa80e5432cfe0fcaa31618f85bf97e4ad Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Thu, 2 Sep 2021 18:29:55 +0100 Subject: [PATCH 1655/2442] remove "straintime" --- .../Difficulty/Preprocessing/OsuDifficultyHitObject.cs | 8 -------- osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs | 6 +++--- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index bc97172dbf..609ad4c995 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -32,11 +32,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing /// public double? Angle { get; private set; } - /// - /// Milliseconds elapsed since the start time of the previous , with a minimum of 50ms. - /// - public readonly double StrainTime; - private readonly OsuHitObject lastLastObject; private readonly OsuHitObject lastObject; @@ -47,9 +42,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing this.lastObject = (OsuHitObject)lastObject; setDistances(); - - // Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure - StrainTime = DeltaTime; } private void setDistances() diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index 16a18cbcb9..63daea1ade 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills Math.Max(osuPrevious.JumpDistance - scale, 0) * Math.Pow(Math.Sin(osuCurrent.Angle.Value - angle_bonus_begin), 2) * Math.Max(osuCurrent.JumpDistance - scale, 0)); - result = 1.4 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, osuPrevious.StrainTime); + result = 1.4 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, osuPrevious.DeltaTime); } } @@ -54,8 +54,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double travelDistanceExp = applyDiminishingExp(osuCurrent.TravelDistance); return Math.Max( - result + (jumpDistanceExp + travelDistanceExp + Math.Sqrt(travelDistanceExp * jumpDistanceExp)) / Math.Max(osuCurrent.StrainTime, timing_threshold), - (Math.Sqrt(travelDistanceExp * jumpDistanceExp) + jumpDistanceExp + travelDistanceExp) / osuCurrent.StrainTime + result + (jumpDistanceExp + travelDistanceExp + Math.Sqrt(travelDistanceExp * jumpDistanceExp)) / Math.Max(osuCurrent.DeltaTime, timing_threshold), + (Math.Sqrt(travelDistanceExp * jumpDistanceExp) + jumpDistanceExp + travelDistanceExp) / osuCurrent.DeltaTime ); } From ce1912781e8b7144fbf4310031e4263636b0178e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 22 Aug 2021 16:40:17 +0200 Subject: [PATCH 1656/2442] Add extension point for ruleset-specific beatmap setup sections --- osu.Game/Rulesets/Ruleset.cs | 7 +++++ .../Screens/Edit/Setup/RulesetSetupSection.cs | 20 ++++++++++++++ osu.Game/Screens/Edit/Setup/SetupScreen.cs | 27 ++++++++++++------- osu.Game/Screens/Edit/Setup/SetupSection.cs | 7 ++--- 4 files changed, 48 insertions(+), 13 deletions(-) create mode 100644 osu.Game/Screens/Edit/Setup/RulesetSetupSection.cs diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 80be61ead1..0a537f2442 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -28,6 +28,7 @@ using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Testing; using osu.Game.Extensions; using osu.Game.Rulesets.Filter; +using osu.Game.Screens.Edit.Setup; using osu.Game.Screens.Ranking.Statistics; namespace osu.Game.Rulesets @@ -315,5 +316,11 @@ namespace osu.Game.Rulesets /// [CanBeNull] public virtual IRulesetFilterCriteria CreateRulesetFilterCriteria() => null; + + /// + /// Can be overridden to add a ruleset-specific section to the editor beatmap setup screen. + /// + [CanBeNull] + public virtual RulesetSetupSection CreateEditorSetupSectionForRuleset() => null; } } diff --git a/osu.Game/Screens/Edit/Setup/RulesetSetupSection.cs b/osu.Game/Screens/Edit/Setup/RulesetSetupSection.cs new file mode 100644 index 0000000000..9344d5b491 --- /dev/null +++ b/osu.Game/Screens/Edit/Setup/RulesetSetupSection.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; +using osu.Game.Rulesets; + +namespace osu.Game.Screens.Edit.Setup +{ + public abstract class RulesetSetupSection : SetupSection + { + public sealed override LocalisableString Title => $"{rulesetInfo.Name}-specific"; + + private readonly RulesetInfo rulesetInfo; + + protected RulesetSetupSection(RulesetInfo rulesetInfo) + { + this.rulesetInfo = rulesetInfo; + } + } +} diff --git a/osu.Game/Screens/Edit/Setup/SetupScreen.cs b/osu.Game/Screens/Edit/Setup/SetupScreen.cs index 746cf38867..53ddb13d85 100644 --- a/osu.Game/Screens/Edit/Setup/SetupScreen.cs +++ b/osu.Game/Screens/Edit/Setup/SetupScreen.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics.Containers; @@ -21,22 +22,28 @@ namespace osu.Game.Screens.Edit.Setup } [BackgroundDependencyLoader] - private void load() + private void load(EditorBeatmap beatmap) { + var sectionsEnumerable = new List + { + new ResourcesSection(), + new MetadataSection(), + new DifficultySection(), + new ColoursSection(), + new DesignSection(), + }; + + var rulesetSpecificSection = beatmap.BeatmapInfo.Ruleset?.CreateInstance()?.CreateEditorSetupSectionForRuleset(); + if (rulesetSpecificSection != null) + sectionsEnumerable.Add(rulesetSpecificSection); + AddRange(new Drawable[] { sections = new SetupScreenSectionsContainer { - FixedHeader = header, RelativeSizeAxes = Axes.Both, - Children = new SetupSection[] - { - new ResourcesSection(), - new MetadataSection(), - new DifficultySection(), - new ColoursSection(), - new DesignSection(), - } + ChildrenEnumerable = sectionsEnumerable, + FixedHeader = header }, }); } diff --git a/osu.Game/Screens/Edit/Setup/SetupSection.cs b/osu.Game/Screens/Edit/Setup/SetupSection.cs index 1f988d62e2..1dde6fb926 100644 --- a/osu.Game/Screens/Edit/Setup/SetupSection.cs +++ b/osu.Game/Screens/Edit/Setup/SetupSection.cs @@ -12,9 +12,9 @@ using osuTK; namespace osu.Game.Screens.Edit.Setup { - internal abstract class SetupSection : Container + public abstract class SetupSection : Container { - private readonly FillFlowContainer flow; + private FillFlowContainer flow; /// /// Used to align some of the child s together to achieve a grid-like look. @@ -31,7 +31,8 @@ namespace osu.Game.Screens.Edit.Setup public abstract LocalisableString Title { get; } - protected SetupSection() + [BackgroundDependencyLoader] + private void load() { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; From a2d2ed2ef68565a72b3560e81ad3957500d2afa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 22 Aug 2021 16:49:27 +0200 Subject: [PATCH 1657/2442] Add stack leniency setting for osu! --- .../Edit/Setup/OsuSetupSection.cs | 52 +++++++++++++++++++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 4 ++ 2 files changed, 56 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/Edit/Setup/OsuSetupSection.cs diff --git a/osu.Game.Rulesets.Osu/Edit/Setup/OsuSetupSection.cs b/osu.Game.Rulesets.Osu/Edit/Setup/OsuSetupSection.cs new file mode 100644 index 0000000000..8cb778a2e1 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/Setup/OsuSetupSection.cs @@ -0,0 +1,52 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Screens.Edit.Setup; + +namespace osu.Game.Rulesets.Osu.Edit.Setup +{ + public class OsuSetupSection : RulesetSetupSection + { + private LabelledSliderBar stackLeniency; + + public OsuSetupSection() + : base(new OsuRuleset().RulesetInfo) + { + } + + [BackgroundDependencyLoader] + private void load() + { + Children = new[] + { + stackLeniency = new LabelledSliderBar + { + Label = "Stack Leniency", + Description = "In play mode, osu! automatically stacks notes which occur at the same location. Increasing this value means it is more likely to snap notes of further time-distance.", + Current = new BindableFloat(Beatmap.BeatmapInfo.StackLeniency) + { + Default = 0.7f, + MinValue = 0, + MaxValue = 1, + Precision = 0.1f + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + stackLeniency.Current.BindValueChanged(_ => updateBeatmap()); + } + + private void updateBeatmap() + { + Beatmap.BeatmapInfo.StackLeniency = stackLeniency.Current.Value; + } + } +} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index b13cdff1ec..97af7d28af 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -30,9 +30,11 @@ using osu.Game.Skinning; using System; using System.Linq; using osu.Framework.Extensions.EnumExtensions; +using osu.Game.Rulesets.Osu.Edit.Setup; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Skinning.Legacy; using osu.Game.Rulesets.Osu.Statistics; +using osu.Game.Screens.Edit.Setup; using osu.Game.Screens.Ranking.Statistics; namespace osu.Game.Rulesets.Osu @@ -305,5 +307,7 @@ namespace osu.Game.Rulesets.Osu } }; } + + public override RulesetSetupSection CreateEditorSetupSectionForRuleset() => new OsuSetupSection(); } } From 565f147a5c631a97dbddbcac76485104cff8ec03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 22 Aug 2021 17:40:11 +0200 Subject: [PATCH 1658/2442] Add special style setting for osu!mania --- .../Edit/Setup/ManiaSetupSection.cs | 45 +++++++++++++++++++ osu.Game.Rulesets.Mania/ManiaRuleset.cs | 4 ++ 2 files changed, 49 insertions(+) create mode 100644 osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs diff --git a/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs b/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs new file mode 100644 index 0000000000..d0b32a7268 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs @@ -0,0 +1,45 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Screens.Edit.Setup; + +namespace osu.Game.Rulesets.Mania.Edit.Setup +{ + public class ManiaSetupSection : RulesetSetupSection + { + private LabelledSwitchButton specialStyle; + + public ManiaSetupSection() + : base(new ManiaRuleset().RulesetInfo) + { + } + + [BackgroundDependencyLoader] + private void load() + { + Children = new Drawable[] + { + specialStyle = new LabelledSwitchButton + { + Label = "Use special (N+1) style", + Current = { Value = Beatmap.BeatmapInfo.SpecialStyle } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + specialStyle.Current.BindValueChanged(_ => updateBeatmap()); + } + + private void updateBeatmap() + { + Beatmap.BeatmapInfo.SpecialStyle = specialStyle.Current.Value; + } + } +} diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index f4b6e10af4..5ba1e2e6c3 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -27,11 +27,13 @@ using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Configuration; using osu.Game.Rulesets.Mania.Difficulty; using osu.Game.Rulesets.Mania.Edit; +using osu.Game.Rulesets.Mania.Edit.Setup; using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Mania.Skinning.Legacy; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; using osu.Game.Scoring; +using osu.Game.Screens.Edit.Setup; using osu.Game.Screens.Ranking.Statistics; namespace osu.Game.Rulesets.Mania @@ -390,6 +392,8 @@ namespace osu.Game.Rulesets.Mania { return new ManiaFilterCriteria(); } + + public override RulesetSetupSection CreateEditorSetupSectionForRuleset() => new ManiaSetupSection(); } public enum PlayfieldType From 0beef9c1e7ed296401b347a42811029b593379f0 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Fri, 3 Sep 2021 02:20:22 +0100 Subject: [PATCH 1659/2442] made double cheese detection stricter --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 6cd32c798d..c4230fc9a4 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double deltaTime = current.DeltaTime; // Aim to nerf cheesy rhythms (Very fast consecutive doubles with large deltatimes between) - if (Previous.Count > 0 && deltaTime <= greatWindow * 2) + if (Previous.Count > 0 && deltaTime <= greatWindow) { deltaTime = Math.Max(Previous[0].DeltaTime, deltaTime); } From bf87a4b2d3802cfdd17b04f412fc9c9f9ab98799 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Fri, 3 Sep 2021 02:39:21 +0100 Subject: [PATCH 1660/2442] interpolate the doubletap cheese nerf instead --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index c4230fc9a4..836e926cf1 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -2,11 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Objects; +using osu.Framework.Utils; namespace osu.Game.Rulesets.Osu.Difficulty.Skills { @@ -47,9 +47,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double deltaTime = current.DeltaTime; // Aim to nerf cheesy rhythms (Very fast consecutive doubles with large deltatimes between) - if (Previous.Count > 0 && deltaTime <= greatWindow) + double deltaTimeThreshold = greatWindow * 2; + + if (Previous.Count > 0 && deltaTime < deltaTimeThreshold && Previous[0].DeltaTime > deltaTime) { - deltaTime = Math.Max(Previous[0].DeltaTime, deltaTime); + double closenessToZero = Math.Min(1, deltaTime / deltaTimeThreshold); + deltaTime = Interpolation.Lerp(Previous[0].DeltaTime, deltaTime, closenessToZero); } // Cap deltatime to the OD 300 hitwindow. From 8654a0af05798ec1a8db0c8af9e9e737b523c724 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Fri, 3 Sep 2021 03:01:25 +0100 Subject: [PATCH 1661/2442] remove unnecessary min & renamed variable so its more descriptive --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 836e926cf1..fb24476493 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -51,8 +51,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills if (Previous.Count > 0 && deltaTime < deltaTimeThreshold && Previous[0].DeltaTime > deltaTime) { - double closenessToZero = Math.Min(1, deltaTime / deltaTimeThreshold); - deltaTime = Interpolation.Lerp(Previous[0].DeltaTime, deltaTime, closenessToZero); + double speedWindowRatio = deltaTime / deltaTimeThreshold; + deltaTime = Interpolation.Lerp(Previous[0].DeltaTime, deltaTime, speedWindowRatio); } // Cap deltatime to the OD 300 hitwindow. From 000b85a860cb4e6f2f8cf12884fdf85adf1799aa Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 3 Sep 2021 17:56:56 +0900 Subject: [PATCH 1662/2442] Add GH Actions workflow for diffcalc checks --- .github/workflows/test-diffcalc.yml | 150 ++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 .github/workflows/test-diffcalc.yml diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml new file mode 100644 index 0000000000..b703c97735 --- /dev/null +++ b/.github/workflows/test-diffcalc.yml @@ -0,0 +1,150 @@ +name: Diffcalc Consistency Checks +on: + issue_comment: + types: [ created ] + +env: + DB_USER: root + DB_HOST: 127.0.0.1 + CONCURRENCY: 4 + ALLOW_DOWNLOAD: 1 + SAVE_DOWNLOADED: 1 + +jobs: + diffcalc: + name: Diffcalc + runs-on: ubuntu-latest + + if: | + contains(github.event.comment.html_url, '/pull/') && + contains(github.event.comment.body, '!pp check') && + ${{ github.event.comment.author_association == 'MEMBER' }} + + + strategy: + fail-fast: false + matrix: + ruleset: + - { name: osu, id: 0 } + - { name: taiko, id: 1 } + - { name: catch, id: 2 } + - { name: mania, id: 3 } + + services: + mysql: + image: mysql:8.0 + env: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + + steps: + - name: Verify MySQL connection from host + run: | + sudo apt-get install -y mysql-client + mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} -e "SHOW DATABASES" + + - name: Create directory structure + run: | + mkdir -p $GITHUB_WORKSPACE/master/ + mkdir -p $GITHUB_WORKSPACE/pr/ + + # Checkout osu + - name: Checkout osu (master) + uses: actions/checkout@v2 + with: + repository: ppy/osu + path: 'master/osu' + - name: Checkout osu (pr) + uses: actions/checkout@v2 + with: + path: 'pr/osu' + + # Checkout osu-difficulty-calculator + - name: Checkout osu-difficulty-calculator (master) + uses: actions/checkout@v2 + with: + repository: ppy/osu-difficulty-calculator + path: 'master/osu-difficulty-calculator' + - name: Checkout osu-difficulty-calculator (pr) + uses: actions/checkout@v2 + with: + repository: ppy/osu-difficulty-calculator + path: 'pr/osu-difficulty-calculator' + + - name: Install .NET 5.0.x + uses: actions/setup-dotnet@v1 + with: + dotnet-version: "5.0.x" + + # Sanity checks to make sure diffcalc is not run when incompatible. + - name: Build diffcalc (master) + run: | + cd $GITHUB_WORKSPACE/master/osu-difficulty-calculator + ./UseLocalOsu.sh + dotnet build + - name: Build diffcalc (pr) + run: | + cd $GITHUB_WORKSPACE/pr/osu-difficulty-calculator + ./UseLocalOsu.sh + dotnet build + + # Initial data imports + - name: Download + import data + run: | + PERFORMANCE_DATA_NAME=$(date +'%Y_%m_01_performance_${{ matrix.ruleset.name }}_random') + BEATMAPS_DATA_NAME=$(date +'%Y_%m_01_osu_files') + + # Set env variable for further steps. + echo "BEATMAPS_PATH=$GITHUB_WORKSPACE/$BEATMAPS_DATA_NAME" >> $GITHUB_ENV + + cd $GITHUB_WORKSPACE + + wget https://data.ppy.sh/$PERFORMANCE_DATA_NAME.tar.bz2 + wget https://data.ppy.sh/$BEATMAPS_DATA_NAME.tar.bz2 + tar -xf $PERFORMANCE_DATA_NAME.tar.bz2 + tar -xf $BEATMAPS_DATA_NAME.tar.bz2 + + cd $GITHUB_WORKSPACE/$PERFORMANCE_DATA_NAME + + mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} -e "CREATE DATABASE osu_master" + mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} -e "CREATE DATABASE osu_pr" + + cat *.sql | mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} --database=osu_master + cat *.sql | mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} --database=osu_pr + + # Run diffcalc + - name: Run diffcalc (master) + env: + DB_NAME: osu_master + run: | + cd $GITHUB_WORKSPACE/master/osu-difficulty-calculator/osu.Server.DifficultyCalculator + dotnet run -c:Release -- all -m ${{ matrix.ruleset.id }} -ac -c ${{ env.CONCURRENCY }} + - name: Run diffcalc (pr) + env: + DB_NAME: osu_pr + run: | + cd $GITHUB_WORKSPACE/pr/osu-difficulty-calculator/osu.Server.DifficultyCalculator + dotnet run -c:Release -- all -m ${{ matrix.ruleset.id }} -ac -c ${{ env.CONCURRENCY }} + + # Print diffs + - name: Print diffs + run: | + mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} -e " + SELECT + m.beatmap_id, + m.mods, + m.diff_unified as `sr_master`, + p.diff_unified as `sr_pr`, + (p.diff_unified - m.diff_unified) as `diff` + FROM osu_master.osu_beatmap_difficulty m + JOIN osu_pr.osu_beatmap_difficulty p + ON m.beatmap_id = p.beatmap_id + AND m.mode = p.mode + AND m.mods = p.mods + WHERE abs(m.diff_unified - p.diff_unified) > 0.1 + ORDER BY abs(m.diff_unified - p.diff_unified) + DESC;" + + # Todo: Run ppcalc \ No newline at end of file From 176c414c112e1321a65fde03b78ecf72a479285a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 3 Sep 2021 18:10:16 +0900 Subject: [PATCH 1663/2442] More accurate data retrieval --- .github/workflows/test-diffcalc.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index b703c97735..3d80f08ebf 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -93,8 +93,8 @@ jobs: # Initial data imports - name: Download + import data run: | - PERFORMANCE_DATA_NAME=$(date +'%Y_%m_01_performance_${{ matrix.ruleset.name }}_random') - BEATMAPS_DATA_NAME=$(date +'%Y_%m_01_osu_files') + PERFORMANCE_DATA_NAME=$(curl https://data.ppy.sh/ | grep performance_${{ matrix.ruleset.name }}_random | tail -1 | awk -F "\"" '{print $2}' | sed 's/\.tar\.bz2//g') + BEATMAPS_DATA_NAME=$(curl https://data.ppy.sh/ | grep osu_files | tail -1 | awk -F "\"" '{print $2}' | sed 's/\.tar\.bz2//g') # Set env variable for further steps. echo "BEATMAPS_PATH=$GITHUB_WORKSPACE/$BEATMAPS_DATA_NAME" >> $GITHUB_ENV From b8ada31d7de0b4fa47abd50a84d83a67e409eec5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 3 Sep 2021 18:47:15 +0900 Subject: [PATCH 1664/2442] Match against individual rulesets --- .github/workflows/test-diffcalc.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 3d80f08ebf..48e1e238c1 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -1,3 +1,8 @@ +# Listens to new PR comments containing "!pp check", and runs diffcalc across the PR and master to produce a table of differences. +# Usage: +# !pp check 0 : Runs only the osu! ruleset +# !pp check 0 1 : Runs the osu! and taiko rulesets. + name: Diffcalc Consistency Checks on: issue_comment: @@ -18,8 +23,8 @@ jobs: if: | contains(github.event.comment.html_url, '/pull/') && contains(github.event.comment.body, '!pp check') && - ${{ github.event.comment.author_association == 'MEMBER' }} - + ${{ github.event.comment.author_association == 'MEMBER' }} && + contains(github.event.comment.body, ${{ matrix.ruleset.id }}) strategy: fail-fast: false From 789c108e8d426fba356d4f424427f558c2b19ec8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 3 Sep 2021 18:56:56 +0900 Subject: [PATCH 1665/2442] Fix if condition --- .github/workflows/test-diffcalc.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 48e1e238c1..64ffa84c5e 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -23,8 +23,7 @@ jobs: if: | contains(github.event.comment.html_url, '/pull/') && contains(github.event.comment.body, '!pp check') && - ${{ github.event.comment.author_association == 'MEMBER' }} && - contains(github.event.comment.body, ${{ matrix.ruleset.id }}) + ${{ github.event.comment.author_association == 'MEMBER' }} strategy: fail-fast: false @@ -34,6 +33,10 @@ jobs: - { name: taiko, id: 1 } - { name: catch, id: 2 } - { name: mania, id: 3 } + isValidRuleset: + - contains(github.event.comment.body, ${{ matrix.ruleset.id }}) + exclude: + - isValidRuleset: false services: mysql: From 33dcb4915b28e36e354151eb8e7ee929695bddd7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 3 Sep 2021 19:12:41 +0900 Subject: [PATCH 1666/2442] Revert "Fix if condition" This reverts commit 789c108e8d426fba356d4f424427f558c2b19ec8. --- .github/workflows/test-diffcalc.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 64ffa84c5e..48e1e238c1 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -23,7 +23,8 @@ jobs: if: | contains(github.event.comment.html_url, '/pull/') && contains(github.event.comment.body, '!pp check') && - ${{ github.event.comment.author_association == 'MEMBER' }} + ${{ github.event.comment.author_association == 'MEMBER' }} && + contains(github.event.comment.body, ${{ matrix.ruleset.id }}) strategy: fail-fast: false @@ -33,10 +34,6 @@ jobs: - { name: taiko, id: 1 } - { name: catch, id: 2 } - { name: mania, id: 3 } - isValidRuleset: - - contains(github.event.comment.body, ${{ matrix.ruleset.id }}) - exclude: - - isValidRuleset: false services: mysql: From 54389561aaefd5b1f69f1aa5b360260ba1328676 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 3 Sep 2021 19:12:55 +0900 Subject: [PATCH 1667/2442] Revert "Match against individual rulesets" This reverts commit b8ada31d7de0b4fa47abd50a84d83a67e409eec5. --- .github/workflows/test-diffcalc.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 48e1e238c1..3d80f08ebf 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -1,8 +1,3 @@ -# Listens to new PR comments containing "!pp check", and runs diffcalc across the PR and master to produce a table of differences. -# Usage: -# !pp check 0 : Runs only the osu! ruleset -# !pp check 0 1 : Runs the osu! and taiko rulesets. - name: Diffcalc Consistency Checks on: issue_comment: @@ -23,8 +18,8 @@ jobs: if: | contains(github.event.comment.html_url, '/pull/') && contains(github.event.comment.body, '!pp check') && - ${{ github.event.comment.author_association == 'MEMBER' }} && - contains(github.event.comment.body, ${{ matrix.ruleset.id }}) + ${{ github.event.comment.author_association == 'MEMBER' }} + strategy: fail-fast: false From 893a4d43657ab4d335d951cee8e22708d00e09b4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 3 Sep 2021 19:23:55 +0900 Subject: [PATCH 1668/2442] Fix incorrect quoting --- .github/workflows/test-diffcalc.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 3d80f08ebf..1e5c033a67 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -135,9 +135,9 @@ jobs: SELECT m.beatmap_id, m.mods, - m.diff_unified as `sr_master`, - p.diff_unified as `sr_pr`, - (p.diff_unified - m.diff_unified) as `diff` + m.diff_unified as 'sr_master', + p.diff_unified as 'sr_pr', + (p.diff_unified - m.diff_unified) as 'diff' FROM osu_master.osu_beatmap_difficulty m JOIN osu_pr.osu_beatmap_difficulty p ON m.beatmap_id = p.beatmap_id From c6d71e1ae8bb7d3862abd063db7e285f311e4d2d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 3 Sep 2021 20:10:17 +0900 Subject: [PATCH 1669/2442] Add back ruleset check --- .github/workflows/test-diffcalc.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 1e5c033a67..ab7107f594 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -20,7 +20,6 @@ jobs: contains(github.event.comment.body, '!pp check') && ${{ github.event.comment.author_association == 'MEMBER' }} - strategy: fail-fast: false matrix: @@ -40,6 +39,12 @@ jobs: options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 steps: + - name: Verify ruleset + if: contains(github.event.comment.body, matrix.ruleset.id) == false + run: | + echo "${{ github.event.comment.body }} doesn't contain ${{ matrix.ruleset.id }}" + exit 1 + - name: Verify MySQL connection from host run: | sudo apt-get install -y mysql-client From 1f7f8bb18992d1a8a558eb432acdc1a7e55e30ec Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 3 Sep 2021 20:13:33 +0900 Subject: [PATCH 1670/2442] Add description to workflow --- .github/workflows/test-diffcalc.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index ab7107f594..c6b2f254c8 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -1,3 +1,9 @@ +# Listens for new PR comments containing !pp check [id], and runs a diffcalc comparison against master. +# Usage: +# !pp check 0 | Runs only the osu! ruleset. +# !pp check 0 2 | Runs only the osu! and catch rulesets. +# + name: Diffcalc Consistency Checks on: issue_comment: From 88158b79f8e5ad2154fa1efd0f89e10532421600 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 3 Sep 2021 20:37:38 +0900 Subject: [PATCH 1671/2442] Change to using top scores --- .github/workflows/test-diffcalc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index c6b2f254c8..3bec11928f 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -104,7 +104,7 @@ jobs: # Initial data imports - name: Download + import data run: | - PERFORMANCE_DATA_NAME=$(curl https://data.ppy.sh/ | grep performance_${{ matrix.ruleset.name }}_random | tail -1 | awk -F "\"" '{print $2}' | sed 's/\.tar\.bz2//g') + PERFORMANCE_DATA_NAME=$(curl https://data.ppy.sh/ | grep performance_${{ matrix.ruleset.name }}_top | tail -1 | awk -F "\"" '{print $2}' | sed 's/\.tar\.bz2//g') BEATMAPS_DATA_NAME=$(curl https://data.ppy.sh/ | grep osu_files | tail -1 | awk -F "\"" '{print $2}' | sed 's/\.tar\.bz2//g') # Set env variable for further steps. From 16beb2c90c1884d8829f52cb92142c06498d3b00 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 4 Sep 2021 15:35:46 +0900 Subject: [PATCH 1672/2442] Expose more pieces of `TabletSettings` --- .../Settings/Sections/Input/TabletAreaSelection.cs | 8 +++++--- .../Overlays/Settings/Sections/Input/TabletSettings.cs | 4 +++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs index 412889d210..e18cf7e1c2 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs @@ -17,6 +17,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input { public class TabletAreaSelection : CompositeDrawable { + public bool IsWithinBounds { get; private set; } + private readonly ITabletHandler handler; private Container tabletContainer; @@ -171,10 +173,10 @@ namespace osu.Game.Overlays.Settings.Sections.Input var usableSsdq = usableAreaContainer.ScreenSpaceDrawQuad; - bool isWithinBounds = tabletContainer.ScreenSpaceDrawQuad.Contains(usableSsdq.TopLeft + new Vector2(1)) && - tabletContainer.ScreenSpaceDrawQuad.Contains(usableSsdq.BottomRight - new Vector2(1)); + IsWithinBounds = tabletContainer.ScreenSpaceDrawQuad.Contains(usableSsdq.TopLeft + new Vector2(1)) && + tabletContainer.ScreenSpaceDrawQuad.Contains(usableSsdq.BottomRight - new Vector2(1)); - usableFill.FadeColour(isWithinBounds ? colour.Blue : colour.RedLight, 100); + usableFill.FadeColour(IsWithinBounds ? colour.Blue : colour.RedLight, 100); } protected override void Update() diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index b8b86d9069..8c60e81fb5 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -20,6 +20,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input { public class TabletSettings : SettingsSubsection { + public TabletAreaSelection AreaSelection { get; private set; } + private readonly ITabletHandler tabletHandler; private readonly Bindable enabled = new BindableBool(true); @@ -121,7 +123,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input Direction = FillDirection.Vertical, Children = new Drawable[] { - new TabletAreaSelection(tabletHandler) + AreaSelection = new TabletAreaSelection(tabletHandler) { RelativeSizeAxes = Axes.X, Height = 300, From 8d44f059ec952080516bb6f811676404bde64ebb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 4 Sep 2021 15:35:54 +0900 Subject: [PATCH 1673/2442] Add test coverage of failing validity checks --- .../Settings/TestSceneTabletSettings.cs | 55 ++++++++++++++----- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs index da474a64ba..0202393973 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs @@ -2,11 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input.Handlers.Tablet; -using osu.Framework.Platform; +using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Overlays; using osu.Game.Overlays.Settings.Sections.Input; @@ -17,22 +16,34 @@ namespace osu.Game.Tests.Visual.Settings [TestFixture] public class TestSceneTabletSettings : OsuTestScene { - [BackgroundDependencyLoader] - private void load(GameHost host) - { - var tabletHandler = new TestTabletHandler(); + private TestTabletHandler tabletHandler; + private TabletSettings settings; - AddRange(new Drawable[] + [SetUpSteps] + public void SetUpSteps() + { + AddStep("create settings", () => { - new TabletSettings(tabletHandler) + tabletHandler = new TestTabletHandler(); + + Children = new Drawable[] { - RelativeSizeAxes = Axes.None, - Width = SettingsPanel.PANEL_WIDTH, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - } + settings = new TabletSettings(tabletHandler) + { + RelativeSizeAxes = Axes.None, + Width = SettingsPanel.PANEL_WIDTH, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + } + }; }); + AddStep("set square size", () => tabletHandler.SetTabletSize(new Vector2(100, 100))); + } + + [Test] + public void TestVariousTabletSizes() + { AddStep("Test with wide tablet", () => tabletHandler.SetTabletSize(new Vector2(160, 100))); AddStep("Test with square tablet", () => tabletHandler.SetTabletSize(new Vector2(300, 300))); AddStep("Test with tall tablet", () => tabletHandler.SetTabletSize(new Vector2(100, 300))); @@ -40,6 +51,24 @@ namespace osu.Game.Tests.Visual.Settings AddStep("Test no tablet present", () => tabletHandler.SetTabletSize(Vector2.Zero)); } + [Test] + public void TestValidAfterRotation() + { + AddAssert("area valid", () => settings.AreaSelection.IsWithinBounds); + + AddStep("rotate 90", () => tabletHandler.Rotation.Value = 90); + AddAssert("area valid", () => settings.AreaSelection.IsWithinBounds); + + AddStep("rotate 180", () => tabletHandler.Rotation.Value = 180); + AddAssert("area valid", () => settings.AreaSelection.IsWithinBounds); + + AddStep("rotate 360", () => tabletHandler.Rotation.Value = 360); + AddAssert("area valid", () => settings.AreaSelection.IsWithinBounds); + + AddStep("rotate 0", () => tabletHandler.Rotation.Value = 0); + AddAssert("area valid", () => settings.AreaSelection.IsWithinBounds); + } + public class TestTabletHandler : ITabletHandler { public Bindable AreaOffset { get; } = new Bindable(); From 66daa553de2eac46f35d511a34ff959d1b3ab9c4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 4 Sep 2021 19:34:55 +0900 Subject: [PATCH 1674/2442] Fix bounds check running too early causing tablet area to show incorrect validity --- .../Overlays/Settings/Sections/Input/TabletAreaSelection.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs index e18cf7e1c2..4f27a2da70 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs @@ -131,9 +131,9 @@ namespace osu.Game.Overlays.Settings.Sections.Input rotation.BindTo(handler.Rotation); rotation.BindValueChanged(val => { - tabletContainer.RotateTo(-val.NewValue, 800, Easing.OutQuint); - usableAreaContainer.RotateTo(val.NewValue, 100, Easing.OutQuint) - .OnComplete(_ => checkBounds()); // required as we are using SSDQ. + usableAreaContainer.RotateTo(val.NewValue, 100, Easing.OutQuint); + tabletContainer.RotateTo(-val.NewValue, 800, Easing.OutQuint) + .OnComplete(_ => checkBounds()); // required as we are using SSDQ. }, true); tablet.BindTo(handler.Tablet); From 7b26e480e6492348bb0c91b65a20c9f3d9cbfcf5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 4 Sep 2021 22:55:14 +0900 Subject: [PATCH 1675/2442] Add extended tests --- .../Settings/TestSceneTabletSettings.cs | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs index 0202393973..a854bec1a9 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs @@ -1,9 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using NUnit.Framework; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Input.Handlers.Tablet; using osu.Framework.Testing; using osu.Framework.Utils; @@ -57,15 +59,27 @@ namespace osu.Game.Tests.Visual.Settings AddAssert("area valid", () => settings.AreaSelection.IsWithinBounds); AddStep("rotate 90", () => tabletHandler.Rotation.Value = 90); - AddAssert("area valid", () => settings.AreaSelection.IsWithinBounds); + ensureValid(); AddStep("rotate 180", () => tabletHandler.Rotation.Value = 180); - AddAssert("area valid", () => settings.AreaSelection.IsWithinBounds); + + ensureValid(); + + AddStep("rotate 270", () => tabletHandler.Rotation.Value = 270); + + ensureValid(); AddStep("rotate 360", () => tabletHandler.Rotation.Value = 360); - AddAssert("area valid", () => settings.AreaSelection.IsWithinBounds); + + ensureValid(); AddStep("rotate 0", () => tabletHandler.Rotation.Value = 0); + ensureValid(); + } + + private void ensureValid() + { + AddUntilStep("wait for transforms", () => settings.AreaSelection.ChildrenOfType().All(c => !c.Transforms.Any())); AddAssert("area valid", () => settings.AreaSelection.IsWithinBounds); } From 94b34a5474cf625b274f3b6f84477417b62ee179 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 4 Sep 2021 23:19:05 +0900 Subject: [PATCH 1676/2442] Add test coverage of invalid cases too --- .../Settings/TestSceneTabletSettings.cs | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs index a854bec1a9..d5bcbc5fd9 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs @@ -54,7 +54,7 @@ namespace osu.Game.Tests.Visual.Settings } [Test] - public void TestValidAfterRotation() + public void TestRotationValidity() { AddAssert("area valid", () => settings.AreaSelection.IsWithinBounds); @@ -75,6 +75,22 @@ namespace osu.Game.Tests.Visual.Settings AddStep("rotate 0", () => tabletHandler.Rotation.Value = 0); ensureValid(); + + AddStep("rotate 0", () => tabletHandler.Rotation.Value = 45); + ensureInvalid(); + + AddStep("rotate 0", () => tabletHandler.Rotation.Value = 0); + ensureValid(); + } + + [Test] + public void TestOffsetValidity() + { + ensureValid(); + AddStep("move right", () => tabletHandler.AreaOffset.Value = Vector2.Zero); + ensureInvalid(); + AddStep("move back", () => tabletHandler.AreaOffset.Value = tabletHandler.AreaSize.Value / 2); + ensureValid(); } private void ensureValid() @@ -83,6 +99,12 @@ namespace osu.Game.Tests.Visual.Settings AddAssert("area valid", () => settings.AreaSelection.IsWithinBounds); } + private void ensureInvalid() + { + AddUntilStep("wait for transforms", () => settings.AreaSelection.ChildrenOfType().All(c => !c.Transforms.Any())); + AddAssert("area invalid", () => !settings.AreaSelection.IsWithinBounds); + } + public class TestTabletHandler : ITabletHandler { public Bindable AreaOffset { get; } = new Bindable(); From 4fb3a1d64162866cb973efeedbe456af2eba8603 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 4 Sep 2021 23:08:35 +0900 Subject: [PATCH 1677/2442] Update check to inflate in the correct direct Also handles previously unhandled edge cases by comparing all four corners, instead of only two. --- .../Sections/Input/TabletAreaSelection.cs | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs index 4f27a2da70..d12052b24d 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs @@ -4,10 +4,13 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.MatrixExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Handlers.Tablet; +using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK; @@ -131,9 +134,9 @@ namespace osu.Game.Overlays.Settings.Sections.Input rotation.BindTo(handler.Rotation); rotation.BindValueChanged(val => { - usableAreaContainer.RotateTo(val.NewValue, 100, Easing.OutQuint); - tabletContainer.RotateTo(-val.NewValue, 800, Easing.OutQuint) - .OnComplete(_ => checkBounds()); // required as we are using SSDQ. + usableAreaContainer.RotateTo(val.NewValue, 100, Easing.OutQuint) + .OnComplete(_ => checkBounds()); // required as we are using SSDQ. + tabletContainer.RotateTo(-val.NewValue, 800, Easing.OutQuint); }, true); tablet.BindTo(handler.Tablet); @@ -171,10 +174,22 @@ namespace osu.Game.Overlays.Settings.Sections.Input if (tablet.Value == null) return; - var usableSsdq = usableAreaContainer.ScreenSpaceDrawQuad; + // All of this manual logic is just to get around floating point issues when doing a contains check on the screen quads. + // This is best effort, as it's only used for display purposes. If we need for anything more, manual math on the raw values should be preferred. + var containerQuad = tabletContainer.ScreenSpaceDrawQuad.AABBFloat.Inflate(1); + var usableAreaQuad = Quad.FromRectangle(usableAreaContainer.ScreenSpaceDrawQuad.AABBFloat); - IsWithinBounds = tabletContainer.ScreenSpaceDrawQuad.Contains(usableSsdq.TopLeft + new Vector2(1)) && - tabletContainer.ScreenSpaceDrawQuad.Contains(usableSsdq.BottomRight - new Vector2(1)); + var matrix = Matrix3.Identity; + MatrixExtensions.TranslateFromLeft(ref matrix, usableAreaQuad.Centre); + MatrixExtensions.RotateFromLeft(ref matrix, MathUtils.DegreesToRadians(rotation.Value)); + MatrixExtensions.TranslateFromLeft(ref matrix, -usableAreaQuad.Centre); + usableAreaQuad *= matrix; + + IsWithinBounds = + containerQuad.Contains(usableAreaQuad.TopLeft) && + containerQuad.Contains(usableAreaQuad.TopRight) && + containerQuad.Contains(usableAreaQuad.BottomLeft) && + containerQuad.Contains(usableAreaQuad.BottomRight); usableFill.FadeColour(IsWithinBounds ? colour.Blue : colour.RedLight, 100); } From 3fce3f620f5903f2aee9ee7566273a887d48be62 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Sat, 4 Sep 2021 16:56:15 +0100 Subject: [PATCH 1678/2442] use OsuHitWindows, amend comment --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index fb24476493..e69f15188d 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -7,6 +7,8 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Objects; using osu.Framework.Utils; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Osu.Scoring; namespace osu.Game.Rulesets.Osu.Difficulty.Skills { @@ -33,7 +35,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills public Speed(Mod[] mods, float od, double clockRate) : base(mods) { - greatWindow = (79 - (od * 6) + 0.5) / clockRate; + HitWindows hitWindows = new OsuHitWindows(); + hitWindows.SetDifficulty(od); + greatWindow = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate; } protected override double StrainValueOf(DifficultyHitObject current) @@ -56,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } // Cap deltatime to the OD 300 hitwindow. - // 0.77 is derived from making sure 260bpm OD8 streams aren't nerfed + // 0.77 is derived from making sure 260bpm OD8 streams aren't nerfed harshly var hitWindowNerf = deltaTime / (greatWindow * 2 * 0.77); deltaTime /= Math.Clamp(hitWindowNerf, 0.92, 1); From 1a90fb1ef341d3d72a75bc29c6e2410967b67800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 4 Sep 2021 19:52:42 +0200 Subject: [PATCH 1679/2442] Fix cached property being assigned twice --- osu.Game/Screens/Edit/Setup/SetupScreen.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Edit/Setup/SetupScreen.cs b/osu.Game/Screens/Edit/Setup/SetupScreen.cs index 53ddb13d85..38fcfc5d2f 100644 --- a/osu.Game/Screens/Edit/Setup/SetupScreen.cs +++ b/osu.Game/Screens/Edit/Setup/SetupScreen.cs @@ -11,7 +11,7 @@ namespace osu.Game.Screens.Edit.Setup public class SetupScreen : EditorRoundedScreen { [Cached] - private SectionsContainer sections = new SectionsContainer(); + private SectionsContainer sections { get; } = new SetupScreenSectionsContainer(); [Cached] private SetupScreenHeader header = new SetupScreenHeader(); @@ -37,15 +37,12 @@ namespace osu.Game.Screens.Edit.Setup if (rulesetSpecificSection != null) sectionsEnumerable.Add(rulesetSpecificSection); - AddRange(new Drawable[] + Add(sections.With(s => { - sections = new SetupScreenSectionsContainer - { - RelativeSizeAxes = Axes.Both, - ChildrenEnumerable = sectionsEnumerable, - FixedHeader = header - }, - }); + s.RelativeSizeAxes = Axes.Both; + s.ChildrenEnumerable = sectionsEnumerable; + s.FixedHeader = header; + })); } private class SetupScreenSectionsContainer : SectionsContainer From 4c006333e0298f0998a1a0044df978c521e52dec Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Sat, 4 Sep 2021 19:42:14 +0100 Subject: [PATCH 1680/2442] add /chat command --- osu.Game/Online/Chat/ChannelManager.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 1937019ef6..fb8c90a80c 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -256,8 +256,21 @@ namespace osu.Game.Online.Chat JoinChannel(channel); break; + case "chat": + if (string.IsNullOrWhiteSpace(content)) + { + target.AddNewMessages(new ErrorMessage("Usage: /chat [user]")); + break; + } + + var request = new GetUserRequest(content); + request.Success += u => OpenPrivateChannel(u); + request.Failure += _ => target.AddNewMessages(new ErrorMessage("User not found.")); + api.Queue(request); + break; + case "help": - target.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action], /join [channel], /np")); + target.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action], /join [channel], /chat [user], /np")); break; default: From ea3be927d7f5c8071bacded41a021340b6a6c5e2 Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Sat, 4 Sep 2021 20:02:10 +0100 Subject: [PATCH 1681/2442] convert to method group --- osu.Game/Online/Chat/ChannelManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index fb8c90a80c..f58f1ff40c 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -264,7 +264,7 @@ namespace osu.Game.Online.Chat } var request = new GetUserRequest(content); - request.Success += u => OpenPrivateChannel(u); + request.Success += OpenPrivateChannel; request.Failure += _ => target.AddNewMessages(new ErrorMessage("User not found.")); api.Queue(request); break; From f76f12d361a97238797b7115003bbfc9f79f5272 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 5 Sep 2021 11:14:28 +0900 Subject: [PATCH 1682/2442] Fix incorrect test step name Co-authored-by: PercyDan <50285552+PercyDan54@users.noreply.github.com> --- osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs index d5bcbc5fd9..49d6b7033e 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs @@ -76,7 +76,7 @@ namespace osu.Game.Tests.Visual.Settings AddStep("rotate 0", () => tabletHandler.Rotation.Value = 0); ensureValid(); - AddStep("rotate 0", () => tabletHandler.Rotation.Value = 45); + AddStep("rotate 45", () => tabletHandler.Rotation.Value = 45); ensureInvalid(); AddStep("rotate 0", () => tabletHandler.Rotation.Value = 0); From 1d23ac0f2db355914a45affc9491970eeec39248 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 5 Sep 2021 12:54:21 +0900 Subject: [PATCH 1683/2442] Initial clean up pass on notification logic --- osu.Game/Graphics/UserInterface/HoverSounds.cs | 3 +-- osu.Game/Overlays/NotificationOverlay.cs | 9 +++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/HoverSounds.cs b/osu.Game/Graphics/UserInterface/HoverSounds.cs index c0ef5cb3fc..7db1efc75f 100644 --- a/osu.Game/Graphics/UserInterface/HoverSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverSounds.cs @@ -6,7 +6,6 @@ using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Extensions; using osu.Framework.Graphics; -using osu.Game.Configuration; using osu.Framework.Utils; namespace osu.Game.Graphics.UserInterface @@ -28,7 +27,7 @@ namespace osu.Game.Graphics.UserInterface } [BackgroundDependencyLoader] - private void load(AudioManager audio, SessionStatics statics) + private void load(AudioManager audio) { sampleHover = audio.Samples.Get($@"UI/{SampleSet.GetDescription()}-hover") ?? audio.Samples.Get($@"UI/{HoverSampleSet.Default.GetDescription()}-hover"); diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 2175e17da9..f5b0bf2a7d 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -98,14 +98,16 @@ namespace osu.Game.Overlays private int runningDepth; - private void notificationClosed() => updateCounts(); - private readonly Scheduler postScheduler = new Scheduler(); public override bool IsPresent => base.IsPresent || postScheduler.HasPendingTasks; private bool processingPosts = true; + /// + /// Post a new notification for display. + /// + /// The notification to display. public void Post(Notification notification) => postScheduler.Add(() => { ++runningDepth; @@ -129,6 +131,7 @@ namespace osu.Game.Overlays protected override void Update() { base.Update(); + if (processingPosts) postScheduler.Update(); } @@ -151,6 +154,8 @@ namespace osu.Game.Overlays this.FadeTo(0, TRANSITION_LENGTH, Easing.OutQuint); } + private void notificationClosed() => updateCounts(); + private void updateCounts() { UnreadCount.Value = sections.Select(c => c.UnreadCount).Sum(); From 473e15e8f3ba1a35419bc0bbb0f72034715a2e80 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 5 Sep 2021 13:22:37 +0900 Subject: [PATCH 1684/2442] Add debounce to notification sample playback logic --- osu.Game/Overlays/NotificationOverlay.cs | 25 +++++++++++++++- .../Overlays/Notifications/Notification.cs | 29 ++++--------------- .../Notifications/NotificationSection.cs | 7 +---- .../Notifications/ProgressNotification.cs | 4 +-- .../Notifications/SimpleErrorNotification.cs | 2 +- 5 files changed, 34 insertions(+), 33 deletions(-) diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index f5b0bf2a7d..fcb8692010 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -9,6 +9,7 @@ using osu.Game.Overlays.Notifications; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.Containers; using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Localisation; using osu.Framework.Threading; @@ -29,6 +30,9 @@ namespace osu.Game.Overlays private FlowContainer sections; + [Resolved] + private AudioManager audio { get; set; } + [BackgroundDependencyLoader] private void load() { @@ -104,6 +108,8 @@ namespace osu.Game.Overlays private bool processingPosts = true; + private double? lastSamplePlayback; + /// /// Post a new notification for display. /// @@ -126,6 +132,7 @@ namespace osu.Game.Overlays Show(); updateCounts(); + playDebouncedSample(notification.PopInSampleName); }); protected override void Update() @@ -154,7 +161,23 @@ namespace osu.Game.Overlays this.FadeTo(0, TRANSITION_LENGTH, Easing.OutQuint); } - private void notificationClosed() => updateCounts(); + private void notificationClosed() + { + updateCounts(); + + // this debounce is currently shared between popin/popout sounds, which means one could potentially not play when the user is expecting it. + // popout is constant across all notification types, and should therefore be handled using playback concurrency instead, but seems broken at the moment. + playDebouncedSample("UI/overlay-pop-out"); + } + + private void playDebouncedSample(string sampleName) + { + if (lastSamplePlayback == null || Time.Current - lastSamplePlayback > 50) + { + audio.Samples.Get(sampleName)?.Play(); + lastSamplePlayback = Time.Current; + } + } private void updateCounts() { diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index d1a97c74b2..44203e8ee7 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -3,20 +3,18 @@ using System; using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; -using osu.Game.Graphics; -using osuTK; -using osuTK.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osuTK; +using osuTK.Graphics; namespace osu.Game.Overlays.Notifications { @@ -42,10 +40,7 @@ namespace osu.Game.Overlays.Notifications /// public virtual bool DisplayOnTop => true; - private Sample samplePopIn; - private Sample samplePopOut; - protected virtual string PopInSampleName => "UI/notification-pop-in"; - protected virtual string PopOutSampleName => "UI/overlay-pop-out"; // TODO: replace with a unique sample? + public virtual string PopInSampleName => "UI/notification-pop-in"; protected NotificationLight Light; private readonly CloseButton closeButton; @@ -114,7 +109,7 @@ namespace osu.Game.Overlays.Notifications closeButton = new CloseButton { Alpha = 0, - Action = () => Close(), + Action = Close, Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, Margin = new MarginPadding @@ -127,13 +122,6 @@ namespace osu.Game.Overlays.Notifications }); } - [BackgroundDependencyLoader] - private void load(AudioManager audio) - { - samplePopIn = audio.Samples.Get(PopInSampleName); - samplePopOut = audio.Samples.Get(PopOutSampleName); - } - protected override bool OnHover(HoverEvent e) { closeButton.FadeIn(75); @@ -158,8 +146,6 @@ namespace osu.Game.Overlays.Notifications { base.LoadComplete(); - samplePopIn?.Play(); - this.FadeInFromZero(200); NotificationContent.MoveToX(DrawSize.X); NotificationContent.MoveToX(0, 500, Easing.OutQuint); @@ -167,15 +153,12 @@ namespace osu.Game.Overlays.Notifications public bool WasClosed; - public virtual void Close(bool playSound = true) + public virtual void Close() { if (WasClosed) return; WasClosed = true; - if (playSound) - samplePopOut?.Play(); - Closed?.Invoke(); this.FadeOut(100); Expire(); diff --git a/osu.Game/Overlays/Notifications/NotificationSection.cs b/osu.Game/Overlays/Notifications/NotificationSection.cs index 2316199049..a23ff07a64 100644 --- a/osu.Game/Overlays/Notifications/NotificationSection.cs +++ b/osu.Game/Overlays/Notifications/NotificationSection.cs @@ -110,12 +110,7 @@ namespace osu.Game.Overlays.Notifications private void clearAll() { - bool first = true; - notifications.Children.ForEach(c => - { - c.Close(first); - first = false; - }); + notifications.Children.ForEach(c => c.Close()); } protected override void Update() diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index 703c14af2b..3105ecd742 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -150,12 +150,12 @@ namespace osu.Game.Overlays.Notifications colourCancelled = colours.Red; } - public override void Close(bool playSound = true) + public override void Close() { switch (State) { case ProgressNotificationState.Cancelled: - base.Close(playSound); + base.Close(); break; case ProgressNotificationState.Active: diff --git a/osu.Game/Overlays/Notifications/SimpleErrorNotification.cs b/osu.Game/Overlays/Notifications/SimpleErrorNotification.cs index 13c9c5a02d..faab4ed472 100644 --- a/osu.Game/Overlays/Notifications/SimpleErrorNotification.cs +++ b/osu.Game/Overlays/Notifications/SimpleErrorNotification.cs @@ -7,7 +7,7 @@ namespace osu.Game.Overlays.Notifications { public class SimpleErrorNotification : SimpleNotification { - protected override string PopInSampleName => "UI/error-notification-pop-in"; + public override string PopInSampleName => "UI/error-notification-pop-in"; public SimpleErrorNotification() { From ab1c64591f2a9b224cec05919606836edccd5c21 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 5 Sep 2021 13:25:10 +0900 Subject: [PATCH 1685/2442] Move sample playback debounce time to central `const` --- .../Graphics/UserInterface/HoverSampleDebounceComponent.cs | 7 +------ osu.Game/OsuGameBase.cs | 5 +++++ osu.Game/Overlays/NotificationOverlay.cs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/HoverSampleDebounceComponent.cs b/osu.Game/Graphics/UserInterface/HoverSampleDebounceComponent.cs index 55f43cfe46..1fd03a34e7 100644 --- a/osu.Game/Graphics/UserInterface/HoverSampleDebounceComponent.cs +++ b/osu.Game/Graphics/UserInterface/HoverSampleDebounceComponent.cs @@ -15,11 +15,6 @@ namespace osu.Game.Graphics.UserInterface /// public abstract class HoverSampleDebounceComponent : CompositeDrawable { - /// - /// Length of debounce for hover sound playback, in milliseconds. - /// - public double HoverDebounceTime { get; } = 20; - private Bindable lastPlaybackTime; [BackgroundDependencyLoader] @@ -34,7 +29,7 @@ namespace osu.Game.Graphics.UserInterface if (e.HasAnyButtonPressed) return false; - bool enoughTimePassedSinceLastPlayback = !lastPlaybackTime.Value.HasValue || Time.Current - lastPlaybackTime.Value >= HoverDebounceTime; + bool enoughTimePassedSinceLastPlayback = !lastPlaybackTime.Value.HasValue || Time.Current - lastPlaybackTime.Value >= OsuGameBase.SAMPLE_DEBOUNCE_TIME; if (enoughTimePassedSinceLastPlayback) { diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 69f6bc1b7b..762216e93c 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -56,6 +56,11 @@ namespace osu.Game public const int SAMPLE_CONCURRENCY = 6; + /// + /// Length of debounce (in milliseconds) for commonly occuring sample playbacks that could stack. + /// + public const int SAMPLE_DEBOUNCE_TIME = 20; + /// /// The maximum volume at which audio tracks should playback. This can be set lower than 1 to create some head-room for sound effects. /// diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index fcb8692010..8809dec642 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -172,7 +172,7 @@ namespace osu.Game.Overlays private void playDebouncedSample(string sampleName) { - if (lastSamplePlayback == null || Time.Current - lastSamplePlayback > 50) + if (lastSamplePlayback == null || Time.Current - lastSamplePlayback > OsuGameBase.SAMPLE_DEBOUNCE_TIME) { audio.Samples.Get(sampleName)?.Play(); lastSamplePlayback = Time.Current; From 25420af078151b68146f8786911562869b39e7fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 5 Sep 2021 13:34:23 +0900 Subject: [PATCH 1686/2442] Rename method to drop redundant ruleset suffix --- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 2 +- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- osu.Game/Rulesets/Ruleset.cs | 2 +- osu.Game/Screens/Edit/Setup/SetupScreen.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 5ba1e2e6c3..1f79dae280 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -393,7 +393,7 @@ namespace osu.Game.Rulesets.Mania return new ManiaFilterCriteria(); } - public override RulesetSetupSection CreateEditorSetupSectionForRuleset() => new ManiaSetupSection(); + public override RulesetSetupSection CreateEditorSetupSection() => new ManiaSetupSection(); } public enum PlayfieldType diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 97af7d28af..f4a93a571d 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -308,6 +308,6 @@ namespace osu.Game.Rulesets.Osu }; } - public override RulesetSetupSection CreateEditorSetupSectionForRuleset() => new OsuSetupSection(); + public override RulesetSetupSection CreateEditorSetupSection() => new OsuSetupSection(); } } diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 0a537f2442..de62cf8d33 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -321,6 +321,6 @@ namespace osu.Game.Rulesets /// Can be overridden to add a ruleset-specific section to the editor beatmap setup screen. /// [CanBeNull] - public virtual RulesetSetupSection CreateEditorSetupSectionForRuleset() => null; + public virtual RulesetSetupSection CreateEditorSetupSection() => null; } } diff --git a/osu.Game/Screens/Edit/Setup/SetupScreen.cs b/osu.Game/Screens/Edit/Setup/SetupScreen.cs index 38fcfc5d2f..04767f1786 100644 --- a/osu.Game/Screens/Edit/Setup/SetupScreen.cs +++ b/osu.Game/Screens/Edit/Setup/SetupScreen.cs @@ -33,7 +33,7 @@ namespace osu.Game.Screens.Edit.Setup new DesignSection(), }; - var rulesetSpecificSection = beatmap.BeatmapInfo.Ruleset?.CreateInstance()?.CreateEditorSetupSectionForRuleset(); + var rulesetSpecificSection = beatmap.BeatmapInfo.Ruleset?.CreateInstance()?.CreateEditorSetupSection(); if (rulesetSpecificSection != null) sectionsEnumerable.Add(rulesetSpecificSection); From e0ee2a553375e3ab7460ac3716b280ef577d04eb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 5 Sep 2021 13:34:57 +0900 Subject: [PATCH 1687/2442] Change section title to read better --- osu.Game/Screens/Edit/Setup/RulesetSetupSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Setup/RulesetSetupSection.cs b/osu.Game/Screens/Edit/Setup/RulesetSetupSection.cs index 9344d5b491..935842ff99 100644 --- a/osu.Game/Screens/Edit/Setup/RulesetSetupSection.cs +++ b/osu.Game/Screens/Edit/Setup/RulesetSetupSection.cs @@ -8,7 +8,7 @@ namespace osu.Game.Screens.Edit.Setup { public abstract class RulesetSetupSection : SetupSection { - public sealed override LocalisableString Title => $"{rulesetInfo.Name}-specific"; + public sealed override LocalisableString Title => $"Ruleset ({rulesetInfo.Name})"; private readonly RulesetInfo rulesetInfo; From 1a26658ba4d3ab18ce3661bb4f1ec4b8405d825d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 5 Sep 2021 13:40:49 +0900 Subject: [PATCH 1688/2442] Add description for mania special style --- osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs b/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs index d0b32a7268..a206aafb8a 100644 --- a/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs +++ b/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs @@ -25,6 +25,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Setup specialStyle = new LabelledSwitchButton { Label = "Use special (N+1) style", + Description = "Changes one column to act as a classic \"scratch\" or \"special\" column, which can be moved around by the user's skin (to the left/right/centre). Generally used in 5k (4+1) or 8key (7+1) configurations.", Current = { Value = Beatmap.BeatmapInfo.SpecialStyle } } }; From 6e4efdd1b11dde4ad43bcee7c1745a7374bf482e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 5 Sep 2021 13:40:58 +0900 Subject: [PATCH 1689/2442] Add test coverage for per-ruleset setup screens --- .../Visual/Editing/TestSceneSetupScreen.cs | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs index 9253023c9a..c3c803ff23 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs @@ -4,8 +4,13 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Mania; +using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Beatmaps; +using osu.Game.Rulesets.Taiko; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Setup; @@ -23,15 +28,31 @@ namespace osu.Game.Tests.Visual.Editing editorBeatmap = new EditorBeatmap(new OsuBeatmap()); } - [BackgroundDependencyLoader] - private void load() - { - Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap); + [Test] + public void TestOsu() => runForRuleset(new OsuRuleset().RulesetInfo); - Child = new SetupScreen + [Test] + public void TestTaiko() => runForRuleset(new TaikoRuleset().RulesetInfo); + + [Test] + public void TestCatch() => runForRuleset(new CatchRuleset().RulesetInfo); + + [Test] + public void TestMania() => runForRuleset(new ManiaRuleset().RulesetInfo); + + private void runForRuleset(RulesetInfo rulesetInfo) + { + AddStep("create screen", () => { - State = { Value = Visibility.Visible }, - }; + editorBeatmap.BeatmapInfo.Ruleset = rulesetInfo; + + Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap); + + Child = new SetupScreen + { + State = { Value = Visibility.Visible }, + }; + }); } } } From 9aa1564e0de3482c29e9b07927090df4bf0ebd75 Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Sun, 5 Sep 2021 10:19:04 +0100 Subject: [PATCH 1690/2442] revert ChannelManager changes --- osu.Game/Online/Chat/ChannelManager.cs | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index f58f1ff40c..bf411d59f6 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -256,21 +256,8 @@ namespace osu.Game.Online.Chat JoinChannel(channel); break; - case "chat": - if (string.IsNullOrWhiteSpace(content)) - { - target.AddNewMessages(new ErrorMessage("Usage: /chat [user]")); - break; - } - - var request = new GetUserRequest(content); - request.Success += OpenPrivateChannel; - request.Failure += _ => target.AddNewMessages(new ErrorMessage("User not found.")); - api.Queue(request); - break; - case "help": - target.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action], /join [channel], /chat [user], /np")); + target.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action], /join [channel], /np")); break; default: @@ -614,4 +601,4 @@ namespace osu.Game.Online.Chat : channel.Id == Id; } } -} +} \ No newline at end of file From e409f2dc6d28257b822ffa06230377c80402dae0 Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Sun, 5 Sep 2021 10:42:38 +0100 Subject: [PATCH 1691/2442] add xmldoc --- osu.Game/Online/API/Requests/GetUserRequest.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/osu.Game/Online/API/Requests/GetUserRequest.cs b/osu.Game/Online/API/Requests/GetUserRequest.cs index 0c8a4a3578..9470c77b79 100644 --- a/osu.Game/Online/API/Requests/GetUserRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRequest.cs @@ -11,16 +11,30 @@ namespace osu.Game.Online.API.Requests private readonly string userIdentifier; public readonly RulesetInfo Ruleset; + + /// + /// Gets the currently logged-in user. + /// public GetUserRequest() { } + /// + /// Gets a user from their ID. + /// + /// The user to get. + /// The ruleset to get the user's info for. public GetUserRequest(long? userId = null, RulesetInfo ruleset = null) { this.userIdentifier = userId.ToString(); Ruleset = ruleset; } + /// + /// Gets a user from their username. + /// + /// The user to get. + /// The ruleset to get the user's info for. public GetUserRequest(string username = null, RulesetInfo ruleset = null) { this.userIdentifier = username; From e5f886a3158e25181b5c047b9e596f88a72332fb Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Sun, 5 Sep 2021 10:45:38 +0100 Subject: [PATCH 1692/2442] revert unnecessary change --- osu.Game/OsuGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 1e6e1e0ead..a83357f4f5 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -329,7 +329,7 @@ namespace osu.Game break; case LinkAction.OpenUserProfile: - if (int.TryParse(link.Argument, out var userId)) + if (int.TryParse(link.Argument, out int userId)) ShowUser(userId); else ShowUser(link.Argument); From f7369e0d682cc27e0e210e1135b1959abc4e1058 Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Sun, 5 Sep 2021 14:47:46 +0100 Subject: [PATCH 1693/2442] create UserIdLookupCache to get user ID when importing scores --- osu.Game/OsuGameBase.cs | 2 +- osu.Game/Scoring/ScoreManager.cs | 35 ++++---- .../Scoring/ScoreManager_UserIdLookupCache.cs | 85 +++++++++++++++++++ 3 files changed, 102 insertions(+), 20 deletions(-) create mode 100644 osu.Game/Scoring/ScoreManager_UserIdLookupCache.cs diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index f2d575550a..484cc23161 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -263,7 +263,7 @@ namespace osu.Game dependencies.Cache(fileStore = new FileStore(contextFactory, Storage)); // ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup() - dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Host, () => difficultyCache, LocalConfig)); + dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Host, () => difficultyCache, LocalConfig, true)); dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Resources, Host, defaultBeatmap, true)); // this should likely be moved to ArchiveModelManager when another case appears where it is necessary diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 3c99dd6637..fcf214268a 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -10,6 +10,7 @@ using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; using osu.Framework.Platform; @@ -27,7 +28,7 @@ using osu.Game.Users; namespace osu.Game.Scoring { - public class ScoreManager : DownloadableArchiveModelManager + public partial class ScoreManager : DownloadableArchiveModelManager { public override IEnumerable HandledExtensions => new[] { ".osr" }; @@ -44,10 +45,13 @@ namespace osu.Game.Scoring [CanBeNull] private readonly OsuConfigManager configManager; + [CanBeNull] + private readonly UserIdLookupCache userIdLookupCache; + private IAPIProvider api { get; set; } public ScoreManager(RulesetStore rulesets, Func beatmaps, Storage storage, IAPIProvider api, IDatabaseContextFactory contextFactory, IIpcHost importHost = null, - Func difficulties = null, OsuConfigManager configManager = null) + Func difficulties = null, OsuConfigManager configManager = null, bool performOnlineLookups = false) : base(storage, contextFactory, api, new ScoreStore(contextFactory, storage), importHost) { this.rulesets = rulesets; @@ -55,6 +59,9 @@ namespace osu.Game.Scoring this.difficulties = difficulties; this.configManager = configManager; this.api = api; + + if (performOnlineLookups) + userIdLookupCache = new UserIdLookupCache(api); } protected override ScoreInfo CreateModel(ArchiveReader archive) @@ -76,30 +83,20 @@ namespace osu.Game.Scoring } } - private readonly Dictionary previouslyLookedUpUsernames = new Dictionary(); - - protected override Task Populate(ScoreInfo model, ArchiveReader archive, CancellationToken cancellationToken = default) + protected override async Task Populate(ScoreInfo model, ArchiveReader archive, CancellationToken cancellationToken = default) { // These scores only provide the user's username but we need the user's ID too. - if (model.UserID <= 1 && model.UserString != null) + if (model.UserID <= 1 && model.UserString != null && userIdLookupCache != null) { - if (previouslyLookedUpUsernames.TryGetValue(model.UserString, out User user)) + try { - model.UserID = user.Id; - return Task.CompletedTask; + model.UserID = await userIdLookupCache.GetUserIdAsync(model.UserString, cancellationToken).ConfigureAwait(false); } - - var request = new GetUserRequest(model.UserString); - request.Success += u => + catch (Exception e) { - model.UserID = u.Id; - previouslyLookedUpUsernames.TryAdd(model.UserString, u); - }; - - api?.Queue(request); + LogForModel(model, $"Online retrieval failed for {model.User} ({e.Message})", e); + } } - - return Task.CompletedTask; } protected override void ExportModelTo(ScoreInfo model, Stream outputStream) diff --git a/osu.Game/Scoring/ScoreManager_UserIdLookupCache.cs b/osu.Game/Scoring/ScoreManager_UserIdLookupCache.cs new file mode 100644 index 0000000000..dc7e244f14 --- /dev/null +++ b/osu.Game/Scoring/ScoreManager_UserIdLookupCache.cs @@ -0,0 +1,85 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Database; + +namespace osu.Game.Scoring +{ + public partial class ScoreManager + { + private class UserIdLookupCache : MemoryCachingComponent + { + private readonly IAPIProvider api; + + public UserIdLookupCache(IAPIProvider api) + { + this.api = api; + } + + /// + /// Perform an API lookup on the specified username, returning the associated ID. + /// + /// The username to lookup. + /// An optional cancellation token. + /// The user ID, or 1 if the user does not exist or the request could not be satisfied. + public Task GetUserIdAsync(string username, CancellationToken token = default) => GetAsync(username, token); + + protected override async Task ComputeValueAsync(string lookup, CancellationToken token = default) + => await queryUserId(lookup).ConfigureAwait(false); + + private readonly Queue<(string username, TaskCompletionSource)> pendingUserTasks = new Queue<(string, TaskCompletionSource)>(); + private Task pendingRequestTask; + private readonly object taskAssignmentLock = new object(); + + private Task queryUserId(string username) + { + lock (taskAssignmentLock) + { + var tcs = new TaskCompletionSource(); + + // Add to the queue. + pendingUserTasks.Enqueue((username, tcs)); + + // Create a request task if there's not already one. + if (pendingRequestTask == null) + createNewTask(); + + return tcs.Task; + } + } + + private void performLookup() + { + (string username, TaskCompletionSource task) next; + + lock (taskAssignmentLock) + { + next = pendingUserTasks.Dequeue(); + } + + var request = new GetUserRequest(next.username); + + // rather than queueing, we maintain our own single-threaded request stream. + // todo: we probably want retry logic here. + api.Perform(request); + + // Create a new request task if there's still more users to query. + lock (taskAssignmentLock) + { + pendingRequestTask = null; + if (pendingUserTasks.Count > 0) + createNewTask(); + } + + next.task.SetResult(request.Result?.Id ?? 1); + } + + private void createNewTask() => pendingRequestTask = Task.Run(performLookup); + } + } +} From 7f9b80e3e5da3c9e362fe028f90d5bfdb5a9e2dc Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Sun, 5 Sep 2021 15:11:41 +0100 Subject: [PATCH 1694/2442] add tests for ShowUser() username overload --- osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs index 03d079261d..70271b0b08 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs @@ -104,6 +104,9 @@ namespace osu.Game.Tests.Visual.Online CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c4.jpg" }, api.IsLoggedIn)); + AddStep("Show ppy from username", () => profile.ShowUser(@"peppy")); + AddStep("Show flyte from username", () => profile.ShowUser(@"flyte")); + AddStep("Hide", profile.Hide); AddStep("Show without reload", profile.Show); } From e78dc1bb4ce6c8d06792bb0c3e2b7042eb33d44f Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Sun, 5 Sep 2021 15:27:28 +0100 Subject: [PATCH 1695/2442] more code quality :/ --- osu.Game/Online/API/Requests/GetUserRequest.cs | 1 - osu.Game/Scoring/ScoreManager.cs | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetUserRequest.cs b/osu.Game/Online/API/Requests/GetUserRequest.cs index 9470c77b79..281926c096 100644 --- a/osu.Game/Online/API/Requests/GetUserRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRequest.cs @@ -11,7 +11,6 @@ namespace osu.Game.Online.API.Requests private readonly string userIdentifier; public readonly RulesetInfo Ruleset; - /// /// Gets the currently logged-in user. /// diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index fcf214268a..ccc5579ee5 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -10,7 +10,6 @@ using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; -using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; using osu.Framework.Platform; @@ -24,7 +23,6 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring.Legacy; -using osu.Game.Users; namespace osu.Game.Scoring { @@ -48,7 +46,7 @@ namespace osu.Game.Scoring [CanBeNull] private readonly UserIdLookupCache userIdLookupCache; - private IAPIProvider api { get; set; } + private readonly IAPIProvider api; public ScoreManager(RulesetStore rulesets, Func beatmaps, Storage storage, IAPIProvider api, IDatabaseContextFactory contextFactory, IIpcHost importHost = null, Func difficulties = null, OsuConfigManager configManager = null, bool performOnlineLookups = false) From b1a995e0bb9d64a663c098518f24790ca0e46a47 Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Sun, 5 Sep 2021 15:49:48 +0100 Subject: [PATCH 1696/2442] revert changes --- osu.Game/Online/Chat/ChannelManager.cs | 2 +- osu.Game/OsuGameBase.cs | 2 +- osu.Game/Scoring/ScoreManager.cs | 30 +------ .../Scoring/ScoreManager_UserIdLookupCache.cs | 85 ------------------- 4 files changed, 6 insertions(+), 113 deletions(-) delete mode 100644 osu.Game/Scoring/ScoreManager_UserIdLookupCache.cs diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index bf411d59f6..1937019ef6 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -601,4 +601,4 @@ namespace osu.Game.Online.Chat : channel.Id == Id; } } -} \ No newline at end of file +} diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 484cc23161..f2d575550a 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -263,7 +263,7 @@ namespace osu.Game dependencies.Cache(fileStore = new FileStore(contextFactory, Storage)); // ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup() - dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Host, () => difficultyCache, LocalConfig, true)); + dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Host, () => difficultyCache, LocalConfig)); dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Resources, Host, defaultBeatmap, true)); // this should likely be moved to ArchiveModelManager when another case appears where it is necessary diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index ccc5579ee5..83bcac01ac 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -26,7 +26,7 @@ using osu.Game.Scoring.Legacy; namespace osu.Game.Scoring { - public partial class ScoreManager : DownloadableArchiveModelManager + public class ScoreManager : DownloadableArchiveModelManager { public override IEnumerable HandledExtensions => new[] { ".osr" }; @@ -43,23 +43,14 @@ namespace osu.Game.Scoring [CanBeNull] private readonly OsuConfigManager configManager; - [CanBeNull] - private readonly UserIdLookupCache userIdLookupCache; - - private readonly IAPIProvider api; - public ScoreManager(RulesetStore rulesets, Func beatmaps, Storage storage, IAPIProvider api, IDatabaseContextFactory contextFactory, IIpcHost importHost = null, - Func difficulties = null, OsuConfigManager configManager = null, bool performOnlineLookups = false) + Func difficulties = null, OsuConfigManager configManager = null) : base(storage, contextFactory, api, new ScoreStore(contextFactory, storage), importHost) { this.rulesets = rulesets; this.beatmaps = beatmaps; this.difficulties = difficulties; this.configManager = configManager; - this.api = api; - - if (performOnlineLookups) - userIdLookupCache = new UserIdLookupCache(api); } protected override ScoreInfo CreateModel(ArchiveReader archive) @@ -81,21 +72,8 @@ namespace osu.Game.Scoring } } - protected override async Task Populate(ScoreInfo model, ArchiveReader archive, CancellationToken cancellationToken = default) - { - // These scores only provide the user's username but we need the user's ID too. - if (model.UserID <= 1 && model.UserString != null && userIdLookupCache != null) - { - try - { - model.UserID = await userIdLookupCache.GetUserIdAsync(model.UserString, cancellationToken).ConfigureAwait(false); - } - catch (Exception e) - { - LogForModel(model, $"Online retrieval failed for {model.User} ({e.Message})", e); - } - } - } + protected override Task Populate(ScoreInfo model, ArchiveReader archive, CancellationToken cancellationToken = default) + => Task.CompletedTask; protected override void ExportModelTo(ScoreInfo model, Stream outputStream) { diff --git a/osu.Game/Scoring/ScoreManager_UserIdLookupCache.cs b/osu.Game/Scoring/ScoreManager_UserIdLookupCache.cs deleted file mode 100644 index dc7e244f14..0000000000 --- a/osu.Game/Scoring/ScoreManager_UserIdLookupCache.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; -using osu.Game.Database; - -namespace osu.Game.Scoring -{ - public partial class ScoreManager - { - private class UserIdLookupCache : MemoryCachingComponent - { - private readonly IAPIProvider api; - - public UserIdLookupCache(IAPIProvider api) - { - this.api = api; - } - - /// - /// Perform an API lookup on the specified username, returning the associated ID. - /// - /// The username to lookup. - /// An optional cancellation token. - /// The user ID, or 1 if the user does not exist or the request could not be satisfied. - public Task GetUserIdAsync(string username, CancellationToken token = default) => GetAsync(username, token); - - protected override async Task ComputeValueAsync(string lookup, CancellationToken token = default) - => await queryUserId(lookup).ConfigureAwait(false); - - private readonly Queue<(string username, TaskCompletionSource)> pendingUserTasks = new Queue<(string, TaskCompletionSource)>(); - private Task pendingRequestTask; - private readonly object taskAssignmentLock = new object(); - - private Task queryUserId(string username) - { - lock (taskAssignmentLock) - { - var tcs = new TaskCompletionSource(); - - // Add to the queue. - pendingUserTasks.Enqueue((username, tcs)); - - // Create a request task if there's not already one. - if (pendingRequestTask == null) - createNewTask(); - - return tcs.Task; - } - } - - private void performLookup() - { - (string username, TaskCompletionSource task) next; - - lock (taskAssignmentLock) - { - next = pendingUserTasks.Dequeue(); - } - - var request = new GetUserRequest(next.username); - - // rather than queueing, we maintain our own single-threaded request stream. - // todo: we probably want retry logic here. - api.Perform(request); - - // Create a new request task if there's still more users to query. - lock (taskAssignmentLock) - { - pendingRequestTask = null; - if (pendingUserTasks.Count > 0) - createNewTask(); - } - - next.task.SetResult(request.Result?.Id ?? 1); - } - - private void createNewTask() => pendingRequestTask = Task.Run(performLookup); - } - } -} From 59ca69e41f2e594acc7a398094bb5f99fbd0e27e Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Sun, 5 Sep 2021 18:16:57 +0100 Subject: [PATCH 1697/2442] add /chat command --- osu.Game/Online/Chat/ChannelManager.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 1937019ef6..f58f1ff40c 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -256,8 +256,21 @@ namespace osu.Game.Online.Chat JoinChannel(channel); break; + case "chat": + if (string.IsNullOrWhiteSpace(content)) + { + target.AddNewMessages(new ErrorMessage("Usage: /chat [user]")); + break; + } + + var request = new GetUserRequest(content); + request.Success += OpenPrivateChannel; + request.Failure += _ => target.AddNewMessages(new ErrorMessage("User not found.")); + api.Queue(request); + break; + case "help": - target.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action], /join [channel], /np")); + target.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action], /join [channel], /chat [user], /np")); break; default: From e8fb5d2e663be713cfba16e967053952305f6e8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 5 Sep 2021 16:07:24 +0200 Subject: [PATCH 1698/2442] Add non-functional difficulty switcher to menu --- osu.Game/Screens/Edit/Editor.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 57c78f3c65..a9fcf29c51 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -703,6 +703,13 @@ namespace osu.Game.Screens.Edit if (RuntimeInfo.IsDesktop) fileMenuItems.Add(new EditorMenuItem("Export package", MenuItemType.Standard, exportBeatmap)); + fileMenuItems.Add(new EditorMenuItemSpacer()); + + var beatmapSet = beatmapManager.QueryBeatmapSet(bs => bs.ID == Beatmap.Value.BeatmapSetInfo.ID) ?? playableBeatmap.BeatmapInfo.BeatmapSet; + var difficultyItems = beatmapSet.Beatmaps.Select(b => new EditorMenuItem(b.Version ?? "(unnamed)")).ToList(); + + fileMenuItems.Add(new EditorMenuItem("Change difficulty") { Items = difficultyItems }); + fileMenuItems.Add(new EditorMenuItemSpacer()); fileMenuItems.Add(new EditorMenuItem("Exit", MenuItemType.Standard, this.Exit)); return fileMenuItems; From 90f0b6874f4fdda9e3b1dee6d7d2dec883e4cfb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 5 Sep 2021 16:10:49 +0200 Subject: [PATCH 1699/2442] Highlight current difficulty in switcher --- osu.Game/Screens/Edit/Editor.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index a9fcf29c51..cb78a19636 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -706,7 +706,10 @@ namespace osu.Game.Screens.Edit fileMenuItems.Add(new EditorMenuItemSpacer()); var beatmapSet = beatmapManager.QueryBeatmapSet(bs => bs.ID == Beatmap.Value.BeatmapSetInfo.ID) ?? playableBeatmap.BeatmapInfo.BeatmapSet; - var difficultyItems = beatmapSet.Beatmaps.Select(b => new EditorMenuItem(b.Version ?? "(unnamed)")).ToList(); + var difficultyItems = beatmapSet.Beatmaps.Select(b => new ToggleMenuItem(b.Version ?? "(unnamed)") + { + State = { Value = playableBeatmap.BeatmapInfo.Equals(b) } + }).ToList(); fileMenuItems.Add(new EditorMenuItem("Change difficulty") { Items = difficultyItems }); From 7befd030dfe3f8fb58c1886696ce07d737743b73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 5 Sep 2021 16:28:32 +0200 Subject: [PATCH 1700/2442] Minimal working example of switching difficulties --- osu.Game/Screens/Edit/Editor.cs | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index cb78a19636..c7ac232f58 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -35,7 +35,9 @@ using osu.Game.Screens.Edit.Design; using osu.Game.Screens.Edit.Setup; using osu.Game.Screens.Edit.Timing; using osu.Game.Screens.Edit.Verify; +using osu.Game.Screens.Menu; using osu.Game.Screens.Play; +using osu.Game.Screens.Select; using osu.Game.Users; using osuTK.Graphics; using osuTK.Input; @@ -101,6 +103,9 @@ namespace osu.Game.Screens.Edit [Resolved] private MusicController music { get; set; } + [Resolved(CanBeNull = true)] + private OsuGame game { get; set; } + [BackgroundDependencyLoader] private void load(OsuColour colours, OsuConfigManager config) { @@ -706,10 +711,7 @@ namespace osu.Game.Screens.Edit fileMenuItems.Add(new EditorMenuItemSpacer()); var beatmapSet = beatmapManager.QueryBeatmapSet(bs => bs.ID == Beatmap.Value.BeatmapSetInfo.ID) ?? playableBeatmap.BeatmapInfo.BeatmapSet; - var difficultyItems = beatmapSet.Beatmaps.Select(b => new ToggleMenuItem(b.Version ?? "(unnamed)") - { - State = { Value = playableBeatmap.BeatmapInfo.Equals(b) } - }).ToList(); + var difficultyItems = beatmapSet.Beatmaps.Select(createDifficultyMenuItem).ToList(); fileMenuItems.Add(new EditorMenuItem("Change difficulty") { Items = difficultyItems }); @@ -718,6 +720,28 @@ namespace osu.Game.Screens.Edit return fileMenuItems; } + private ToggleMenuItem createDifficultyMenuItem(BeatmapInfo b) + { + bool isCurrentDifficulty = playableBeatmap.BeatmapInfo.Equals(b); + + var menuItem = new ToggleMenuItem(b.Version ?? "(unnamed)") { State = { Value = isCurrentDifficulty }, }; + + if (!isCurrentDifficulty) + { + menuItem.Action.Value = () => + { + game?.PerformFromScreen(screen => + { + var osuScreen = (OsuScreen)screen; + osuScreen.Beatmap.Value = beatmapManager.GetWorkingBeatmap(b); + screen.Push(new Editor()); + }, new[] { typeof(MainMenu), typeof(SongSelect) }); + }; + } + + return menuItem; + } + public double SnapTime(double time, double? referenceTime) => editorBeatmap.SnapTime(time, referenceTime); public double GetBeatLengthAtTime(double referenceTime) => editorBeatmap.GetBeatLengthAtTime(referenceTime); From fe2520c599b59e5f66e0aefba32c167c98f72052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 5 Sep 2021 16:59:28 +0200 Subject: [PATCH 1701/2442] Add intermediary screen to avoid going back to menus --- osu.Game/Screens/Edit/Editor.cs | 21 +++++++++++++++------ osu.Game/Screens/Edit/EditorLoader.cs | 27 +++++++++++++++++++++++++++ osu.Game/Screens/Menu/MainMenu.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 2 +- 4 files changed, 44 insertions(+), 8 deletions(-) create mode 100644 osu.Game/Screens/Edit/EditorLoader.cs diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index c7ac232f58..bc6d26135b 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using JetBrains.Annotations; using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -35,9 +36,7 @@ using osu.Game.Screens.Edit.Design; using osu.Game.Screens.Edit.Setup; using osu.Game.Screens.Edit.Timing; using osu.Game.Screens.Edit.Verify; -using osu.Game.Screens.Menu; using osu.Game.Screens.Play; -using osu.Game.Screens.Select; using osu.Game.Users; using osuTK.Graphics; using osuTK.Input; @@ -77,6 +76,9 @@ namespace osu.Game.Screens.Edit private Container screenContainer; + [CanBeNull] + private readonly EditorLoader loader; + private EditorScreen currentScreen; private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); @@ -106,6 +108,11 @@ namespace osu.Game.Screens.Edit [Resolved(CanBeNull = true)] private OsuGame game { get; set; } + public Editor(EditorLoader loader = null) + { + this.loader = loader; + } + [BackgroundDependencyLoader] private void load(OsuColour colours, OsuConfigManager config) { @@ -730,12 +737,14 @@ namespace osu.Game.Screens.Edit { menuItem.Action.Value = () => { + if (loader != null) + loader.ValidForResume = true; + game?.PerformFromScreen(screen => { - var osuScreen = (OsuScreen)screen; - osuScreen.Beatmap.Value = beatmapManager.GetWorkingBeatmap(b); - screen.Push(new Editor()); - }, new[] { typeof(MainMenu), typeof(SongSelect) }); + if (screen != null && screen == loader) + loader.PushEditor(); + }, new[] { typeof(EditorLoader) }); }; } diff --git a/osu.Game/Screens/Edit/EditorLoader.cs b/osu.Game/Screens/Edit/EditorLoader.cs new file mode 100644 index 0000000000..5c8d0f5b16 --- /dev/null +++ b/osu.Game/Screens/Edit/EditorLoader.cs @@ -0,0 +1,27 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Screens; +using osu.Game.Screens.Play; + +namespace osu.Game.Screens.Edit +{ + /// + /// Transition screen for the editor. + /// Used to avoid backing out to main menu/song select when switching difficulties from within the editor. + /// + public class EditorLoader : ScreenWithBeatmapBackground + { + public override void OnEntering(IScreen last) + { + base.OnEntering(last); + PushEditor(); + } + + public void PushEditor() + { + this.Push(new Editor(this)); + ValidForResume = false; + } + } +} diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 1d0182a945..8b2ec43e3e 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -103,7 +103,7 @@ namespace osu.Game.Screens.Menu OnEdit = delegate { Beatmap.SetDefault(); - this.Push(new Editor()); + this.Push(new EditorLoader()); }, OnSolo = loadSoloSongSelect, OnMultiplayer = () => this.Push(new Multiplayer()), diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index b3d715e580..f11f9fd614 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -349,7 +349,7 @@ namespace osu.Game.Screens.Select throw new InvalidOperationException($"Attempted to edit when {nameof(AllowEditing)} is disabled"); Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap ?? beatmapNoDebounce); - this.Push(new Editor()); + this.Push(new EditorLoader()); } /// From c397cc2027ceb14e216daa075263e2f52588de0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 5 Sep 2021 17:26:09 +0200 Subject: [PATCH 1702/2442] Restructure proof of concept --- .../Components/Menus/DifficultyMenuItem.cs | 24 +++++++++++++++ osu.Game/Screens/Edit/Editor.cs | 30 ++++++++----------- osu.Game/Screens/Edit/EditorLoader.cs | 11 ++++++- 3 files changed, 47 insertions(+), 18 deletions(-) create mode 100644 osu.Game/Screens/Edit/Components/Menus/DifficultyMenuItem.cs diff --git a/osu.Game/Screens/Edit/Components/Menus/DifficultyMenuItem.cs b/osu.Game/Screens/Edit/Components/Menus/DifficultyMenuItem.cs new file mode 100644 index 0000000000..74b18f63ab --- /dev/null +++ b/osu.Game/Screens/Edit/Components/Menus/DifficultyMenuItem.cs @@ -0,0 +1,24 @@ +// 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 osu.Framework.Graphics.Sprites; +using osu.Game.Beatmaps; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Screens.Edit.Components.Menus +{ + public class DifficultyMenuItem : StatefulMenuItem + { + public DifficultyMenuItem(BeatmapInfo beatmapInfo, bool selected, Action difficultyChangeFunc) + : base(beatmapInfo.Version ?? "(unnamed)", null) + { + State.Value = selected; + + if (!selected) + Action.Value = () => difficultyChangeFunc.Invoke(beatmapInfo); + } + + public override IconUsage? GetIconForState(bool state) => state ? (IconUsage?)FontAwesome.Solid.Check : null; + } +} diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index bc6d26135b..f30f92b602 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -727,28 +727,24 @@ namespace osu.Game.Screens.Edit return fileMenuItems; } - private ToggleMenuItem createDifficultyMenuItem(BeatmapInfo b) + private DifficultyMenuItem createDifficultyMenuItem(BeatmapInfo beatmapInfo) { - bool isCurrentDifficulty = playableBeatmap.BeatmapInfo.Equals(b); + bool isCurrentDifficulty = playableBeatmap.BeatmapInfo.Equals(beatmapInfo); + return new DifficultyMenuItem(beatmapInfo, isCurrentDifficulty, switchToDifficulty); + } - var menuItem = new ToggleMenuItem(b.Version ?? "(unnamed)") { State = { Value = isCurrentDifficulty }, }; + private void switchToDifficulty(BeatmapInfo beatmapInfo) + { + if (loader != null) + loader.ValidForResume = true; - if (!isCurrentDifficulty) + game?.PerformFromScreen(screen => { - menuItem.Action.Value = () => - { - if (loader != null) - loader.ValidForResume = true; + if (screen == null || screen != loader) + return; - game?.PerformFromScreen(screen => - { - if (screen != null && screen == loader) - loader.PushEditor(); - }, new[] { typeof(EditorLoader) }); - }; - } - - return menuItem; + loader.PushEditor(beatmapInfo); + }, new[] { typeof(EditorLoader) }); } public double SnapTime(double time, double? referenceTime) => editorBeatmap.SnapTime(time, referenceTime); diff --git a/osu.Game/Screens/Edit/EditorLoader.cs b/osu.Game/Screens/Edit/EditorLoader.cs index 5c8d0f5b16..45f2d1bffd 100644 --- a/osu.Game/Screens/Edit/EditorLoader.cs +++ b/osu.Game/Screens/Edit/EditorLoader.cs @@ -1,7 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using JetBrains.Annotations; +using osu.Framework.Allocation; using osu.Framework.Screens; +using osu.Game.Beatmaps; using osu.Game.Screens.Play; namespace osu.Game.Screens.Edit @@ -12,14 +15,20 @@ namespace osu.Game.Screens.Edit /// public class EditorLoader : ScreenWithBeatmapBackground { + [Resolved] + private BeatmapManager beatmapManager { get; set; } + public override void OnEntering(IScreen last) { base.OnEntering(last); PushEditor(); } - public void PushEditor() + public void PushEditor([CanBeNull] BeatmapInfo beatmapInfo = null) { + if (beatmapInfo != null) + Beatmap.Value = beatmapManager.GetWorkingBeatmap(beatmapInfo); + this.Push(new Editor(this)); ValidForResume = false; } From a9403b65b3eccda6ac7f14826bb90efff5c71555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 5 Sep 2021 17:53:57 +0200 Subject: [PATCH 1703/2442] Eliminate dependency on OsuGame --- osu.Game/Screens/Edit/Editor.cs | 17 +++++------------ osu.Game/Screens/Edit/EditorLoader.cs | 4 ++-- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index f30f92b602..8e845bac05 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -105,9 +105,6 @@ namespace osu.Game.Screens.Edit [Resolved] private MusicController music { get; set; } - [Resolved(CanBeNull = true)] - private OsuGame game { get; set; } - public Editor(EditorLoader loader = null) { this.loader = loader; @@ -735,16 +732,12 @@ namespace osu.Game.Screens.Edit private void switchToDifficulty(BeatmapInfo beatmapInfo) { - if (loader != null) - loader.ValidForResume = true; + if (loader == null) + return; - game?.PerformFromScreen(screen => - { - if (screen == null || screen != loader) - return; - - loader.PushEditor(beatmapInfo); - }, new[] { typeof(EditorLoader) }); + loader.ValidForResume = true; + this.Exit(); + loader.PushEditor(beatmapInfo); } public double SnapTime(double time, double? referenceTime) => editorBeatmap.SnapTime(time, referenceTime); diff --git a/osu.Game/Screens/Edit/EditorLoader.cs b/osu.Game/Screens/Edit/EditorLoader.cs index 45f2d1bffd..011f40419c 100644 --- a/osu.Game/Screens/Edit/EditorLoader.cs +++ b/osu.Game/Screens/Edit/EditorLoader.cs @@ -24,13 +24,13 @@ namespace osu.Game.Screens.Edit PushEditor(); } - public void PushEditor([CanBeNull] BeatmapInfo beatmapInfo = null) + public void PushEditor([CanBeNull] BeatmapInfo beatmapInfo = null) => Schedule(() => { if (beatmapInfo != null) Beatmap.Value = beatmapManager.GetWorkingBeatmap(beatmapInfo); this.Push(new Editor(this)); ValidForResume = false; - } + }); } } From c72523bc14afe2b96ad0fb3a298b0edc9d8f5a1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 5 Sep 2021 18:32:38 +0200 Subject: [PATCH 1704/2442] Add basic test for difficulty switching --- .../Editing/TestSceneDifficultySwitching.cs | 91 +++++++++++++++++++ .../Components/Menus/DifficultyMenuItem.cs | 3 + 2 files changed, 94 insertions(+) create mode 100644 osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs new file mode 100644 index 0000000000..4ac2e9cfef --- /dev/null +++ b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs @@ -0,0 +1,91 @@ +// 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.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Graphics.UserInterface; +using osu.Game.Screens.Edit; +using osu.Game.Screens.Edit.Components.Menus; +using osu.Game.Tests.Beatmaps.IO; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Editing +{ + public class TestSceneDifficultySwitching : ScreenTestScene + { + private BeatmapSetInfo importedBeatmapSet; + + [Resolved] + private OsuGameBase game { get; set; } + + private Editor editor; + + [Resolved] + private BeatmapManager beatmaps { get; set; } + + [SetUpSteps] + public void SetUp() + { + AddStep("import test beatmap", () => importedBeatmapSet = ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).Result); + + AddStep("set current beatmap", () => Beatmap.Value = beatmaps.GetWorkingBeatmap(importedBeatmapSet.Beatmaps.First())); + AddStep("push loader", () => Stack.Push(new EditorLoader())); + + AddUntilStep("wait for editor to load", () => Stack.CurrentScreen is Editor); + AddStep("store editor", () => editor = (Editor)Stack.CurrentScreen); + } + + [Test] + public void TestBasicSwitch() + { + BeatmapInfo targetDifficulty = null; + + AddStep("set target difficulty", () => targetDifficulty = importedBeatmapSet.Beatmaps.Last(beatmap => !beatmap.Equals(Beatmap.Value.BeatmapInfo))); + switchToDifficulty(() => targetDifficulty); + confirmEditingBeatmap(() => targetDifficulty); + + AddStep("exit editor", () => Stack.Exit()); + // ensure editor loader didn't resume. + AddAssert("stack empty", () => Stack.CurrentScreen == null); + } + + private void switchToDifficulty(Func difficulty) + { + AddUntilStep("wait for menubar to load", () => editor.ChildrenOfType().Any()); + AddStep("open file menu", () => + { + var menuBar = editor.ChildrenOfType().Single(); + var fileMenu = menuBar.ChildrenOfType().First(); + InputManager.MoveMouseTo(fileMenu); + InputManager.Click(MouseButton.Left); + }); + + AddStep("open difficulty menu", () => + { + var difficultySelector = + editor.ChildrenOfType().Single(item => item.Item.Text.Value.ToString().Contains("Change difficulty")); + InputManager.MoveMouseTo(difficultySelector); + }); + AddWaitStep("wait for open", 3); + + AddStep("switch to target difficulty", () => + { + var difficultyMenuItem = + editor.ChildrenOfType() + .Last(item => item.Item is DifficultyMenuItem difficultyItem && difficultyItem.Beatmap.Equals(difficulty.Invoke())); + InputManager.MoveMouseTo(difficultyMenuItem); + InputManager.Click(MouseButton.Left); + }); + } + + private void confirmEditingBeatmap(Func targetDifficulty) + { + AddUntilStep("current beatmap is correct", () => Beatmap.Value.BeatmapInfo.Equals(targetDifficulty.Invoke())); + AddUntilStep("current screen is editor", () => Stack.CurrentScreen is Editor); + } + } +} diff --git a/osu.Game/Screens/Edit/Components/Menus/DifficultyMenuItem.cs b/osu.Game/Screens/Edit/Components/Menus/DifficultyMenuItem.cs index 74b18f63ab..5f9b72447b 100644 --- a/osu.Game/Screens/Edit/Components/Menus/DifficultyMenuItem.cs +++ b/osu.Game/Screens/Edit/Components/Menus/DifficultyMenuItem.cs @@ -10,9 +10,12 @@ namespace osu.Game.Screens.Edit.Components.Menus { public class DifficultyMenuItem : StatefulMenuItem { + public BeatmapInfo Beatmap { get; } + public DifficultyMenuItem(BeatmapInfo beatmapInfo, bool selected, Action difficultyChangeFunc) : base(beatmapInfo.Version ?? "(unnamed)", null) { + Beatmap = beatmapInfo; State.Value = selected; if (!selected) From 382269b362061da851565b88092c08b923eeadbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 5 Sep 2021 19:11:46 +0200 Subject: [PATCH 1705/2442] Test staying on same difficulty due to unsaved changes --- .../Editing/TestSceneDifficultySwitching.cs | 37 ++++++++++++++++++- osu.Game/Screens/Edit/Editor.cs | 13 ++++++- osu.Game/Screens/Edit/EditorLoader.cs | 25 ++++++++++--- osu.Game/Screens/Edit/PromptForSaveDialog.cs | 3 +- 4 files changed, 68 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs index 4ac2e9cfef..60c2518a7e 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs @@ -8,6 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Dialog; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Components.Menus; using osu.Game.Tests.Beatmaps.IO; @@ -35,8 +36,9 @@ namespace osu.Game.Tests.Visual.Editing AddStep("set current beatmap", () => Beatmap.Value = beatmaps.GetWorkingBeatmap(importedBeatmapSet.Beatmaps.First())); AddStep("push loader", () => Stack.Push(new EditorLoader())); - AddUntilStep("wait for editor to load", () => Stack.CurrentScreen is Editor); + AddUntilStep("wait for editor push", () => Stack.CurrentScreen is Editor); AddStep("store editor", () => editor = (Editor)Stack.CurrentScreen); + AddUntilStep("wait for editor to load", () => editor.IsLoaded); } [Test] @@ -53,6 +55,39 @@ namespace osu.Game.Tests.Visual.Editing AddAssert("stack empty", () => Stack.CurrentScreen == null); } + [Test] + public void TestPreventSwitchDueToUnsavedChanges() + { + BeatmapInfo targetDifficulty = null; + PromptForSaveDialog saveDialog = null; + + AddStep("remove first hitobject", () => + { + var editorBeatmap = editor.ChildrenOfType().Single(); + editorBeatmap.RemoveAt(0); + }); + + AddStep("set target difficulty", () => targetDifficulty = importedBeatmapSet.Beatmaps.Last(beatmap => !beatmap.Equals(Beatmap.Value.BeatmapInfo))); + switchToDifficulty(() => targetDifficulty); + + AddUntilStep("prompt for save dialog shown", () => + { + saveDialog = this.ChildrenOfType().Single(); + return saveDialog != null; + }); + AddStep("continue editing", () => + { + var continueButton = saveDialog.ChildrenOfType().Last(); + continueButton.TriggerClick(); + }); + + confirmEditingBeatmap(() => importedBeatmapSet.Beatmaps.First()); + + AddRepeatStep("exit editor forcefully", () => Stack.Exit(), 2); + // ensure editor loader didn't resume. + AddAssert("stack empty", () => Stack.CurrentScreen == null); + } + private void switchToDifficulty(Func difficulty) { AddUntilStep("wait for menubar to load", () => editor.ChildrenOfType().Any()); diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 8e845bac05..df08e0a017 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -498,7 +498,7 @@ namespace osu.Game.Screens.Edit if (isNewBeatmap || HasUnsavedChanges) { - dialogOverlay?.Push(new PromptForSaveDialog(confirmExit, confirmExitWithSave)); + dialogOverlay?.Push(new PromptForSaveDialog(confirmExit, confirmExitWithSave, cancelPendingDifficultySwitch)); return true; } } @@ -737,7 +737,16 @@ namespace osu.Game.Screens.Edit loader.ValidForResume = true; this.Exit(); - loader.PushEditor(beatmapInfo); + loader.ScheduleDifficultySwitch(beatmapInfo); + } + + private void cancelPendingDifficultySwitch() + { + if (loader == null) + return; + + loader.ValidForResume = false; + loader.CancelDifficultySwitch(); } public double SnapTime(double time, double? referenceTime) => editorBeatmap.SnapTime(time, referenceTime); diff --git a/osu.Game/Screens/Edit/EditorLoader.cs b/osu.Game/Screens/Edit/EditorLoader.cs index 011f40419c..d913076e83 100644 --- a/osu.Game/Screens/Edit/EditorLoader.cs +++ b/osu.Game/Screens/Edit/EditorLoader.cs @@ -4,6 +4,7 @@ using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Screens; +using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Screens.Play; @@ -18,19 +19,31 @@ namespace osu.Game.Screens.Edit [Resolved] private BeatmapManager beatmapManager { get; set; } + [CanBeNull] + private ScheduledDelegate scheduledDifficultySwitch; + public override void OnEntering(IScreen last) { base.OnEntering(last); - PushEditor(); + pushEditor(); } - public void PushEditor([CanBeNull] BeatmapInfo beatmapInfo = null) => Schedule(() => + private void pushEditor() { - if (beatmapInfo != null) - Beatmap.Value = beatmapManager.GetWorkingBeatmap(beatmapInfo); - this.Push(new Editor(this)); ValidForResume = false; - }); + } + + public void ScheduleDifficultySwitch(BeatmapInfo beatmapInfo) + { + CancelDifficultySwitch(); + scheduledDifficultySwitch = Schedule(() => + { + Beatmap.Value = beatmapManager.GetWorkingBeatmap(beatmapInfo); + pushEditor(); + }); + } + + public void CancelDifficultySwitch() => scheduledDifficultySwitch?.Cancel(); } } diff --git a/osu.Game/Screens/Edit/PromptForSaveDialog.cs b/osu.Game/Screens/Edit/PromptForSaveDialog.cs index 16504b47bd..e308a9533d 100644 --- a/osu.Game/Screens/Edit/PromptForSaveDialog.cs +++ b/osu.Game/Screens/Edit/PromptForSaveDialog.cs @@ -9,7 +9,7 @@ namespace osu.Game.Screens.Edit { public class PromptForSaveDialog : PopupDialog { - public PromptForSaveDialog(Action exit, Action saveAndExit) + public PromptForSaveDialog(Action exit, Action saveAndExit, Action cancel) { HeaderText = "Did you want to save your changes?"; @@ -30,6 +30,7 @@ namespace osu.Game.Screens.Edit new PopupDialogCancelButton { Text = @"Oops, continue editing", + Action = cancel }, }; } From 74a129dc27fd3c0bc6ce6e5919106f7a1b9e45ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 5 Sep 2021 19:20:18 +0200 Subject: [PATCH 1706/2442] Test switching difficulties after discarding changes --- .../Editing/TestSceneDifficultySwitching.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs index 60c2518a7e..ecf1fec850 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs @@ -88,6 +88,39 @@ namespace osu.Game.Tests.Visual.Editing AddAssert("stack empty", () => Stack.CurrentScreen == null); } + [Test] + public void TestAllowSwitchAfterDiscardingUnsavedChanges() + { + BeatmapInfo targetDifficulty = null; + PromptForSaveDialog saveDialog = null; + + AddStep("remove first hitobject", () => + { + var editorBeatmap = editor.ChildrenOfType().Single(); + editorBeatmap.RemoveAt(0); + }); + + AddStep("set target difficulty", () => targetDifficulty = importedBeatmapSet.Beatmaps.Last(beatmap => !beatmap.Equals(Beatmap.Value.BeatmapInfo))); + switchToDifficulty(() => targetDifficulty); + + AddUntilStep("prompt for save dialog shown", () => + { + saveDialog = this.ChildrenOfType().Single(); + return saveDialog != null; + }); + AddStep("discard changes", () => + { + var continueButton = saveDialog.ChildrenOfType().Single(); + continueButton.TriggerClick(); + }); + + confirmEditingBeatmap(() => targetDifficulty); + + AddStep("exit editor forcefully", () => Stack.Exit()); + // ensure editor loader didn't resume. + AddAssert("stack empty", () => Stack.CurrentScreen == null); + } + private void switchToDifficulty(Func difficulty) { AddUntilStep("wait for menubar to load", () => editor.ChildrenOfType().Any()); From 7012a1d9340a01011cbc0e3ef190f3f0ec8030d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 5 Sep 2021 20:24:07 +0200 Subject: [PATCH 1707/2442] Fix issues with main menu -> editor loader transition --- .../Editing/TestSceneDifficultySwitching.cs | 15 +++++++++++-- osu.Game/Screens/Edit/EditorLoader.cs | 22 ++++++++++++++----- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs index ecf1fec850..0f3d413a7d 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs @@ -11,6 +11,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Dialog; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Components.Menus; +using osu.Game.Screens.Menu; using osu.Game.Tests.Beatmaps.IO; using osuTK.Input; @@ -19,15 +20,25 @@ namespace osu.Game.Tests.Visual.Editing public class TestSceneDifficultySwitching : ScreenTestScene { private BeatmapSetInfo importedBeatmapSet; + private Editor editor; + + // required for screen transitions to work properly + // (see comment in EditorLoader.LogoArriving). + [Cached] + private OsuLogo logo = new OsuLogo + { + Alpha = 0 + }; [Resolved] private OsuGameBase game { get; set; } - private Editor editor; - [Resolved] private BeatmapManager beatmaps { get; set; } + [BackgroundDependencyLoader] + private void load() => Add(logo); + [SetUpSteps] public void SetUp() { diff --git a/osu.Game/Screens/Edit/EditorLoader.cs b/osu.Game/Screens/Edit/EditorLoader.cs index d913076e83..07eb5e5b1b 100644 --- a/osu.Game/Screens/Edit/EditorLoader.cs +++ b/osu.Game/Screens/Edit/EditorLoader.cs @@ -3,10 +3,11 @@ using JetBrains.Annotations; using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Framework.Threading; using osu.Game.Beatmaps; -using osu.Game.Screens.Play; +using osu.Game.Screens.Menu; namespace osu.Game.Screens.Edit { @@ -14,18 +15,29 @@ namespace osu.Game.Screens.Edit /// Transition screen for the editor. /// Used to avoid backing out to main menu/song select when switching difficulties from within the editor. /// - public class EditorLoader : ScreenWithBeatmapBackground + public class EditorLoader : OsuScreen { + public override float BackgroundParallaxAmount => 0.1f; + + public override bool AllowBackButton => false; + + public override bool HideOverlaysOnEnter => true; + + public override bool DisallowExternalBeatmapRulesetChanges => true; + [Resolved] private BeatmapManager beatmapManager { get; set; } [CanBeNull] private ScheduledDelegate scheduledDifficultySwitch; - public override void OnEntering(IScreen last) + protected override void LogoArriving(OsuLogo logo, bool resuming) { - base.OnEntering(last); - pushEditor(); + base.LogoArriving(logo, resuming); + // the push cannot happen in OnEntering() or similar (even if scheduled), because the transition from main menu will look bad. + // that is because this screen pushing the editor makes it no longer current, and OsuScreen checks if the screen is current + // before enqueueing this screen's LogoArriving onto the logo animation sequence. + logo.Delay(300).Schedule(pushEditor); } private void pushEditor() From d6a47fd99ceeb2f4a707da8c11ed66c940fc787e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 5 Sep 2021 21:00:19 +0200 Subject: [PATCH 1708/2442] Sort difficulties by ruleset and star rating in menu --- osu.Game/Screens/Edit/Editor.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index df08e0a017..7d0b936df9 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -715,7 +715,17 @@ namespace osu.Game.Screens.Edit fileMenuItems.Add(new EditorMenuItemSpacer()); var beatmapSet = beatmapManager.QueryBeatmapSet(bs => bs.ID == Beatmap.Value.BeatmapSetInfo.ID) ?? playableBeatmap.BeatmapInfo.BeatmapSet; - var difficultyItems = beatmapSet.Beatmaps.Select(createDifficultyMenuItem).ToList(); + + var difficultyItems = new List(); + + foreach (var rulesetBeatmaps in beatmapSet.Beatmaps.GroupBy(b => b.RulesetID).OrderBy(group => group.Key)) + { + if (difficultyItems.Count > 0) + difficultyItems.Add(new EditorMenuItemSpacer()); + + foreach (var beatmap in rulesetBeatmaps.OrderBy(b => b.StarDifficulty)) + difficultyItems.Add(createDifficultyMenuItem(beatmap)); + } fileMenuItems.Add(new EditorMenuItem("Change difficulty") { Items = difficultyItems }); From cb6cee9aea75901d4bcf230e5b1cf8d8f5c13c1a Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Sun, 5 Sep 2021 21:10:08 +0100 Subject: [PATCH 1709/2442] add /query as alias of /chat --- osu.Game/Online/Chat/ChannelManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index f58f1ff40c..d72a050d84 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -257,6 +257,7 @@ namespace osu.Game.Online.Chat break; case "chat": + case "query": if (string.IsNullOrWhiteSpace(content)) { target.AddNewMessages(new ErrorMessage("Usage: /chat [user]")); From 5c385e84ea4988528ff628867c34705d22c889c3 Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Sun, 5 Sep 2021 21:20:19 +0100 Subject: [PATCH 1710/2442] wrong command name in query message --- osu.Game/Online/Chat/ChannelManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index d72a050d84..d2500f0d7f 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -260,7 +260,7 @@ namespace osu.Game.Online.Chat case "query": if (string.IsNullOrWhiteSpace(content)) { - target.AddNewMessages(new ErrorMessage("Usage: /chat [user]")); + target.AddNewMessages(new ErrorMessage($"Usage: /{command} [user]")); break; } From 458cde832da5a75b742fb808637e559de62fad3a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 6 Sep 2021 14:09:40 +0900 Subject: [PATCH 1711/2442] Avoid using SSDQ for validity computation --- .../Settings/TestSceneTabletSettings.cs | 14 +----- .../Sections/Input/TabletAreaSelection.cs | 44 ++++++++++++------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs index 49d6b7033e..2486abdd7a 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs @@ -1,11 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; using NUnit.Framework; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Input.Handlers.Tablet; using osu.Framework.Testing; using osu.Framework.Utils; @@ -93,17 +91,9 @@ namespace osu.Game.Tests.Visual.Settings ensureValid(); } - private void ensureValid() - { - AddUntilStep("wait for transforms", () => settings.AreaSelection.ChildrenOfType().All(c => !c.Transforms.Any())); - AddAssert("area valid", () => settings.AreaSelection.IsWithinBounds); - } + private void ensureValid() => AddAssert("area valid", () => settings.AreaSelection.IsWithinBounds); - private void ensureInvalid() - { - AddUntilStep("wait for transforms", () => settings.AreaSelection.ChildrenOfType().All(c => !c.Transforms.Any())); - AddAssert("area invalid", () => !settings.AreaSelection.IsWithinBounds); - } + private void ensureInvalid() => AddAssert("area invalid", () => !settings.AreaSelection.IsWithinBounds); public class TestTabletHandler : ITabletHandler { diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs index d12052b24d..58abfab29c 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs @@ -114,29 +114,30 @@ namespace osu.Game.Overlays.Settings.Sections.Input areaOffset.BindTo(handler.AreaOffset); areaOffset.BindValueChanged(val => { - usableAreaContainer.MoveTo(val.NewValue, 100, Easing.OutQuint) - .OnComplete(_ => checkBounds()); // required as we are using SSDQ. + usableAreaContainer.MoveTo(val.NewValue, 100, Easing.OutQuint); + checkBounds(); }, true); areaSize.BindTo(handler.AreaSize); areaSize.BindValueChanged(val => { - usableAreaContainer.ResizeTo(val.NewValue, 100, Easing.OutQuint) - .OnComplete(_ => checkBounds()); // required as we are using SSDQ. + usableAreaContainer.ResizeTo(val.NewValue, 100, Easing.OutQuint); int x = (int)val.NewValue.X; int y = (int)val.NewValue.Y; int commonDivider = greatestCommonDivider(x, y); usableAreaText.Text = $"{(float)x / commonDivider}:{(float)y / commonDivider}"; + checkBounds(); }, true); rotation.BindTo(handler.Rotation); rotation.BindValueChanged(val => { - usableAreaContainer.RotateTo(val.NewValue, 100, Easing.OutQuint) - .OnComplete(_ => checkBounds()); // required as we are using SSDQ. + usableAreaContainer.RotateTo(val.NewValue, 100, Easing.OutQuint); tabletContainer.RotateTo(-val.NewValue, 800, Easing.OutQuint); + + checkBounds(); }, true); tablet.BindTo(handler.Tablet); @@ -174,22 +175,33 @@ namespace osu.Game.Overlays.Settings.Sections.Input if (tablet.Value == null) return; - // All of this manual logic is just to get around floating point issues when doing a contains check on the screen quads. - // This is best effort, as it's only used for display purposes. If we need for anything more, manual math on the raw values should be preferred. - var containerQuad = tabletContainer.ScreenSpaceDrawQuad.AABBFloat.Inflate(1); - var usableAreaQuad = Quad.FromRectangle(usableAreaContainer.ScreenSpaceDrawQuad.AABBFloat); + // allow for some degree of floating point error, as we don't care about being perfect here. + const float lenience = 0.5f; + + var tabletArea = new Quad(-lenience, -lenience, tablet.Value.Size.X + lenience * 2, tablet.Value.Size.Y + lenience * 2); + + var halfUsableArea = areaSize.Value / 2; + var offset = areaOffset.Value; + + var usableAreaQuad = new Quad( + new Vector2(-halfUsableArea.X, -halfUsableArea.Y), + new Vector2(halfUsableArea.X, -halfUsableArea.Y), + new Vector2(-halfUsableArea.X, halfUsableArea.Y), + new Vector2(halfUsableArea.X, halfUsableArea.Y) + ); var matrix = Matrix3.Identity; - MatrixExtensions.TranslateFromLeft(ref matrix, usableAreaQuad.Centre); + + MatrixExtensions.TranslateFromLeft(ref matrix, offset); MatrixExtensions.RotateFromLeft(ref matrix, MathUtils.DegreesToRadians(rotation.Value)); - MatrixExtensions.TranslateFromLeft(ref matrix, -usableAreaQuad.Centre); + usableAreaQuad *= matrix; IsWithinBounds = - containerQuad.Contains(usableAreaQuad.TopLeft) && - containerQuad.Contains(usableAreaQuad.TopRight) && - containerQuad.Contains(usableAreaQuad.BottomLeft) && - containerQuad.Contains(usableAreaQuad.BottomRight); + tabletArea.Contains(usableAreaQuad.TopLeft) && + tabletArea.Contains(usableAreaQuad.TopRight) && + tabletArea.Contains(usableAreaQuad.BottomLeft) && + tabletArea.Contains(usableAreaQuad.BottomRight); usableFill.FadeColour(IsWithinBounds ? colour.Blue : colour.RedLight, 100); } From 6f482c3602b3fd2c6801878e0fcb368ba5dd1633 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 6 Sep 2021 14:14:42 +0900 Subject: [PATCH 1712/2442] Add test coverage of sharper aspect ratio --- .../Settings/TestSceneTabletSettings.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs index 2486abdd7a..997eac709d 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using NUnit.Framework; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -8,6 +9,7 @@ using osu.Framework.Input.Handlers.Tablet; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Overlays; +using osu.Game.Overlays.Settings; using osu.Game.Overlays.Settings.Sections.Input; using osuTK; @@ -51,6 +53,27 @@ namespace osu.Game.Tests.Visual.Settings AddStep("Test no tablet present", () => tabletHandler.SetTabletSize(Vector2.Zero)); } + [Test] + public void TestWideAspectRatioValidity() + { + AddStep("Test with wide tablet", () => tabletHandler.SetTabletSize(new Vector2(160, 100))); + + AddStep("Reset to full area", () => settings.ChildrenOfType().First().TriggerClick()); + ensureValid(); + + AddStep("rotate 10", () => tabletHandler.Rotation.Value = 10); + ensureInvalid(); + + AddStep("scale down", () => tabletHandler.AreaSize.Value *= 0.9f); + ensureInvalid(); + + AddStep("scale down", () => tabletHandler.AreaSize.Value *= 0.9f); + ensureInvalid(); + + AddStep("scale down", () => tabletHandler.AreaSize.Value *= 0.9f); + ensureValid(); + } + [Test] public void TestRotationValidity() { From 1c4a3c584a76fb9ecbf7b56d6d62850fcc89b88d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 6 Sep 2021 15:04:27 +0900 Subject: [PATCH 1713/2442] Use correct lookup type to ensure username based lookups always prefer username --- osu.Game/Online/API/Requests/GetUserRequest.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetUserRequest.cs b/osu.Game/Online/API/Requests/GetUserRequest.cs index 281926c096..e49c4ab298 100644 --- a/osu.Game/Online/API/Requests/GetUserRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRequest.cs @@ -8,8 +8,9 @@ namespace osu.Game.Online.API.Requests { public class GetUserRequest : APIRequest { - private readonly string userIdentifier; + private readonly string lookup; public readonly RulesetInfo Ruleset; + private readonly LookupType lookupType; /// /// Gets the currently logged-in user. @@ -25,7 +26,8 @@ namespace osu.Game.Online.API.Requests /// The ruleset to get the user's info for. public GetUserRequest(long? userId = null, RulesetInfo ruleset = null) { - this.userIdentifier = userId.ToString(); + lookup = userId.ToString(); + lookupType = LookupType.Id; Ruleset = ruleset; } @@ -36,10 +38,17 @@ namespace osu.Game.Online.API.Requests /// The ruleset to get the user's info for. public GetUserRequest(string username = null, RulesetInfo ruleset = null) { - this.userIdentifier = username; + lookup = username; + lookupType = LookupType.Username; Ruleset = ruleset; } - protected override string Target => userIdentifier != null ? $@"users/{userIdentifier}/{Ruleset?.ShortName}" : $@"me/{Ruleset?.ShortName}"; + protected override string Target => lookup != null ? $@"users/{lookup}/{Ruleset?.ShortName}?k={lookupType.ToString().ToLower()}" : $@"me/{Ruleset?.ShortName}"; + + private enum LookupType + { + Id, + Username + } } } From 62d65f81fb785ae4656b312f6617f469401349d6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 6 Sep 2021 16:53:09 +0900 Subject: [PATCH 1714/2442] Limit at 10000 entries --- .github/workflows/test-diffcalc.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 3bec11928f..d963e49d9f 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -156,6 +156,7 @@ jobs: AND m.mods = p.mods WHERE abs(m.diff_unified - p.diff_unified) > 0.1 ORDER BY abs(m.diff_unified - p.diff_unified) - DESC;" + DESC + LIMIT 10000;" # Todo: Run ppcalc \ No newline at end of file From 401d38fc051366cb26f3f25bf389decbf5a5bfec Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 6 Sep 2021 19:07:28 +0900 Subject: [PATCH 1715/2442] Fix possible nullref --- osu.Game/Screens/Ranking/ScorePanelList.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index 711e0d60ec..4ac30ef4ed 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -301,6 +301,9 @@ namespace osu.Game.Screens.Ranking protected override bool OnKeyDown(KeyDownEvent e) { + if (expandedPanel == null) + return base.OnKeyDown(e); + var expandedPanelIndex = flow.GetPanelIndex(expandedPanel.Score); switch (e.Key) From 20100b8894d5a58af13e1351628e0366e6256ac5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 6 Sep 2021 20:20:52 +0900 Subject: [PATCH 1716/2442] Fix a few test failures --- .../Visual/Playlists/TestScenePlaylistsResultsScreen.cs | 2 ++ osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs | 8 +++++--- osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs | 5 +++++ .../Visual/UserInterface/TestSceneDeleteLocalScore.cs | 4 ++-- osu.Game/Screens/Ranking/ScorePanelList.cs | 2 ++ 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs index 61d49e4018..b826683cd5 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs @@ -160,6 +160,8 @@ namespace osu.Game.Tests.Visual.Playlists Ruleset = { Value = new OsuRuleset().RulesetInfo } })); }); + + AddUntilStep("wait for load", () => resultsScreen.ChildrenOfType().FirstOrDefault()?.AllPanelsVisible == true); } private void waitForDisplay() diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs index ba6b6bd529..34a9610804 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs @@ -99,7 +99,7 @@ namespace osu.Game.Tests.Visual.Ranking TestResultsScreen screen = null; AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen())); - AddUntilStep("wait for loaded", () => screen.IsLoaded); + AddUntilStep("wait for load", () => this.ChildrenOfType().Single().AllPanelsVisible); AddStep("click expanded panel", () => { @@ -138,7 +138,7 @@ namespace osu.Game.Tests.Visual.Ranking TestResultsScreen screen = null; AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen())); - AddUntilStep("wait for loaded", () => screen.IsLoaded); + AddUntilStep("wait for load", () => this.ChildrenOfType().Single().AllPanelsVisible); AddStep("click expanded panel", () => { @@ -177,7 +177,7 @@ namespace osu.Game.Tests.Visual.Ranking TestResultsScreen screen = null; AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen())); - AddUntilStep("wait for loaded", () => screen.IsLoaded); + AddUntilStep("wait for load", () => this.ChildrenOfType().Single().AllPanelsVisible); ScorePanel expandedPanel = null; ScorePanel contractedPanel = null; @@ -201,6 +201,7 @@ namespace osu.Game.Tests.Visual.Ranking [Test] public void TestFetchScoresAfterShowingStatistics() + { DelayedFetchResultsScreen screen = null; @@ -223,6 +224,7 @@ namespace osu.Game.Tests.Visual.Ranking TestResultsScreen screen = null; AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen())); + AddUntilStep("wait for load", () => this.ChildrenOfType().Single().AllPanelsVisible); AddAssert("download button is disabled", () => !screen.ChildrenOfType().Last().Enabled.Value); diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs index f330e99d55..b2585c0509 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs @@ -159,6 +159,9 @@ namespace osu.Game.Tests.Visual.Ranking var firstScore = new TestScoreInfo(new OsuRuleset().RulesetInfo); var secondScore = new TestScoreInfo(new OsuRuleset().RulesetInfo); + firstScore.User.Username = "A"; + secondScore.User.Username = "B"; + createListStep(() => new ScorePanelList()); AddStep("add scores and select first", () => @@ -168,6 +171,8 @@ namespace osu.Game.Tests.Visual.Ranking list.SelectedScore.Value = firstScore; }); + AddUntilStep("wait for load", () => list.AllPanelsVisible); + assertScoreState(firstScore, true); assertScoreState(secondScore, false); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index 98482601ee..8efee2723f 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -158,14 +158,14 @@ namespace osu.Game.Tests.Visual.UserInterface InputManager.Click(MouseButton.Left); }); - AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s.OnlineScoreID != scores[0].OnlineScoreID)); + AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s != scores[0])); } [Test] public void TestDeleteViaDatabase() { AddStep("delete top score", () => scoreManager.Delete(scores[0])); - AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s.OnlineScoreID != scores[0].OnlineScoreID)); + AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s != scores[0])); } } } diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index 4ac30ef4ed..6d65137984 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -47,6 +47,8 @@ namespace osu.Game.Screens.Ranking /// public bool IsScrolledToEnd => flow.Count > 0 && scroll.ScrollableExtent > 0 && scroll.IsScrolledToEnd(scroll_endpoint_distance); + public bool AllPanelsVisible => flow.All(p => p.IsPresent); + /// /// The current scroll position. /// From 2a5b857f10f51f0d5e2cd3666be3e08db9ed8338 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Sep 2021 00:45:53 +0900 Subject: [PATCH 1717/2442] Avoid loading unnecessary fonts in headless testing --- osu.Game/OsuGameBase.cs | 55 +++++++++++++++------------ osu.Game/Tests/Visual/OsuTestScene.cs | 5 +++ 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 762216e93c..a63e59f3d3 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -205,31 +205,7 @@ namespace osu.Game dependencies.CacheAs(this); dependencies.CacheAs(LocalConfig); - AddFont(Resources, @"Fonts/osuFont"); - - AddFont(Resources, @"Fonts/Torus/Torus-Regular"); - AddFont(Resources, @"Fonts/Torus/Torus-Light"); - AddFont(Resources, @"Fonts/Torus/Torus-SemiBold"); - AddFont(Resources, @"Fonts/Torus/Torus-Bold"); - - AddFont(Resources, @"Fonts/Inter/Inter-Regular"); - AddFont(Resources, @"Fonts/Inter/Inter-RegularItalic"); - AddFont(Resources, @"Fonts/Inter/Inter-Light"); - AddFont(Resources, @"Fonts/Inter/Inter-LightItalic"); - AddFont(Resources, @"Fonts/Inter/Inter-SemiBold"); - AddFont(Resources, @"Fonts/Inter/Inter-SemiBoldItalic"); - AddFont(Resources, @"Fonts/Inter/Inter-Bold"); - AddFont(Resources, @"Fonts/Inter/Inter-BoldItalic"); - - AddFont(Resources, @"Fonts/Noto/Noto-Basic"); - AddFont(Resources, @"Fonts/Noto/Noto-Hangul"); - AddFont(Resources, @"Fonts/Noto/Noto-CJK-Basic"); - AddFont(Resources, @"Fonts/Noto/Noto-CJK-Compatibility"); - AddFont(Resources, @"Fonts/Noto/Noto-Thai"); - - AddFont(Resources, @"Fonts/Venera/Venera-Light"); - AddFont(Resources, @"Fonts/Venera/Venera-Bold"); - AddFont(Resources, @"Fonts/Venera/Venera-Black"); + InitialiseFonts(); Audio.Samples.PlaybackConcurrency = SAMPLE_CONCURRENCY; @@ -368,6 +344,35 @@ namespace osu.Game Ruleset.BindValueChanged(onRulesetChanged); } + protected virtual void InitialiseFonts() + { + AddFont(Resources, @"Fonts/osuFont"); + + AddFont(Resources, @"Fonts/Torus/Torus-Regular"); + AddFont(Resources, @"Fonts/Torus/Torus-Light"); + AddFont(Resources, @"Fonts/Torus/Torus-SemiBold"); + AddFont(Resources, @"Fonts/Torus/Torus-Bold"); + + AddFont(Resources, @"Fonts/Inter/Inter-Regular"); + AddFont(Resources, @"Fonts/Inter/Inter-RegularItalic"); + AddFont(Resources, @"Fonts/Inter/Inter-Light"); + AddFont(Resources, @"Fonts/Inter/Inter-LightItalic"); + AddFont(Resources, @"Fonts/Inter/Inter-SemiBold"); + AddFont(Resources, @"Fonts/Inter/Inter-SemiBoldItalic"); + AddFont(Resources, @"Fonts/Inter/Inter-Bold"); + AddFont(Resources, @"Fonts/Inter/Inter-BoldItalic"); + + AddFont(Resources, @"Fonts/Noto/Noto-Basic"); + AddFont(Resources, @"Fonts/Noto/Noto-Hangul"); + AddFont(Resources, @"Fonts/Noto/Noto-CJK-Basic"); + AddFont(Resources, @"Fonts/Noto/Noto-CJK-Compatibility"); + AddFont(Resources, @"Fonts/Noto/Noto-Thai"); + + AddFont(Resources, @"Fonts/Venera/Venera-Light"); + AddFont(Resources, @"Fonts/Venera/Venera-Bold"); + AddFont(Resources, @"Fonts/Venera/Venera-Black"); + } + private IDisposable blocking; private void updateThreadStateChanged(ValueChangedEvent state) diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index ef9181c8a6..03434961ea 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -367,6 +367,11 @@ namespace osu.Game.Tests.Visual Add(runner = new TestSceneTestRunner.TestRunner()); } + protected override void InitialiseFonts() + { + // skip fonts load as it's not required for testing purposes. + } + public void RunTestBlocking(TestScene test) => runner.RunTestBlocking(test); } } From bd7d6dd35d54ca11c11616ea1b5f97a74e87b2af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 6 Sep 2021 21:27:17 +0200 Subject: [PATCH 1718/2442] Rename method --- osu.Game/Screens/Edit/Editor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 7d0b936df9..d0b50c13e6 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -498,7 +498,7 @@ namespace osu.Game.Screens.Edit if (isNewBeatmap || HasUnsavedChanges) { - dialogOverlay?.Push(new PromptForSaveDialog(confirmExit, confirmExitWithSave, cancelPendingDifficultySwitch)); + dialogOverlay?.Push(new PromptForSaveDialog(confirmExit, confirmExitWithSave, cancelExit)); return true; } } @@ -750,7 +750,7 @@ namespace osu.Game.Screens.Edit loader.ScheduleDifficultySwitch(beatmapInfo); } - private void cancelPendingDifficultySwitch() + private void cancelExit() { if (loader == null) return; From 2d59008f528f441cff81e9144b25bf208da04173 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 6 Sep 2021 21:30:50 +0200 Subject: [PATCH 1719/2442] Move screen management logic to `EditorLoader` --- osu.Game/Screens/Edit/Editor.cs | 19 ++----------------- osu.Game/Screens/Edit/EditorLoader.cs | 11 +++++++++-- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index d0b50c13e6..1b9a94da58 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -740,24 +740,9 @@ namespace osu.Game.Screens.Edit return new DifficultyMenuItem(beatmapInfo, isCurrentDifficulty, switchToDifficulty); } - private void switchToDifficulty(BeatmapInfo beatmapInfo) - { - if (loader == null) - return; + private void switchToDifficulty(BeatmapInfo beatmapInfo) => loader?.ScheduleDifficultySwitch(beatmapInfo); - loader.ValidForResume = true; - this.Exit(); - loader.ScheduleDifficultySwitch(beatmapInfo); - } - - private void cancelExit() - { - if (loader == null) - return; - - loader.ValidForResume = false; - loader.CancelDifficultySwitch(); - } + private void cancelExit() => loader?.CancelPendingDifficultySwitch(); public double SnapTime(double time, double? referenceTime) => editorBeatmap.SnapTime(time, referenceTime); diff --git a/osu.Game/Screens/Edit/EditorLoader.cs b/osu.Game/Screens/Edit/EditorLoader.cs index 07eb5e5b1b..fbb358d217 100644 --- a/osu.Game/Screens/Edit/EditorLoader.cs +++ b/osu.Game/Screens/Edit/EditorLoader.cs @@ -48,7 +48,10 @@ namespace osu.Game.Screens.Edit public void ScheduleDifficultySwitch(BeatmapInfo beatmapInfo) { - CancelDifficultySwitch(); + scheduledDifficultySwitch?.Cancel(); + ValidForResume = true; + + this.MakeCurrent(); scheduledDifficultySwitch = Schedule(() => { Beatmap.Value = beatmapManager.GetWorkingBeatmap(beatmapInfo); @@ -56,6 +59,10 @@ namespace osu.Game.Screens.Edit }); } - public void CancelDifficultySwitch() => scheduledDifficultySwitch?.Cancel(); + public void CancelPendingDifficultySwitch() + { + scheduledDifficultySwitch?.Cancel(); + ValidForResume = false; + } } } From 5b9f37702b72e69e1976e6091bb8754650149135 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 6 Sep 2021 21:31:59 +0200 Subject: [PATCH 1720/2442] Remove unnecessary delay before pushing editor from loader --- osu.Game/Screens/Edit/EditorLoader.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorLoader.cs b/osu.Game/Screens/Edit/EditorLoader.cs index fbb358d217..92420fa7c0 100644 --- a/osu.Game/Screens/Edit/EditorLoader.cs +++ b/osu.Game/Screens/Edit/EditorLoader.cs @@ -3,7 +3,6 @@ using JetBrains.Annotations; using osu.Framework.Allocation; -using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Framework.Threading; using osu.Game.Beatmaps; @@ -34,10 +33,11 @@ namespace osu.Game.Screens.Edit protected override void LogoArriving(OsuLogo logo, bool resuming) { base.LogoArriving(logo, resuming); + // the push cannot happen in OnEntering() or similar (even if scheduled), because the transition from main menu will look bad. // that is because this screen pushing the editor makes it no longer current, and OsuScreen checks if the screen is current // before enqueueing this screen's LogoArriving onto the logo animation sequence. - logo.Delay(300).Schedule(pushEditor); + pushEditor(); } private void pushEditor() From c9325cc41947c5c1b62afe5b3bc249b643f5527b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 7 Sep 2021 14:15:23 +0900 Subject: [PATCH 1721/2442] Fix results screen test scene --- .../Visual/Playlists/TestScenePlaylistsResultsScreen.cs | 1 + osu.Game/Screens/Ranking/ScorePanelList.cs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs index b826683cd5..4bcc887b9f 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs @@ -167,6 +167,7 @@ namespace osu.Game.Tests.Visual.Playlists private void waitForDisplay() { AddUntilStep("wait for request to complete", () => requestComplete); + AddUntilStep("wait for panels to be visible", () => resultsScreen.ChildrenOfType().FirstOrDefault()?.AllPanelsVisible == true); AddWaitStep("wait for display", 5); } diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index 6d65137984..a847df344f 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -40,12 +40,12 @@ namespace osu.Game.Screens.Ranking /// /// Whether this can be scrolled and is currently scrolled to the start. /// - public bool IsScrolledToStart => flow.Count > 0 && scroll.ScrollableExtent > 0 && scroll.Current <= scroll_endpoint_distance; + public bool IsScrolledToStart => flow.Count > 0 && AllPanelsVisible && scroll.ScrollableExtent > 0 && scroll.Current <= scroll_endpoint_distance; /// /// Whether this can be scrolled and is currently scrolled to the end. /// - public bool IsScrolledToEnd => flow.Count > 0 && scroll.ScrollableExtent > 0 && scroll.IsScrolledToEnd(scroll_endpoint_distance); + public bool IsScrolledToEnd => flow.Count > 0 && AllPanelsVisible && scroll.ScrollableExtent > 0 && scroll.IsScrolledToEnd(scroll_endpoint_distance); public bool AllPanelsVisible => flow.All(p => p.IsPresent); From 59aa4dabfda755ffac0a5e9bb4a58d371994394a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Sep 2021 14:33:58 +0900 Subject: [PATCH 1722/2442] Improve code around background screen handling to read better --- osu.Game/Screens/BackgroundScreenStack.cs | 12 +++++++++--- osu.Game/Screens/OsuScreen.cs | 9 +++------ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/BackgroundScreenStack.cs b/osu.Game/Screens/BackgroundScreenStack.cs index 294f23d2ac..9f562a618e 100644 --- a/osu.Game/Screens/BackgroundScreenStack.cs +++ b/osu.Game/Screens/BackgroundScreenStack.cs @@ -17,15 +17,21 @@ namespace osu.Game.Screens Origin = Anchor.Centre; } - public void Push(BackgroundScreen screen) + /// + /// Attempt to push a new background screen to this stack. + /// + /// The screen to attempt to push. + /// Whether the push succeeded. For example, if the existing screen was already of the correct type this will return false. + public bool Push(BackgroundScreen screen) { if (screen == null) - return; + return false; if (EqualityComparer.Default.Equals((BackgroundScreen)CurrentScreen, screen)) - return; + return false; base.Push(screen); + return true; } } } diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index e3fe14a585..9aec2a5c19 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -186,17 +186,14 @@ namespace osu.Game.Screens { applyArrivingDefaults(false); - backgroundStack?.Push(ownedBackground = CreateBackground()); - - background = backgroundStack?.CurrentScreen as BackgroundScreen; - - if (background != ownedBackground) + if (backgroundStack?.Push(ownedBackground = CreateBackground()) != true) { - // background may have not been replaced, at which point we don't want to track the background lifetime. + // If the constructed instance was not actually pushed to the background stack, we don't want to track it unnecessarily. ownedBackground?.Dispose(); ownedBackground = null; } + background = backgroundStack?.CurrentScreen as BackgroundScreen; base.OnEntering(last); } From ddaa95a1ca37e1c264850c4a239e13e83c500cde Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Sep 2021 14:34:18 +0900 Subject: [PATCH 1723/2442] Fix `pushEditor` function running twice on returning to loader --- osu.Game/Screens/Edit/EditorLoader.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorLoader.cs b/osu.Game/Screens/Edit/EditorLoader.cs index 92420fa7c0..8798a7964b 100644 --- a/osu.Game/Screens/Edit/EditorLoader.cs +++ b/osu.Game/Screens/Edit/EditorLoader.cs @@ -34,10 +34,13 @@ namespace osu.Game.Screens.Edit { base.LogoArriving(logo, resuming); - // the push cannot happen in OnEntering() or similar (even if scheduled), because the transition from main menu will look bad. - // that is because this screen pushing the editor makes it no longer current, and OsuScreen checks if the screen is current - // before enqueueing this screen's LogoArriving onto the logo animation sequence. - pushEditor(); + if (!resuming) + { + // the push cannot happen in OnEntering() or similar (even if scheduled), because the transition from main menu will look bad. + // that is because this screen pushing the editor makes it no longer current, and OsuScreen checks if the screen is current + // before enqueueing this screen's LogoArriving onto the logo animation sequence. + pushEditor(); + } } private void pushEditor() From 7921ad451678925d6bf51628cc6015ac0c69c26b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Sep 2021 14:34:47 +0900 Subject: [PATCH 1724/2442] Add loading spinner in case load takes longer than expected --- osu.Game/Screens/Edit/EditorLoader.cs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorLoader.cs b/osu.Game/Screens/Edit/EditorLoader.cs index 8798a7964b..499baeec4f 100644 --- a/osu.Game/Screens/Edit/EditorLoader.cs +++ b/osu.Game/Screens/Edit/EditorLoader.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Screens; using osu.Framework.Threading; using osu.Game.Beatmaps; +using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Menu; namespace osu.Game.Screens.Edit @@ -43,10 +44,16 @@ namespace osu.Game.Screens.Edit } } - private void pushEditor() + [BackgroundDependencyLoader] + private void load() { - this.Push(new Editor(this)); - ValidForResume = false; + AddRangeInternal(new Drawable[] + { + new LoadingSpinner(true) + { + State = { Value = Visibility.Visible }, + } + }); } public void ScheduleDifficultySwitch(BeatmapInfo beatmapInfo) @@ -62,6 +69,12 @@ namespace osu.Game.Screens.Edit }); } + private void pushEditor() + { + this.Push(new Editor(this)); + ValidForResume = false; + } + public void CancelPendingDifficultySwitch() { scheduledDifficultySwitch?.Cancel(); From 9edd010b1d0a52dd233ea41f2e42ea7fb05b808e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Sep 2021 14:34:54 +0900 Subject: [PATCH 1725/2442] Fix unnecessary background screen transition --- osu.Game/Screens/Edit/EditorLoader.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/EditorLoader.cs b/osu.Game/Screens/Edit/EditorLoader.cs index 499baeec4f..aec7d32939 100644 --- a/osu.Game/Screens/Edit/EditorLoader.cs +++ b/osu.Game/Screens/Edit/EditorLoader.cs @@ -3,11 +3,14 @@ using JetBrains.Annotations; using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Menu; +using osu.Game.Screens.Play; namespace osu.Game.Screens.Edit { @@ -15,7 +18,7 @@ namespace osu.Game.Screens.Edit /// Transition screen for the editor. /// Used to avoid backing out to main menu/song select when switching difficulties from within the editor. /// - public class EditorLoader : OsuScreen + public class EditorLoader : ScreenWithBeatmapBackground { public override float BackgroundParallaxAmount => 0.1f; @@ -62,9 +65,16 @@ namespace osu.Game.Screens.Edit ValidForResume = true; this.MakeCurrent(); + scheduledDifficultySwitch = Schedule(() => { Beatmap.Value = beatmapManager.GetWorkingBeatmap(beatmapInfo); + + // This screen is a weird exception to the rule that nothing after song select changes the global beatmap. + // Because of this, we need to update the background stack's beatmap to match. + // If we don't do this, the editor will see a discrepancy and create a new background, along with an unnecessary transition. + ApplyToBackground(b => b.Beatmap = Beatmap.Value); + pushEditor(); }); } From 93da531d135890c7b9addf0570721fdf7e6573da Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Sep 2021 14:33:58 +0900 Subject: [PATCH 1726/2442] Improve code around background screen handling to read better --- osu.Game/Screens/BackgroundScreenStack.cs | 12 +++++++++--- osu.Game/Screens/OsuScreen.cs | 9 +++------ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/BackgroundScreenStack.cs b/osu.Game/Screens/BackgroundScreenStack.cs index 294f23d2ac..9f562a618e 100644 --- a/osu.Game/Screens/BackgroundScreenStack.cs +++ b/osu.Game/Screens/BackgroundScreenStack.cs @@ -17,15 +17,21 @@ namespace osu.Game.Screens Origin = Anchor.Centre; } - public void Push(BackgroundScreen screen) + /// + /// Attempt to push a new background screen to this stack. + /// + /// The screen to attempt to push. + /// Whether the push succeeded. For example, if the existing screen was already of the correct type this will return false. + public bool Push(BackgroundScreen screen) { if (screen == null) - return; + return false; if (EqualityComparer.Default.Equals((BackgroundScreen)CurrentScreen, screen)) - return; + return false; base.Push(screen); + return true; } } } diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index e3fe14a585..9aec2a5c19 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -186,17 +186,14 @@ namespace osu.Game.Screens { applyArrivingDefaults(false); - backgroundStack?.Push(ownedBackground = CreateBackground()); - - background = backgroundStack?.CurrentScreen as BackgroundScreen; - - if (background != ownedBackground) + if (backgroundStack?.Push(ownedBackground = CreateBackground()) != true) { - // background may have not been replaced, at which point we don't want to track the background lifetime. + // If the constructed instance was not actually pushed to the background stack, we don't want to track it unnecessarily. ownedBackground?.Dispose(); ownedBackground = null; } + background = backgroundStack?.CurrentScreen as BackgroundScreen; base.OnEntering(last); } From 4658577b1d272aceba1624b0954ffff11e2cb01f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 7 Sep 2021 15:18:59 +0900 Subject: [PATCH 1727/2442] Factor in total score calculation time in results screen load --- .../Playlists/PlaylistsResultsScreen.cs | 32 ++++++++++++------- osu.Game/Screens/Ranking/ResultsScreen.cs | 14 ++++---- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs index 2b252f9db7..89bc659f63 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs @@ -32,6 +32,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists [Resolved] private IAPIProvider api { get; set; } + [Resolved] + private ScoreManager scoreManager { get; set; } + public PlaylistsResultsScreen(ScoreInfo score, long roomId, PlaylistItem playlistItem, bool allowRetry, bool allowWatchingReplay = true) : base(score, allowRetry, allowWatchingReplay) { @@ -166,23 +169,28 @@ namespace osu.Game.Screens.OnlinePlay.Playlists /// An optional pivot around which the scores were retrieved. private void performSuccessCallback([NotNull] Action> callback, [NotNull] List scores, [CanBeNull] MultiplayerScores pivot = null) { - var scoreInfos = new List(scores.Select(s => s.CreateScoreInfo(playlistItem))); + var scoreInfos = scores.Select(s => s.CreateScoreInfo(playlistItem)).ToArray(); - // Select a score if we don't already have one selected. - // Note: This is done before the callback so that the panel list centres on the selected score before panels are added (eliminating initial scroll). - if (SelectedScore.Value == null) + // Score panels calculate total score before displaying, which can take some time. In order to count that calculation as part of the loading spinner display duration, + // calculate the total scores locally before invoking the success callback. + scoreManager.OrderByTotalScoreAsync(scoreInfos).ContinueWith(_ => Schedule(() => { - Schedule(() => + // Select a score if we don't already have one selected. + // Note: This is done before the callback so that the panel list centres on the selected score before panels are added (eliminating initial scroll). + if (SelectedScore.Value == null) { - // Prefer selecting the local user's score, or otherwise default to the first visible score. - SelectedScore.Value = scoreInfos.FirstOrDefault(s => s.User.Id == api.LocalUser.Value.Id) ?? scoreInfos.FirstOrDefault(); - }); - } + Schedule(() => + { + // Prefer selecting the local user's score, or otherwise default to the first visible score. + SelectedScore.Value = scoreInfos.FirstOrDefault(s => s.User.Id == api.LocalUser.Value.Id) ?? scoreInfos.FirstOrDefault(); + }); + } - // Invoke callback to add the scores. Exclude the user's current score which was added previously. - callback.Invoke(scoreInfos.Where(s => s.OnlineScoreID != Score?.OnlineScoreID)); + // Invoke callback to add the scores. Exclude the user's current score which was added previously. + callback.Invoke(scoreInfos.Where(s => s.OnlineScoreID != Score?.OnlineScoreID)); - hideLoadingSpinners(pivot); + hideLoadingSpinners(pivot); + })); } private void hideLoadingSpinners([CanBeNull] MultiplayerScores pivot = null) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index d44d1f2cc9..822ee1cf90 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -52,8 +52,7 @@ namespace osu.Game.Screens.Ranking private Drawable bottomPanel; private Container detachedPanelContainer; - private bool fetchedInitialScores; - private APIRequest nextPageRequest; + private bool lastFetchCompleted; private readonly bool allowRetry; private readonly bool allowWatchingReplay; @@ -191,8 +190,10 @@ namespace osu.Game.Screens.Ranking { base.Update(); - if (fetchedInitialScores && nextPageRequest == null) + if (lastFetchCompleted) { + APIRequest nextPageRequest = null; + if (ScorePanelList.IsScrolledToStart) nextPageRequest = FetchNextPage(-1, fetchScoresCallback); else if (ScorePanelList.IsScrolledToEnd) @@ -200,10 +201,7 @@ namespace osu.Game.Screens.Ranking if (nextPageRequest != null) { - // Scheduled after children to give the list a chance to update its scroll position and not potentially trigger a second request too early. - nextPageRequest.Success += () => ScheduleAfterChildren(() => nextPageRequest = null); - nextPageRequest.Failure += _ => ScheduleAfterChildren(() => nextPageRequest = null); - + lastFetchCompleted = false; api.Queue(nextPageRequest); } } @@ -229,7 +227,7 @@ namespace osu.Game.Screens.Ranking foreach (var s in scores) addScore(s); - fetchedInitialScores = true; + lastFetchCompleted = true; }); public override void OnEntering(IScreen last) From 5b13b566b5151dce86f1f93f54604bbde2f44514 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Sep 2021 15:19:23 +0900 Subject: [PATCH 1728/2442] Reduce startup overhead during default key binding handling --- .../Database/TestRealmKeyBindingStore.cs | 5 +- osu.Game/Input/RealmKeyBindingStore.cs | 69 ++++++++++--------- osu.Game/OsuGameBase.cs | 5 +- 3 files changed, 39 insertions(+), 40 deletions(-) diff --git a/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs b/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs index 642ecf00b8..8be74f1a7c 100644 --- a/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs +++ b/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs @@ -11,6 +11,7 @@ using osu.Framework.Platform; using osu.Game.Database; using osu.Game.Input; using osu.Game.Input.Bindings; +using osu.Game.Rulesets; using Realms; namespace osu.Game.Tests.Database @@ -42,7 +43,7 @@ namespace osu.Game.Tests.Database KeyBindingContainer testContainer = new TestKeyBindingContainer(); - keyBindingStore.Register(testContainer); + keyBindingStore.Register(testContainer, Enumerable.Empty()); Assert.That(queryCount(), Is.EqualTo(3)); @@ -66,7 +67,7 @@ namespace osu.Game.Tests.Database { KeyBindingContainer testContainer = new TestKeyBindingContainer(); - keyBindingStore.Register(testContainer); + keyBindingStore.Register(testContainer, Enumerable.Empty()); using (var primaryUsage = realmContextFactory.GetForRead()) { diff --git a/osu.Game/Input/RealmKeyBindingStore.cs b/osu.Game/Input/RealmKeyBindingStore.cs index 9089169877..03cb4031ca 100644 --- a/osu.Game/Input/RealmKeyBindingStore.cs +++ b/osu.Game/Input/RealmKeyBindingStore.cs @@ -46,52 +46,53 @@ namespace osu.Game.Input } /// - /// Register a new type of , adding default bindings from . + /// Register all defaults for this store. /// /// The container to populate defaults from. - public void Register(KeyBindingContainer container) => insertDefaults(container.DefaultKeyBindings); - - /// - /// Register a ruleset, adding default bindings for each of its variants. - /// - /// The ruleset to populate defaults from. - public void Register(RulesetInfo ruleset) - { - var instance = ruleset.CreateInstance(); - - foreach (var variant in instance.AvailableVariants) - insertDefaults(instance.GetDefaultKeyBindings(variant), ruleset.ID, variant); - } - - private void insertDefaults(IEnumerable defaults, int? rulesetId = null, int? variant = null) + /// The rulesets to populate defaults from. + public void Register(KeyBindingContainer container, IEnumerable rulesets) { using (var usage = realmFactory.GetForWrite()) { - // compare counts in database vs defaults - foreach (var defaultsForAction in defaults.GroupBy(k => k.Action)) + // intentionally flattened to a list rather than querying against the IQueryable, as nullable fields being queried against aren't indexed. + // this is much faster as a result. + var existingBindings = usage.Realm.All().ToList(); + + insertDefaults(usage, existingBindings, container.DefaultKeyBindings); + + foreach (var ruleset in rulesets) { - int existingCount = usage.Realm.All().Count(k => k.RulesetID == rulesetId && k.Variant == variant && k.ActionInt == (int)defaultsForAction.Key); - - if (defaultsForAction.Count() <= existingCount) - continue; - - foreach (var k in defaultsForAction.Skip(existingCount)) - { - // insert any defaults which are missing. - usage.Realm.Add(new RealmKeyBinding - { - KeyCombinationString = k.KeyCombination.ToString(), - ActionInt = (int)k.Action, - RulesetID = rulesetId, - Variant = variant - }); - } + var instance = ruleset.CreateInstance(); + foreach (var variant in instance.AvailableVariants) + insertDefaults(usage, existingBindings, instance.GetDefaultKeyBindings(variant), ruleset.ID, variant); } usage.Commit(); } } + private void insertDefaults(RealmContextFactory.RealmUsage usage, List existingBindings, IEnumerable defaults, int? rulesetId = null, int? variant = null) + { + // compare counts in database vs defaults for each action type. + foreach (var defaultsForAction in defaults.GroupBy(k => k.Action)) + { + // avoid performing redundant queries when the database is empty and needs to be re-filled. + int existingCount = existingBindings.Count(k => k.RulesetID == rulesetId && k.Variant == variant && k.ActionInt == (int)defaultsForAction.Key); + + if (defaultsForAction.Count() <= existingCount) + continue; + + // insert any defaults which are missing. + usage.Realm.Add(defaultsForAction.Skip(existingCount).Select(k => new RealmKeyBinding + { + KeyCombinationString = k.KeyCombination.ToString(), + ActionInt = (int)k.Action, + RulesetID = rulesetId, + Variant = variant + })); + } + } + /// /// Keys which should not be allowed for gameplay input purposes. /// diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 762216e93c..8563c4e171 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -351,10 +351,7 @@ namespace osu.Game base.Content.Add(CreateScalingContainer().WithChildren(mainContent)); KeyBindingStore = new RealmKeyBindingStore(realmFactory); - KeyBindingStore.Register(globalBindings); - - foreach (var r in RulesetStore.AvailableRulesets) - KeyBindingStore.Register(r); + KeyBindingStore.Register(globalBindings, RulesetStore.AvailableRulesets); dependencies.Cache(globalBindings); From 44b1af5ae4419495dcba68f6472b609e43656726 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Sep 2021 15:28:52 +0900 Subject: [PATCH 1729/2442] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 8a9bf1b9cd..05367c00f6 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ebe3de6ea4..ae423bac8c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 1714bae53c..be737392e1 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From 61f819b66d16f0d65a14f767cd66d70d13379f00 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 7 Sep 2021 15:51:57 +0900 Subject: [PATCH 1730/2442] Add COE and better PR condition --- .github/workflows/test-diffcalc.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index d963e49d9f..efa36712be 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -22,12 +22,13 @@ jobs: runs-on: ubuntu-latest if: | - contains(github.event.comment.html_url, '/pull/') && + ${{ github.event.issue.pull_request }} && contains(github.event.comment.body, '!pp check') && ${{ github.event.comment.author_association == 'MEMBER' }} strategy: fail-fast: false + continue-on-error: true matrix: ruleset: - { name: osu, id: 0 } From 842696c388a6b2d1401b4a41fbe1d39af53491d1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 7 Sep 2021 15:54:58 +0900 Subject: [PATCH 1731/2442] Fix incorrect definition --- .github/workflows/test-diffcalc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index efa36712be..7728d91152 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -20,6 +20,7 @@ jobs: diffcalc: name: Diffcalc runs-on: ubuntu-latest + continue-on-error: true if: | ${{ github.event.issue.pull_request }} && @@ -28,7 +29,6 @@ jobs: strategy: fail-fast: false - continue-on-error: true matrix: ruleset: - { name: osu, id: 0 } From f3d2d93aa14227f41b6f92e250de9aff5d484164 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Sep 2021 16:09:22 +0900 Subject: [PATCH 1732/2442] Remove stray newline --- osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs index 34a9610804..631455b727 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs @@ -201,7 +201,6 @@ namespace osu.Game.Tests.Visual.Ranking [Test] public void TestFetchScoresAfterShowingStatistics() - { DelayedFetchResultsScreen screen = null; From b6c80f04b07b33ec6a3504a6ae968c32d724dc75 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 7 Sep 2021 16:44:45 +0900 Subject: [PATCH 1733/2442] Add "featured artists" filter to beatmap search --- .../UserInterface/TestSceneBeatmapListingSearchControl.cs | 3 ++- osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs | 3 ++- osu.Game/Overlays/BeatmapListing/SearchGeneral.cs | 6 +++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs index abd1baf0ac..008d91f649 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; +using Humanizer; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -73,7 +74,7 @@ namespace osu.Game.Tests.Visual.UserInterface }; control.Query.BindValueChanged(q => query.Text = $"Query: {q.NewValue}", true); - control.General.BindCollectionChanged((u, v) => general.Text = $"General: {(control.General.Any() ? string.Join('.', control.General.Select(i => i.ToString().ToLowerInvariant())) : "")}", true); + control.General.BindCollectionChanged((u, v) => general.Text = $"General: {(control.General.Any() ? string.Join('.', control.General.Select(i => i.ToString().Underscore())) : "")}", true); control.Ruleset.BindValueChanged(r => ruleset.Text = $"Ruleset: {r.NewValue}", true); control.Category.BindValueChanged(c => category.Text = $"Category: {c.NewValue}", true); control.Genre.BindValueChanged(g => genre.Text = $"Genre: {g.NewValue}", true); diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index 8ce495e274..ae082ca82e 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; +using Humanizer; using JetBrains.Annotations; using osu.Framework.IO.Network; using osu.Game.Extensions; @@ -83,7 +84,7 @@ namespace osu.Game.Online.API.Requests req.AddParameter("q", query); if (General != null && General.Any()) - req.AddParameter("c", string.Join('.', General.Select(e => e.ToString().ToLowerInvariant()))); + req.AddParameter("c", string.Join('.', General.Select(e => e.ToString().Underscore()))); if (ruleset.ID.HasValue) req.AddParameter("m", ruleset.ID.Value.ToString()); diff --git a/osu.Game/Overlays/BeatmapListing/SearchGeneral.cs b/osu.Game/Overlays/BeatmapListing/SearchGeneral.cs index d334b82e88..9387020bdf 100644 --- a/osu.Game/Overlays/BeatmapListing/SearchGeneral.cs +++ b/osu.Game/Overlays/BeatmapListing/SearchGeneral.cs @@ -19,6 +19,10 @@ namespace osu.Game.Overlays.BeatmapListing [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GeneralFollows))] [Description("Subscribed mappers")] - Follows + Follows, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.GeneralFeaturedArtists))] + [Description("Featured artists")] + FeaturedArtists } } From d922210d2feda6f03de5717d3ca53e99b4864feb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Sep 2021 16:46:27 +0900 Subject: [PATCH 1734/2442] Fix `TestSceneDeleteLocalScore` not properly comparing post-delete scores --- .../TestSceneDeleteLocalScore.cs | 19 +++++++------ .../Online/Leaderboards/LeaderboardScore.cs | 27 ++++++++++--------- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index 8efee2723f..2e30ed9827 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.UserInterface private BeatmapManager beatmapManager; private ScoreManager scoreManager; - private readonly List scores = new List(); + private readonly List importedScores = new List(); private BeatmapInfo beatmap; [Cached] @@ -100,11 +100,9 @@ namespace osu.Game.Tests.Visual.UserInterface User = new User { Username = "TestUser" }, }; - scores.Add(scoreManager.Import(score).Result); + importedScores.Add(scoreManager.Import(score).Result); } - scores.Sort(Comparer.Create((s1, s2) => s2.TotalScore.CompareTo(s1.TotalScore))); - return dependencies; } @@ -134,9 +132,14 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestDeleteViaRightClick() { + ScoreInfo scoreBeingDeleted = null; AddStep("open menu for top score", () => { - InputManager.MoveMouseTo(leaderboard.ChildrenOfType().First()); + var leaderboardScore = leaderboard.ChildrenOfType().First(); + + scoreBeingDeleted = leaderboardScore.Score; + + InputManager.MoveMouseTo(leaderboardScore); InputManager.Click(MouseButton.Right); }); @@ -158,14 +161,14 @@ namespace osu.Game.Tests.Visual.UserInterface InputManager.Click(MouseButton.Left); }); - AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s != scores[0])); + AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s.OnlineScoreID != scoreBeingDeleted.OnlineScoreID)); } [Test] public void TestDeleteViaDatabase() { - AddStep("delete top score", () => scoreManager.Delete(scores[0])); - AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s != scores[0])); + AddStep("delete top score", () => scoreManager.Delete(importedScores[0])); + AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s.OnlineScoreID != importedScores[0].OnlineScoreID)); } } } diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index 934b905a1a..090a4014aa 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -34,6 +34,8 @@ namespace osu.Game.Online.Leaderboards { public const float HEIGHT = 60; + public readonly ScoreInfo Score; + private const float corner_radius = 5; private const float edge_margin = 5; private const float background_alpha = 0.25f; @@ -41,7 +43,6 @@ namespace osu.Game.Online.Leaderboards protected Container RankContainer { get; private set; } - private readonly ScoreInfo score; private readonly int? rank; private readonly bool allowHighlight; @@ -67,7 +68,7 @@ namespace osu.Game.Online.Leaderboards public LeaderboardScore(ScoreInfo score, int? rank, bool allowHighlight = true) { - this.score = score; + this.Score = score; this.rank = rank; this.allowHighlight = allowHighlight; @@ -78,9 +79,9 @@ namespace osu.Game.Online.Leaderboards [BackgroundDependencyLoader] private void load(IAPIProvider api, OsuColour colour, ScoreManager scoreManager) { - var user = score.User; + var user = Score.User; - statisticsLabels = GetStatistics(score).Select(s => new ScoreComponentLabel(s)).ToList(); + statisticsLabels = GetStatistics(Score).Select(s => new ScoreComponentLabel(s)).ToList(); ClickableAvatar innerAvatar; @@ -198,7 +199,7 @@ namespace osu.Game.Online.Leaderboards { TextColour = Color4.White, GlowColour = Color4Extensions.FromHex(@"83ccfa"), - Current = scoreManager.GetBindableTotalScoreString(score), + Current = scoreManager.GetBindableTotalScoreString(Score), Font = OsuFont.Numeric.With(size: 23), }, RankContainer = new Container @@ -206,7 +207,7 @@ namespace osu.Game.Online.Leaderboards Size = new Vector2(40f, 20f), Children = new[] { - scoreRank = new UpdateableRank(score.Rank) + scoreRank = new UpdateableRank(Score.Rank) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -223,7 +224,7 @@ namespace osu.Game.Online.Leaderboards AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, Spacing = new Vector2(1), - ChildrenEnumerable = score.Mods.Select(mod => new ModIcon(mod) { Scale = new Vector2(0.375f) }) + ChildrenEnumerable = Score.Mods.Select(mod => new ModIcon(mod) { Scale = new Vector2(0.375f) }) }, }, }, @@ -389,14 +390,14 @@ namespace osu.Game.Online.Leaderboards { List items = new List(); - if (score.Mods.Length > 0 && modsContainer.Any(s => s.IsHovered) && songSelect != null) - items.Add(new OsuMenuItem("Use these mods", MenuItemType.Highlighted, () => songSelect.Mods.Value = score.Mods)); + if (Score.Mods.Length > 0 && modsContainer.Any(s => s.IsHovered) && songSelect != null) + items.Add(new OsuMenuItem("Use these mods", MenuItemType.Highlighted, () => songSelect.Mods.Value = Score.Mods)); - if (score.Files?.Count > 0) - items.Add(new OsuMenuItem("Export", MenuItemType.Standard, () => scoreManager.Export(score))); + if (Score.Files?.Count > 0) + items.Add(new OsuMenuItem("Export", MenuItemType.Standard, () => scoreManager.Export(Score))); - if (score.ID != 0) - items.Add(new OsuMenuItem("Delete", MenuItemType.Destructive, () => dialogOverlay?.Push(new LocalScoreDeleteDialog(score)))); + if (Score.ID != 0) + items.Add(new OsuMenuItem("Delete", MenuItemType.Destructive, () => dialogOverlay?.Push(new LocalScoreDeleteDialog(Score)))); return items.ToArray(); } From a42527b8f553f83923647259c53f9ce53bf67392 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Sep 2021 16:46:40 +0900 Subject: [PATCH 1735/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 05367c00f6..7378450c38 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ae423bac8c..d80dd075ee 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index be737392e1..8ce757974e 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 91a48084c7b1e71b83f552db216e11f2d1a04143 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Sep 2021 17:25:03 +0900 Subject: [PATCH 1736/2442] Update asserts in line with framework changes to `PlaybackPosition` --- osu.Game.Tests/NonVisual/Skinning/LegacySkinAnimationTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/NonVisual/Skinning/LegacySkinAnimationTest.cs b/osu.Game.Tests/NonVisual/Skinning/LegacySkinAnimationTest.cs index e45b8f7dc5..785f31386d 100644 --- a/osu.Game.Tests/NonVisual/Skinning/LegacySkinAnimationTest.cs +++ b/osu.Game.Tests/NonVisual/Skinning/LegacySkinAnimationTest.cs @@ -40,10 +40,10 @@ namespace osu.Game.Tests.NonVisual.Skinning assertPlaybackPosition(0); AddStep("set start time to 1000", () => animationTimeReference.AnimationStartTime.Value = 1000); - assertPlaybackPosition(-1000); + assertPlaybackPosition(0); AddStep("set current time to 500", () => animationTimeReference.ManualClock.CurrentTime = 500); - assertPlaybackPosition(-500); + assertPlaybackPosition(0); } private void assertPlaybackPosition(double expectedPosition) From 5ab2f4b38665b0971fff93d752b7213b176934b4 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 7 Sep 2021 17:31:54 +0900 Subject: [PATCH 1737/2442] Give an orange colour to "featured artists" filter to match web --- .../BeatmapListingSearchControl.cs | 2 +- .../BeatmapSearchGeneralFilterRow.cs | 39 +++++++++++++++++++ .../Overlays/BeatmapListing/FilterTabItem.cs | 4 +- 3 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 osu.Game/Overlays/BeatmapListing/BeatmapSearchGeneralFilterRow.cs diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs index 0626f236b8..d1e2ac38df 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs @@ -127,7 +127,7 @@ namespace osu.Game.Overlays.BeatmapListing Padding = new MarginPadding { Horizontal = 10 }, Children = new Drawable[] { - generalFilter = new BeatmapSearchMultipleSelectionFilterRow(BeatmapsStrings.ListingSearchFiltersGeneral), + generalFilter = new BeatmapSearchGeneralFilterRow(), modeFilter = new BeatmapSearchRulesetFilterRow(), categoryFilter = new BeatmapSearchFilterRow(BeatmapsStrings.ListingSearchFiltersStatus), genreFilter = new BeatmapSearchFilterRow(BeatmapsStrings.ListingSearchFiltersGenre), diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapSearchGeneralFilterRow.cs b/osu.Game/Overlays/BeatmapListing/BeatmapSearchGeneralFilterRow.cs new file mode 100644 index 0000000000..fb9e1c0420 --- /dev/null +++ b/osu.Game/Overlays/BeatmapListing/BeatmapSearchGeneralFilterRow.cs @@ -0,0 +1,39 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Resources.Localisation.Web; +using osuTK.Graphics; + +namespace osu.Game.Overlays.BeatmapListing +{ + public class BeatmapSearchGeneralFilterRow : BeatmapSearchMultipleSelectionFilterRow + { + public BeatmapSearchGeneralFilterRow() + : base(BeatmapsStrings.ListingSearchFiltersGeneral) + { + } + + protected override MultipleSelectionFilter CreateMultipleSelectionFilter() => new GeneralFilter(); + + private class GeneralFilter : MultipleSelectionFilter + { + protected override MultipleSelectionFilterTabItem CreateTabItem(SearchGeneral value) + { + if (value == SearchGeneral.FeaturedArtists) + return new FeaturedArtistsTabItem(); + + return new MultipleSelectionFilterTabItem(value); + } + } + + private class FeaturedArtistsTabItem : MultipleSelectionFilterTabItem + { + public FeaturedArtistsTabItem() + : base(SearchGeneral.FeaturedArtists) + { + } + + protected override Color4 GetStateColour() => OverlayColourProvider.Orange.Colour1; + } + } +} diff --git a/osu.Game/Overlays/BeatmapListing/FilterTabItem.cs b/osu.Game/Overlays/BeatmapListing/FilterTabItem.cs index 46cb1e822f..9274cf20aa 100644 --- a/osu.Game/Overlays/BeatmapListing/FilterTabItem.cs +++ b/osu.Game/Overlays/BeatmapListing/FilterTabItem.cs @@ -71,10 +71,10 @@ namespace osu.Game.Overlays.BeatmapListing private void updateState() { - text.FadeColour(IsHovered ? colourProvider.Light1 : getStateColour(), 200, Easing.OutQuint); + text.FadeColour(IsHovered ? colourProvider.Light1 : GetStateColour(), 200, Easing.OutQuint); text.Font = text.Font.With(weight: Active.Value ? FontWeight.SemiBold : FontWeight.Regular); } - private Color4 getStateColour() => Active.Value ? colourProvider.Content1 : colourProvider.Light2; + protected virtual Color4 GetStateColour() => Active.Value ? colourProvider.Content1 : colourProvider.Light2; } } From 92f59c10f5108d23d84698d0b5c978726c629b5d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Sep 2021 17:45:21 +0900 Subject: [PATCH 1738/2442] Remove redundant `this.` in assignment --- osu.Game/Online/Leaderboards/LeaderboardScore.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index 090a4014aa..26749a23f9 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -68,7 +68,8 @@ namespace osu.Game.Online.Leaderboards public LeaderboardScore(ScoreInfo score, int? rank, bool allowHighlight = true) { - this.Score = score; + Score = score; + this.rank = rank; this.allowHighlight = allowHighlight; From 3d8faea4b073718900da16d0a0b522fb700aed9d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 Sep 2021 18:52:25 +0900 Subject: [PATCH 1739/2442] Simplify nesting of `OrderByTotalScoreAsync` --- osu.Game/Scoring/ScoreManager.cs | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 2cda8959f4..81e701f001 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -116,25 +116,20 @@ namespace osu.Game.Scoring { var difficultyCache = difficulties?.Invoke(); - if (difficultyCache == null) - return orderByTotalScore(scores); - - // Compute difficulties asynchronously first to prevent blocking via the GetTotalScore() call below. - foreach (var s in scores) + if (difficultyCache != null) { - await difficultyCache.GetDifficultyAsync(s.Beatmap, s.Ruleset, s.Mods, cancellationToken).ConfigureAwait(false); - cancellationToken.ThrowIfCancellationRequested(); + // Compute difficulties asynchronously first to prevent blocking via the GetTotalScore() call below. + foreach (var s in scores) + { + await difficultyCache.GetDifficultyAsync(s.Beatmap, s.Ruleset, s.Mods, cancellationToken).ConfigureAwait(false); + cancellationToken.ThrowIfCancellationRequested(); + } } - return orderByTotalScore(scores); - - ScoreInfo[] orderByTotalScore(IEnumerable incoming) - { - // We're calling .Result, but this should not be a blocking call due to the above GetDifficultyAsync() calls. - return incoming.OrderByDescending(s => GetTotalScoreAsync(s, cancellationToken: cancellationToken).Result) - .ThenBy(s => s.OnlineScoreID) - .ToArray(); - } + // We're calling .Result, but this should not be a blocking call due to the above GetDifficultyAsync() calls. + return scores.OrderByDescending(s => GetTotalScoreAsync(s, cancellationToken: cancellationToken).Result) + .ThenBy(s => s.OnlineScoreID) + .ToArray(); } /// From 2f42a8461e64ac7f33b75fcb2171b61bc296c99c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 7 Sep 2021 20:19:07 +0900 Subject: [PATCH 1740/2442] Fix diffcalc workflow triggering too often --- .github/workflows/test-diffcalc.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 7728d91152..4274d01bab 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -23,9 +23,9 @@ jobs: continue-on-error: true if: | - ${{ github.event.issue.pull_request }} && + github.event.issue.pull_request && contains(github.event.comment.body, '!pp check') && - ${{ github.event.comment.author_association == 'MEMBER' }} + (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') strategy: fail-fast: false From f54d5675db79ae378f8623115e984e65446198c3 Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Tue, 7 Sep 2021 17:06:12 +0100 Subject: [PATCH 1741/2442] check if user joined requested channel already --- osu.Game/Online/Chat/ChannelManager.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index d2500f0d7f..43da69db97 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -264,6 +264,11 @@ namespace osu.Game.Online.Chat break; } + // Check if the user has joined requested channel already. + var alreadyJoinedChannel = JoinedChannels.FirstOrDefault(c => c.Type == ChannelType.PM && c.Users.Count == 1 && c.Name == content); + if (alreadyJoinedChannel != null) + CurrentChannel.Value = alreadyJoinedChannel; + var request = new GetUserRequest(content); request.Success += OpenPrivateChannel; request.Failure += _ => target.AddNewMessages(new ErrorMessage("User not found.")); From be9540e53507c83cadd5fc6cdaf18bf5f2495e02 Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Tue, 7 Sep 2021 17:17:10 +0100 Subject: [PATCH 1742/2442] fix "key" having wrong url parameter name --- osu.Game/Online/API/Requests/GetUserRequest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/GetUserRequest.cs b/osu.Game/Online/API/Requests/GetUserRequest.cs index e49c4ab298..fe954372bf 100644 --- a/osu.Game/Online/API/Requests/GetUserRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRequest.cs @@ -43,7 +43,7 @@ namespace osu.Game.Online.API.Requests Ruleset = ruleset; } - protected override string Target => lookup != null ? $@"users/{lookup}/{Ruleset?.ShortName}?k={lookupType.ToString().ToLower()}" : $@"me/{Ruleset?.ShortName}"; + protected override string Target => lookup != null ? $@"users/{lookup}/{Ruleset?.ShortName}?key={lookupType.ToString().ToLower()}" : $@"me/{Ruleset?.ShortName}"; private enum LookupType { From b1c89f761871445a01997c7ac62ba6919eb03055 Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Tue, 7 Sep 2021 17:22:59 +0100 Subject: [PATCH 1743/2442] ignore case when search for already joined channel --- osu.Game/Online/Chat/ChannelManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 43da69db97..05f9d244f2 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -265,7 +265,8 @@ namespace osu.Game.Online.Chat } // Check if the user has joined requested channel already. - var alreadyJoinedChannel = JoinedChannels.FirstOrDefault(c => c.Type == ChannelType.PM && c.Users.Count == 1 && c.Name == content); + var alreadyJoinedChannel = JoinedChannels.FirstOrDefault( + c => c.Type == ChannelType.PM && c.Users.Count == 1 && c.Name.Equals(content, StringComparison.OrdinalIgnoreCase)); if (alreadyJoinedChannel != null) CurrentChannel.Value = alreadyJoinedChannel; From 255f8a9769a0fc1c6ad99df1f5e9a37b2c305bf1 Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Tue, 7 Sep 2021 17:25:47 +0100 Subject: [PATCH 1744/2442] add alias "/msg" (also a command in stable) --- osu.Game/Online/Chat/ChannelManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 05f9d244f2..34c6d048a3 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -257,6 +257,7 @@ namespace osu.Game.Online.Chat break; case "chat": + case "msg": case "query": if (string.IsNullOrWhiteSpace(content)) { From 2097889ce1a83a8ed2415c7fd81e7d990e3ade1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 7 Sep 2021 20:58:14 +0200 Subject: [PATCH 1745/2442] Add failing test case --- .../Visual/Ranking/TestSceneScorePanelList.cs | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs index b2585c0509..6f3b3028be 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs @@ -203,6 +203,71 @@ namespace osu.Game.Tests.Visual.Ranking assertExpandedPanelCentred(); } + [Test] + public void TestKeyboardNavigation() + { + var lowestScore = new TestScoreInfo(new OsuRuleset().RulesetInfo) { MaxCombo = 100 }; + var middleScore = new TestScoreInfo(new OsuRuleset().RulesetInfo) { MaxCombo = 200 }; + var highestScore = new TestScoreInfo(new OsuRuleset().RulesetInfo) { MaxCombo = 300 }; + + createListStep(() => new ScorePanelList()); + + AddStep("add scores and select middle", () => + { + // order of addition purposefully scrambled. + list.AddScore(middleScore); + list.AddScore(lowestScore); + list.AddScore(highestScore); + list.SelectedScore.Value = middleScore; + }); + + assertScoreState(highestScore, false); + assertScoreState(middleScore, true); + assertScoreState(lowestScore, false); + + AddStep("press left", () => InputManager.Key(Key.Left)); + + assertScoreState(highestScore, true); + assertScoreState(middleScore, false); + assertScoreState(lowestScore, false); + assertExpandedPanelCentred(); + + AddStep("press left at start of list", () => InputManager.Key(Key.Left)); + + assertScoreState(highestScore, true); + assertScoreState(middleScore, false); + assertScoreState(lowestScore, false); + assertExpandedPanelCentred(); + + AddStep("press right", () => InputManager.Key(Key.Right)); + + assertScoreState(highestScore, false); + assertScoreState(middleScore, true); + assertScoreState(lowestScore, false); + assertExpandedPanelCentred(); + + AddStep("press right again", () => InputManager.Key(Key.Right)); + + assertScoreState(highestScore, false); + assertScoreState(middleScore, false); + assertScoreState(lowestScore, true); + assertExpandedPanelCentred(); + + AddStep("press right at end of list", () => InputManager.Key(Key.Right)); + + assertScoreState(highestScore, false); + assertScoreState(middleScore, false); + assertScoreState(lowestScore, true); + assertExpandedPanelCentred(); + + AddStep("press left", () => InputManager.Key(Key.Left)); + + assertScoreState(highestScore, false); + assertScoreState(middleScore, true); + assertScoreState(lowestScore, false); + assertExpandedPanelCentred(); + } + private void createListStep(Func creationFunc) { AddStep("create list", () => Child = list = creationFunc().With(d => From 8cc444df5f6a17fbea64c5d2b34d618fbd04530e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 7 Sep 2021 21:14:38 +0200 Subject: [PATCH 1746/2442] Fix incorrect keyboard navigation order in score panel list --- osu.Game/Screens/Ranking/ScorePanelList.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index a847df344f..d5b8a4c8ea 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -306,18 +307,18 @@ namespace osu.Game.Screens.Ranking if (expandedPanel == null) return base.OnKeyDown(e); - var expandedPanelIndex = flow.GetPanelIndex(expandedPanel.Score); - switch (e.Key) { case Key.Left: - if (expandedPanelIndex > 0) - SelectedScore.Value = flow.Children[expandedPanelIndex - 1].Panel.Score; + var previousScore = flow.GetPreviousScore(expandedPanel.Score); + if (previousScore != null) + SelectedScore.Value = previousScore; return true; case Key.Right: - if (expandedPanelIndex < flow.Count - 1) - SelectedScore.Value = flow.Children[expandedPanelIndex + 1].Panel.Score; + var nextScore = flow.GetNextScore(expandedPanel.Score); + if (nextScore != null) + SelectedScore.Value = nextScore; return true; } @@ -336,6 +337,12 @@ namespace osu.Game.Screens.Ranking public int GetPanelIndex(ScoreInfo score) => applySorting(Children).TakeWhile(s => s.Panel.Score != score).Count(); + [CanBeNull] + public ScoreInfo GetPreviousScore(ScoreInfo score) => applySorting(Children).TakeWhile(s => s.Panel.Score != score).LastOrDefault()?.Panel.Score; + + [CanBeNull] + public ScoreInfo GetNextScore(ScoreInfo score) => applySorting(Children).SkipWhile(s => s.Panel.Score != score).ElementAtOrDefault(1)?.Panel.Score; + private IEnumerable applySorting(IEnumerable drawables) => drawables.OfType() .OrderByDescending(GetLayoutPosition) .ThenBy(s => s.Panel.Score.OnlineScoreID); From d8fe98fe12ac5e305c00c78657322b50c337cfa6 Mon Sep 17 00:00:00 2001 From: Ethan Ng Date: Tue, 7 Sep 2021 14:06:23 -0600 Subject: [PATCH 1747/2442] Fixed grammatical error in ChangelogSupporterPromo --- osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index 508c8399b6..689a7bb4a3 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -126,7 +126,7 @@ namespace osu.Game.Overlays.Changelog }; supportLinkText.AddText("Support further development of osu! and "); - supportLinkText.AddLink("become and osu!supporter", "https://osu.ppy.sh/home/support", t => t.Font = t.Font.With(weight: FontWeight.Bold)); + supportLinkText.AddLink("become an osu!supporter", "https://osu.ppy.sh/home/support", t => t.Font = t.Font.With(weight: FontWeight.Bold)); supportLinkText.AddText(" today!"); imageContainer.Children = new Drawable[] From b8a1ebb786c6bd8a9581d62f489ccd598563e513 Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Tue, 7 Sep 2021 16:54:21 -0400 Subject: [PATCH 1748/2442] Hide Popover after failed password attempt Instead of throwing an error, just close the popover and let the user continue --- osu.Game/Screens/OnlinePlay/Components/RoomManager.cs | 3 ++- .../Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs | 11 +++++++---- osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 6 ++++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs index 3b6c1c8be0..4b3617c74a 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs @@ -87,7 +87,8 @@ namespace osu.Game.Screens.OnlinePlay.Components currentJoinRoomRequest.Failure += exception => { - if (!(exception is OperationCanceledException)) + // provide error output if the operation wasn't canceled and the error doesn't stem from an invalid password + if (!(exception is OperationCanceledException) && !((APIException)exception).Message.Equals("Invalid room password", StringComparison.Ordinal)) Logger.Log($"Failed to join room: {exception}", level: LogLevel.Important); onError?.Invoke(exception.ToString()); }; diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index 6dc3cd18c5..c8b647fc9a 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -178,7 +178,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { private readonly Room room; - public Action JoinRequested; + public Action, Action> JoinRequested; public PasswordEntryPopover(Room room) { @@ -186,6 +186,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge } private OsuPasswordTextBox passwordTextbox; + private TriangleButton joinButton; [BackgroundDependencyLoader] private void load() @@ -201,15 +202,17 @@ namespace osu.Game.Screens.OnlinePlay.Lounge passwordTextbox = new OsuPasswordTextBox { Width = 200, + PlaceholderText = "password", }, - new TriangleButton + joinButton = new TriangleButton { Width = 80, Text = "Join Room", - Action = () => JoinRequested?.Invoke(room, passwordTextbox.Text) } } }; + + joinButton.Action = () => JoinRequested?.Invoke(room, passwordTextbox.Text, null, _ => this.HidePopover()); } protected override void LoadComplete() @@ -217,7 +220,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge base.LoadComplete(); Schedule(() => GetContainingInputManager().ChangeFocus(passwordTextbox)); - passwordTextbox.OnCommit += (_, __) => JoinRequested?.Invoke(room, passwordTextbox.Text); + passwordTextbox.OnCommit += (_, __) => JoinRequested?.Invoke(room, passwordTextbox.Text, null, _ => this.HidePopover()); } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index cca1394b6d..08bdd0487a 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -290,7 +290,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge popoverContainer.HidePopover(); } - public void Join(Room room, string password) => Schedule(() => + public void Join(Room room, string password, Action onSuccess = null, Action onFailure = null) => Schedule(() => { if (joiningRoomOperation != null) return; @@ -302,10 +302,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge Open(room); joiningRoomOperation?.Dispose(); joiningRoomOperation = null; - }, _ => + onSuccess?.Invoke(room); + }, error => { joiningRoomOperation?.Dispose(); joiningRoomOperation = null; + onFailure?.Invoke(error); }); }); From b1f91596a7e7a07082056686ba49a8acb81244b2 Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Tue, 7 Sep 2021 20:05:24 -0400 Subject: [PATCH 1749/2442] Give user feedback on password attempt fail Shake the popover Set the input box's color to red and set the placeholder text to "incorrect password" --- .../Graphics/Containers/ShakeContainer.cs | 7 +++ .../OnlinePlay/Lounge/DrawableLoungeRoom.cs | 47 +++++++++++++------ 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/osu.Game/Graphics/Containers/ShakeContainer.cs b/osu.Game/Graphics/Containers/ShakeContainer.cs index dca9df1e98..ffa8ef585e 100644 --- a/osu.Game/Graphics/Containers/ShakeContainer.cs +++ b/osu.Game/Graphics/Containers/ShakeContainer.cs @@ -1,6 +1,7 @@ // 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 osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -26,6 +27,11 @@ namespace osu.Game.Graphics.Containers /// public float ShakeMagnitude = 8; + /// + /// Fired when finishes + /// + public event Action OnShakeFinish; + /// /// Shake the contents of this container. /// @@ -50,6 +56,7 @@ namespace osu.Game.Graphics.Containers } sequence.MoveToX(0, ShakeDuration / 2, Easing.InSine); + sequence.Finally(_ => OnShakeFinish?.Invoke()); } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index c8b647fc9a..bedee39a26 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -15,6 +15,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Input.Bindings; @@ -187,32 +188,50 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private OsuPasswordTextBox passwordTextbox; private TriangleButton joinButton; + private ShakeContainer shakeContainer; [BackgroundDependencyLoader] private void load() { - Child = new FillFlowContainer + shakeContainer = new ShakeContainer { Margin = new MarginPadding(10), - Spacing = new Vector2(5), AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Children = new Drawable[] + Child = new FillFlowContainer { - passwordTextbox = new OsuPasswordTextBox + Margin = new MarginPadding(10), + Spacing = new Vector2(5), + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new Drawable[] { - Width = 200, - PlaceholderText = "password", - }, - joinButton = new TriangleButton - { - Width = 80, - Text = "Join Room", + passwordTextbox = new OsuPasswordTextBox + { + Width = 200, + PlaceholderText = "password", + }, + joinButton = new TriangleButton + { + Width = 80, + Text = "Join Room", + } } } }; + Child = shakeContainer; - joinButton.Action = () => JoinRequested?.Invoke(room, passwordTextbox.Text, null, _ => this.HidePopover()); + joinButton.Action = () => JoinRequested?.Invoke(room, passwordTextbox.Text, null, joinFailed); + } + + private void joinFailed(string error) + { + passwordTextbox.Text = string.Empty; + passwordTextbox.PlaceholderText = "incorrect password"; + passwordTextbox.Colour = Color4.Red; + + shakeContainer.OnShakeFinish += () => passwordTextbox.Colour = Color4.White; + + shakeContainer.Shake(); } protected override void LoadComplete() @@ -220,7 +239,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge base.LoadComplete(); Schedule(() => GetContainingInputManager().ChangeFocus(passwordTextbox)); - passwordTextbox.OnCommit += (_, __) => JoinRequested?.Invoke(room, passwordTextbox.Text, null, _ => this.HidePopover()); + passwordTextbox.OnCommit += (_, __) => JoinRequested?.Invoke(room, passwordTextbox.Text, null, joinFailed); } } } From 7543f9dfb0155a3e6232f429add9f04bb7b43231 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 8 Sep 2021 12:21:24 +0900 Subject: [PATCH 1750/2442] Add featured artist track ID online info --- osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs | 6 ++++++ osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs | 6 +++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs index 48f1f0ce68..bf8063cfaf 100644 --- a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs @@ -90,6 +90,12 @@ namespace osu.Game.Beatmaps /// The song language of this beatmap set. /// public BeatmapSetOnlineLanguage Language { get; set; } + + /// + /// The song ID of this beatmap set. + /// Non-null only if the song is from a featured artist. + /// + public int? TrackId { get; set; } } public class BeatmapSetOnlineGenre diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index 45d9c9405f..f653a654ca 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -63,6 +63,9 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"ratings")] private int[] ratings { get; set; } + [JsonProperty(@"track_id")] + private int? trackId { get; set; } + [JsonProperty(@"user_id")] private int creatorId { @@ -106,7 +109,8 @@ namespace osu.Game.Online.API.Requests.Responses Availability = availability, HasFavourited = hasFavourited, Genre = genre, - Language = language + Language = language, + TrackId = trackId }, }; From 81c9d831f496702d2536b7fb27c04b96a9f3a24f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Sep 2021 13:26:07 +0900 Subject: [PATCH 1751/2442] Cache reflection portion of `SettingSource` lookups Fixes the mod-related overhead portion of https://github.com/ppy/osu/issues/14638. There's still a significant performance issue that will be addressed separately. --- .../Configuration/SettingSourceAttribute.cs | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/osu.Game/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs index f373e59417..4fff81b587 100644 --- a/osu.Game/Configuration/SettingSourceAttribute.cs +++ b/osu.Game/Configuration/SettingSourceAttribute.cs @@ -167,18 +167,7 @@ namespace osu.Game.Configuration } } - public static IEnumerable<(SettingSourceAttribute, PropertyInfo)> GetSettingsSourceProperties(this object obj) - { - foreach (var property in obj.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance)) - { - var attr = property.GetCustomAttribute(true); - - if (attr == null) - continue; - - yield return (attr, property); - } - } + public static IEnumerable<(SettingSourceAttribute, PropertyInfo)> GetSettingsSourceProperties(this T obj) => SettingSourceCache.SettingSourceProperties; public static ICollection<(SettingSourceAttribute, PropertyInfo)> GetOrderedSettingsSourceProperties(this object obj) => obj.GetSettingsSourceProperties() @@ -196,5 +185,14 @@ namespace osu.Game.Configuration protected override DropdownMenu CreateMenu() => base.CreateMenu().With(m => m.MaxHeight = 100); } } + + private static class SettingSourceCache + { + public static (SettingSourceAttribute, PropertyInfo)[] SettingSourceProperties { get; } = + typeof(T).GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance) + .Select(property => (property.GetCustomAttribute(), property)) + .Where(pair => pair.Item1 != null) + .ToArray(); + } } } From f96be2cab89cd11e08325e4e22f1a8a45a6ea308 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 8 Sep 2021 13:27:20 +0900 Subject: [PATCH 1752/2442] Add featured artist marker to beatmap set listing and overlay --- .../BeatmapListing/Panels/GridBeatmapPanel.cs | 28 +++++++++++--- .../BeatmapListing/Panels/ListBeatmapPanel.cs | 26 +++++++++++-- .../BeatmapSet/BeatmapSetBadgePill.cs | 32 ++++++++++++++++ .../BeatmapSet/BeatmapSetHeaderContent.cs | 23 +++++++++-- .../BeatmapSet/ExplicitContentBeatmapPill.cs | 38 +++++-------------- .../BeatmapSet/FeaturedArtistBeatmapPill.cs | 27 +++++++++++++ 6 files changed, 133 insertions(+), 41 deletions(-) create mode 100644 osu.Game/Overlays/BeatmapSet/BeatmapSetBadgePill.cs create mode 100644 osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs diff --git a/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs index 4d5c387c4a..c078127353 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs @@ -25,7 +25,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels private const float horizontal_padding = 10; private const float vertical_padding = 5; - private FillFlowContainer bottomPanel, statusContainer, titleContainer; + private FillFlowContainer bottomPanel, statusContainer, titleContainer, artistContainer; private PlayButton playButton; private Box progressBar; @@ -89,11 +89,19 @@ namespace osu.Game.Overlays.BeatmapListing.Panels }, } }, - new OsuSpriteText + artistContainer = new FillFlowContainer { - Text = new RomanisableString(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist), - Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true) - }, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = new RomanisableString(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist), + Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true) + } + } + } }, }, new Container @@ -213,6 +221,16 @@ namespace osu.Game.Overlays.BeatmapListing.Panels }); } + if (SetInfo.OnlineInfo?.TrackId != null) + { + artistContainer.Add(new FeaturedArtistBeatmapPill + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding { Left = 10f, Top = 2f }, + }); + } + if (SetInfo.OnlineInfo?.HasVideo ?? false) { statusContainer.Add(new IconPill(FontAwesome.Solid.Film)); diff --git a/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs index 00ffd168c1..5011749c5f 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels private const float vertical_padding = 5; private const float height = 70; - private FillFlowContainer statusContainer, titleContainer; + private FillFlowContainer statusContainer, titleContainer, artistContainer; protected BeatmapPanelDownloadButton DownloadButton; private PlayButton playButton; private Box progressBar; @@ -112,10 +112,18 @@ namespace osu.Game.Overlays.BeatmapListing.Panels }, } }, - new OsuSpriteText + artistContainer = new FillFlowContainer { - Text = new RomanisableString(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist), - Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true) + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new[] + { + new OsuSpriteText + { + Text = new RomanisableString(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist), + Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true) + }, + }, }, } }, @@ -227,6 +235,16 @@ namespace osu.Game.Overlays.BeatmapListing.Panels }); } + if (SetInfo.OnlineInfo?.TrackId != null) + { + artistContainer.Add(new FeaturedArtistBeatmapPill + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding { Left = 10f, Top = 2f }, + }); + } + if (SetInfo.OnlineInfo?.HasVideo ?? false) { statusContainer.Add(new IconPill(FontAwesome.Solid.Film) { IconSize = new Vector2(20) }); diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetBadgePill.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetBadgePill.cs new file mode 100644 index 0000000000..15c691103a --- /dev/null +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetBadgePill.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. + +using JetBrains.Annotations; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osuTK.Graphics; + +namespace osu.Game.Overlays.BeatmapSet +{ + public class BeatmapSetBadgePill : CircularContainer + { + public BeatmapSetBadgePill() + { + AutoSizeAxes = Axes.Both; + Masking = true; + } + + [BackgroundDependencyLoader(true)] + private void load([CanBeNull] OsuColour colours, [CanBeNull] OverlayColourProvider colourProvider) + { + Add(new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider?.Background5 ?? colours?.Gray2 ?? Color4.DarkGray, + }); + } + } +} diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs index a61640a02e..c3b6444a24 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs @@ -37,6 +37,7 @@ namespace osu.Game.Overlays.BeatmapSet private readonly OsuSpriteText title, artist; private readonly AuthorInfo author; private readonly ExplicitContentBeatmapPill explicitContentPill; + private readonly FeaturedArtistBeatmapPill featuredArtistPill; private readonly FillFlowContainer downloadButtonsContainer; private readonly BeatmapAvailability beatmapAvailability; private readonly BeatmapSetOnlineStatusPill onlineStatusPill; @@ -129,10 +130,25 @@ namespace osu.Game.Overlays.BeatmapSet } } }, - artist = new OsuSpriteText + new FillFlowContainer { - Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, italics: true), - Margin = new MarginPadding { Bottom = 20 } + Direction = FillDirection.Horizontal, + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Bottom = 20 }, + Children = new Drawable[] + { + artist = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, italics: true), + }, + featuredArtistPill = new FeaturedArtistBeatmapPill + { + Alpha = 0f, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Left = 10 } + } + } }, new Container { @@ -233,6 +249,7 @@ namespace osu.Game.Overlays.BeatmapSet artist.Text = new RomanisableString(setInfo.NewValue.Metadata.ArtistUnicode, setInfo.NewValue.Metadata.Artist); explicitContentPill.Alpha = setInfo.NewValue.OnlineInfo.HasExplicitContent ? 1 : 0; + featuredArtistPill.Alpha = setInfo.NewValue.OnlineInfo.TrackId != null ? 1 : 0; onlineStatusPill.FadeIn(500, Easing.OutQuint); onlineStatusPill.Status = setInfo.NewValue.OnlineInfo.Status; diff --git a/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs b/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs index ba78592ed2..60fa3ea900 100644 --- a/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs +++ b/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs @@ -4,44 +4,24 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapSet { - public class ExplicitContentBeatmapPill : CompositeDrawable + public class ExplicitContentBeatmapPill : BeatmapSetBadgePill { - public ExplicitContentBeatmapPill() + [BackgroundDependencyLoader] + private void load() { - AutoSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader(true)] - private void load(OsuColour colours, OverlayColourProvider colourProvider) - { - InternalChild = new CircularContainer + Add(new OsuSpriteText { - Masking = true, - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider?.Background5 ?? colours.Gray2, - }, - new OsuSpriteText - { - Margin = new MarginPadding { Horizontal = 10f, Vertical = 2f }, - Text = BeatmapsetsStrings.NsfwBadgeLabel.ToUpper(), - Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), - Colour = OverlayColourProvider.Orange.Colour2, - } - } - }; + Margin = new MarginPadding { Horizontal = 10f, Vertical = 2f }, + Text = BeatmapsetsStrings.NsfwBadgeLabel.ToUpper(), + Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), + Colour = OverlayColourProvider.Orange.Colour2, + }); } } } diff --git a/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs b/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs new file mode 100644 index 0000000000..7facc2953a --- /dev/null +++ b/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs @@ -0,0 +1,27 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Extensions.LocalisationExtensions; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; + +namespace osu.Game.Overlays.BeatmapSet +{ + public class FeaturedArtistBeatmapPill : BeatmapSetBadgePill + { + [BackgroundDependencyLoader] + private void load() + { + Add(new OsuSpriteText + { + Margin = new MarginPadding { Horizontal = 10f, Vertical = 2f }, + Text = BeatmapsetsStrings.FeaturedArtistBadgeLabel.ToUpper(), + Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), + Colour = OverlayColourProvider.Blue.Colour1 + }); + } + } +} From 8745d299dc36f4817e73729d5ead419689397046 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 8 Sep 2021 13:27:45 +0900 Subject: [PATCH 1753/2442] Add visual tests for featured artist markers --- .../Online/TestSceneBeatmapSetOverlay.cs | 11 +++++++++++ .../Visual/Online/TestSceneDirectPanel.cs | 19 ++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index edc1696456..de7e4075f3 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -246,6 +246,17 @@ namespace osu.Game.Tests.Visual.Online }); } + [Test] + public void TestFeaturedBeatmap() + { + AddStep("show featured map", () => + { + var beatmapSet = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet; + beatmapSet.OnlineInfo.TrackId = 1; + overlay.ShowBeatmapSet(beatmapSet); + }); + } + [Test] public void TestHide() { diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs index fd5f306e07..722010ace2 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs @@ -99,16 +99,23 @@ namespace osu.Game.Tests.Visual.Online [BackgroundDependencyLoader] private void load(RulesetStore rulesets) { - var normal = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet; + var normal = getBeatmapSet(); normal.OnlineInfo.HasVideo = true; normal.OnlineInfo.HasStoryboard = true; var undownloadable = getUndownloadableBeatmapSet(); var manyDifficulties = getManyDifficultiesBeatmapSet(rulesets); - var explicitMap = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet; + var explicitMap = getBeatmapSet(); explicitMap.OnlineInfo.HasExplicitContent = true; + var featuredMap = getBeatmapSet(); + featuredMap.OnlineInfo.TrackId = 1; + + var explicitFeaturedMap = getBeatmapSet(); + explicitFeaturedMap.OnlineInfo.HasExplicitContent = true; + explicitFeaturedMap.OnlineInfo.TrackId = 2; + Child = new BasicScrollContainer { RelativeSizeAxes = Axes.Both, @@ -125,13 +132,19 @@ namespace osu.Game.Tests.Visual.Online new GridBeatmapPanel(undownloadable), new GridBeatmapPanel(manyDifficulties), new GridBeatmapPanel(explicitMap), + new GridBeatmapPanel(featuredMap), + new GridBeatmapPanel(explicitFeaturedMap), new ListBeatmapPanel(normal), new ListBeatmapPanel(undownloadable), new ListBeatmapPanel(manyDifficulties), - new ListBeatmapPanel(explicitMap) + new ListBeatmapPanel(explicitMap), + new ListBeatmapPanel(featuredMap), + new ListBeatmapPanel(explicitFeaturedMap) }, }, }; + + BeatmapSetInfo getBeatmapSet() => CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet; } } } From 217ca754aef221d955d25203d7aca757594ed184 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Wed, 8 Sep 2021 13:45:05 +0900 Subject: [PATCH 1754/2442] Add sound for team swaps --- .../Multiplayer/Participants/TeamDisplay.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/TeamDisplay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/TeamDisplay.cs index 351b9b3673..915cf30963 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/TeamDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/TeamDisplay.cs @@ -3,6 +3,8 @@ using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; @@ -22,6 +24,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants private Drawable box; + private Sample sampleTeamSwap; + [Resolved] private OsuColour colours { get; set; } @@ -39,7 +43,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants } [BackgroundDependencyLoader] - private void load() + private void load(AudioManager audio) { box = new Container { @@ -72,6 +76,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants { InternalChild = box; } + + sampleTeamSwap = audio.Samples.Get(@"Multiplayer/team-swap"); } private void changeTeam() @@ -99,6 +105,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants if (newTeam == displayedTeam) return; + if (newTeam != null && displayedTeam != null) + sampleTeamSwap?.Play(); + displayedTeam = newTeam; if (displayedTeam != null) From 9637326f0c8be64912660890f59f3175a68b908a Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 8 Sep 2021 15:50:19 +0900 Subject: [PATCH 1755/2442] Allow customizing link style by override in `LinkFlowContainer` --- .../Graphics/Containers/LinkFlowContainer.cs | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 85ef779e48..21c1d70d45 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -87,23 +87,25 @@ namespace osu.Game.Graphics.Containers private void createLink(IEnumerable drawables, LinkDetails link, string tooltipText, Action action = null) { - AddInternal(new DrawableLinkCompiler(drawables.OfType().ToList()) + var linkCompiler = CreateLinkCompiler(drawables.OfType()); + linkCompiler.RelativeSizeAxes = Axes.Both; + linkCompiler.TooltipText = tooltipText; + linkCompiler.Action = () => { - RelativeSizeAxes = Axes.Both, - TooltipText = tooltipText, - Action = () => - { - if (action != null) - action(); - else if (game != null) - game.HandleLink(link); - // fallback to handle cases where OsuGame is not available, ie. tournament client. - else if (link.Action == LinkAction.External) - host.OpenUrlExternally(link.Argument); - }, - }); + if (action != null) + action(); + else if (game != null) + game.HandleLink(link); + // fallback to handle cases where OsuGame is not available, ie. tournament client. + else if (link.Action == LinkAction.External) + host.OpenUrlExternally(link.Argument); + }; + + AddInternal(linkCompiler); } + protected virtual DrawableLinkCompiler CreateLinkCompiler(IEnumerable parts) => new DrawableLinkCompiler(parts); + // We want the compilers to always be visible no matter where they are, so RelativeSizeAxes is used. // However due to https://github.com/ppy/osu-framework/issues/2073, it's possible for the compilers to be relative size in the flow's auto-size axes - an unsupported operation. // Since the compilers don't display any content and don't affect the layout, it's simplest to exclude them from the flow. From d417f03a39d22f7e5b39a31735659fb531f354be Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 8 Sep 2021 15:52:01 +0900 Subject: [PATCH 1756/2442] Simplify link style customization code --- .../Overlays/Changelog/ChangelogSupporterPromo.cs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index 689a7bb4a3..c2b855a0f8 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -126,7 +126,7 @@ namespace osu.Game.Overlays.Changelog }; supportLinkText.AddText("Support further development of osu! and "); - supportLinkText.AddLink("become an osu!supporter", "https://osu.ppy.sh/home/support", t => t.Font = t.Font.With(weight: FontWeight.Bold)); + supportLinkText.AddLink("become an osu!supporter", @"https://osu.ppy.sh/home/support", t => t.Font = t.Font.With(weight: FontWeight.Bold)); supportLinkText.AddText(" today!"); imageContainer.Children = new Drawable[] @@ -170,27 +170,18 @@ namespace osu.Game.Overlays.Changelog { } - public new void AddLink(string text, string url, Action creationParameters) => - AddInternal(new SupporterPromoLinkCompiler(AddText(text, creationParameters)) { Url = url }); + protected override DrawableLinkCompiler CreateLinkCompiler(IEnumerable parts) => new SupporterPromoLinkCompiler(parts); private class SupporterPromoLinkCompiler : DrawableLinkCompiler { - [Resolved(CanBeNull = true)] - private OsuGame game { get; set; } - - public string Url; - public SupporterPromoLinkCompiler(IEnumerable parts) : base(parts) { - RelativeSizeAxes = Axes.Both; } [BackgroundDependencyLoader] private void load(OsuColour colour) { - TooltipText = Url; - Action = () => game?.HandleLink(Url); IdleColour = colour.PinkDark; HoverColour = Color4.White; } From 037b9cfb59d189576ad6f0cd3ec8ed04964d4968 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Sep 2021 16:53:23 +0900 Subject: [PATCH 1757/2442] Load `DrawableLoungRoom`s asynchronously --- .../OnlinePlay/Lounge/Components/RoomsContainer.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index 76cb02199b..8e3c74aba9 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -64,9 +64,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components protected override void LoadComplete() { - rooms.CollectionChanged += roomsChanged; roomManager.RoomsUpdated += updateSorting; + rooms.CollectionChanged += roomsChanged; rooms.BindTo(roomManager.Rooms); Filter?.BindValueChanged(criteria => applyFilterCriteria(criteria.NewValue), true); @@ -108,10 +108,14 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private void addRooms(IEnumerable rooms) { - foreach (var room in rooms) - roomFlow.Add(new DrawableLoungeRoom(room) { SelectedRoom = { BindTarget = SelectedRoom } }); + LoadComponentsAsync(rooms.Select(room => + new DrawableLoungeRoom(room) { SelectedRoom = { BindTarget = SelectedRoom } }), rooms => + { + // check against rooms collection to ensure the room wasn't removed since this async load started. + roomFlow.AddRange(rooms.Where(r => this.rooms.Contains(r.Room))); - applyFilterCriteria(Filter?.Value); + applyFilterCriteria(Filter?.Value); + }); } private void removeRooms(IEnumerable rooms) From bebb9d7e675f18e7667ab202335efcb542a1f32b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Sep 2021 17:13:12 +0900 Subject: [PATCH 1758/2442] Wrap main content of `DrawableRoom` --- .../Lounge/Components/DrawableRoom.cs | 286 +++++++++--------- 1 file changed, 146 insertions(+), 140 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 106211c833..964a9eddfb 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -43,6 +43,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private PasswordProtectedIcon passwordIcon; private EndDateInfo endDateInfo; + private DelayedLoadWrapper wrapper; + public DrawableRoom(Room room) { Room = room; @@ -75,155 +77,156 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { d.RelativeSizeAxes = Axes.Both; }), - new Container - { - Name = @"Room content", - RelativeSizeAxes = Axes.Both, - // This negative padding resolves 1px gaps between this background and the background above. - Padding = new MarginPadding { Left = 20, Vertical = -0.5f }, - Child = new Container + wrapper = new DelayedLoadWrapper(() => + new Container { + Name = @"Room content", RelativeSizeAxes = Axes.Both, - Masking = true, - CornerRadius = CORNER_RADIUS, - Children = new Drawable[] + // This negative padding resolves 1px gaps between this background and the background above. + Padding = new MarginPadding { Left = 20, Vertical = -0.5f }, + Child = new Container { - new GridContainer + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = CORNER_RADIUS, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - ColumnDimensions = new[] + new GridContainer { - new Dimension(GridSizeMode.Relative, 0.2f) - }, - Content = new[] - { - new Drawable[] + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colours.Background5, - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal(colours.Background5, colours.Background5.Opacity(0.3f)) - }, - } - } - }, - new Container - { - Name = @"Left details", - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding - { - Left = 20, - Vertical = 5 - }, - Children = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(5), - Children = new Drawable[] - { - new RoomStatusPill - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft - }, - specialCategoryPill = new RoomSpecialCategoryPill - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft - }, - endDateInfo = new EndDateInfo - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - }, - } - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = 3 }, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new RoomNameText(), - new RoomHostText(), - } - } - }, + new Dimension(GridSizeMode.Relative, 0.2f) }, - new FillFlowContainer + Content = new[] { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(5), - Children = new Drawable[] + new Drawable[] { - new PlaylistCountPill + new Box { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + Colour = colours.Background5, }, - new StarRatingRangeDisplay + new Box { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Scale = new Vector2(0.8f) + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientHorizontal(colours.Background5, colours.Background5.Opacity(0.3f)) + }, + } + } + }, + new Container + { + Name = @"Left details", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding + { + Left = 20, + Vertical = 5 + }, + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(5), + Children = new Drawable[] + { + new RoomStatusPill + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft + }, + specialCategoryPill = new RoomSpecialCategoryPill + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft + }, + endDateInfo = new EndDateInfo + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + } + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = 3 }, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new RoomNameText(), + new RoomHostText(), + } + } + }, + }, + new FillFlowContainer + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(5), + Children = new Drawable[] + { + new PlaylistCountPill + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + new StarRatingRangeDisplay + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Scale = new Vector2(0.8f) + } } } } - } - }, - new FillFlowContainer - { - Name = "Right content", - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - Spacing = new Vector2(5), - Padding = new MarginPadding - { - Right = 10, - Vertical = 20, }, - Children = new Drawable[] + new FillFlowContainer { - ButtonsContainer = new Container + Name = "Right content", + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Spacing = new Vector2(5), + Padding = new MarginPadding { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X + Right = 10, + Vertical = 20, }, - recentParticipantsList = new RecentParticipantsList + Children = new Drawable[] { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - NumberOfCircles = NumberOfAvatars + ButtonsContainer = new Container + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X + }, + recentParticipantsList = new RecentParticipantsList + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + NumberOfCircles = NumberOfAvatars + } } - } + }, + passwordIcon = new PasswordProtectedIcon { Alpha = 0 } }, - passwordIcon = new PasswordProtectedIcon { Alpha = 0 } }, - }, - }, + }, 0) }; } @@ -231,23 +234,26 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { base.LoadComplete(); - roomCategory.BindTo(Room.Category); - roomCategory.BindValueChanged(c => + wrapper.DelayedLoadComplete += _ => { - if (c.NewValue == RoomCategory.Spotlight) - specialCategoryPill.Show(); - else - specialCategoryPill.Hide(); - }, true); + roomCategory.BindTo(Room.Category); + roomCategory.BindValueChanged(c => + { + if (c.NewValue == RoomCategory.Spotlight) + specialCategoryPill.Show(); + else + specialCategoryPill.Hide(); + }, true); - roomType.BindTo(Room.Type); - roomType.BindValueChanged(t => - { - endDateInfo.Alpha = t.NewValue == MatchType.Playlists ? 1 : 0; - }, true); + roomType.BindTo(Room.Type); + roomType.BindValueChanged(t => + { + endDateInfo.Alpha = t.NewValue == MatchType.Playlists ? 1 : 0; + }, true); - hasPassword.BindTo(Room.HasPassword); - hasPassword.BindValueChanged(v => passwordIcon.Alpha = v.NewValue ? 1 : 0, true); + hasPassword.BindTo(Room.HasPassword); + hasPassword.BindValueChanged(v => passwordIcon.Alpha = v.NewValue ? 1 : 0, true); + }; } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) From 3e41d8b32e146285b610b93b9f2f6e2d5e69221c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Sep 2021 17:30:13 +0900 Subject: [PATCH 1759/2442] Reduce initial fade transforms --- osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index 6dc3cd18c5..a13d67a0c9 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -83,12 +83,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { base.LoadComplete(); - if (matchingFilter) - this.FadeInFromZero(transition_duration); - else - Alpha = 0; + Alpha = matchingFilter ? 1 : 0; + selectionBox.Alpha = SelectedRoom.Value == Room ? 1 : 0; - SelectedRoom.BindValueChanged(updateSelectedRoom, true); + SelectedRoom.BindValueChanged(updateSelectedRoom); } private void updateSelectedRoom(ValueChangedEvent selected) From 7ed995fbc504f67bc009d0a79ede13e985e0589d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Sep 2021 19:38:47 +0900 Subject: [PATCH 1760/2442] Add test with many rooms displayed --- .../Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs index dafa8300f6..5c248163d7 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs @@ -32,6 +32,12 @@ namespace osu.Game.Tests.Visual.Playlists private RoomsContainer roomsContainer => loungeScreen.ChildrenOfType().First(); + [Test] + public void TestManyRooms() + { + AddStep("add rooms", () => RoomManager.AddRooms(500)); + } + [Test] public void TestScrollByDraggingRooms() { From 7941240a007696d64f77c62afd561e2119c7808e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Sep 2021 20:51:05 +0900 Subject: [PATCH 1761/2442] Revert "Load `DrawableLoungRoom`s asynchronously" This reverts commit 0b55bb6913fbf05ebb8ecadfe711084d6797efe0. --- .../OnlinePlay/Lounge/Components/RoomsContainer.cs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index 8e3c74aba9..76cb02199b 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -64,9 +64,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components protected override void LoadComplete() { + rooms.CollectionChanged += roomsChanged; roomManager.RoomsUpdated += updateSorting; - rooms.CollectionChanged += roomsChanged; rooms.BindTo(roomManager.Rooms); Filter?.BindValueChanged(criteria => applyFilterCriteria(criteria.NewValue), true); @@ -108,14 +108,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private void addRooms(IEnumerable rooms) { - LoadComponentsAsync(rooms.Select(room => - new DrawableLoungeRoom(room) { SelectedRoom = { BindTarget = SelectedRoom } }), rooms => - { - // check against rooms collection to ensure the room wasn't removed since this async load started. - roomFlow.AddRange(rooms.Where(r => this.rooms.Contains(r.Room))); + foreach (var room in rooms) + roomFlow.Add(new DrawableLoungeRoom(room) { SelectedRoom = { BindTarget = SelectedRoom } }); - applyFilterCriteria(Filter?.Value); - }); + applyFilterCriteria(Filter?.Value); } private void removeRooms(IEnumerable rooms) From 136573982c114ec5b626e92f76510bd0fdf253b1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Sep 2021 20:51:16 +0900 Subject: [PATCH 1762/2442] Add fade in and fix incorrect wrapper bounds --- .../Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 964a9eddfb..3a3c07fb19 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -227,6 +227,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components }, }, }, 0) + { + RelativeSizeAxes = Axes.Both, + } }; } @@ -236,6 +239,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components wrapper.DelayedLoadComplete += _ => { + wrapper.FadeInFromZero(200); + roomCategory.BindTo(Room.Category); roomCategory.BindValueChanged(c => { From a622a0b6609bd676cf23a6d536cbc24d998aadf4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Sep 2021 23:07:53 +0900 Subject: [PATCH 1763/2442] Use a more traditional method of caching --- .../Configuration/SettingSourceAttribute.cs | 35 +++++++++++++------ 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/osu.Game/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs index 4fff81b587..4346a583ec 100644 --- a/osu.Game/Configuration/SettingSourceAttribute.cs +++ b/osu.Game/Configuration/SettingSourceAttribute.cs @@ -4,6 +4,7 @@ #nullable enable using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -167,7 +168,30 @@ namespace osu.Game.Configuration } } - public static IEnumerable<(SettingSourceAttribute, PropertyInfo)> GetSettingsSourceProperties(this T obj) => SettingSourceCache.SettingSourceProperties; + private static readonly ConcurrentDictionary> property_info_cache = new ConcurrentDictionary>(); + + public static IEnumerable<(SettingSourceAttribute, PropertyInfo)> GetSettingsSourceProperties(this object obj) + { + var type = obj.GetType(); + + if (!property_info_cache.TryGetValue(type, out var properties)) + property_info_cache[type] = properties = getSettingsSourceProperties(type); + + return properties; + } + + private static IEnumerable<(SettingSourceAttribute, PropertyInfo)> getSettingsSourceProperties(Type type) + { + foreach (var property in type.GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance)) + { + var attr = property.GetCustomAttribute(true); + + if (attr == null) + continue; + + yield return (attr, property); + } + } public static ICollection<(SettingSourceAttribute, PropertyInfo)> GetOrderedSettingsSourceProperties(this object obj) => obj.GetSettingsSourceProperties() @@ -185,14 +209,5 @@ namespace osu.Game.Configuration protected override DropdownMenu CreateMenu() => base.CreateMenu().With(m => m.MaxHeight = 100); } } - - private static class SettingSourceCache - { - public static (SettingSourceAttribute, PropertyInfo)[] SettingSourceProperties { get; } = - typeof(T).GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance) - .Select(property => (property.GetCustomAttribute(), property)) - .Where(pair => pair.Item1 != null) - .ToArray(); - } } } From 6fc46792d3fecfdd78158f5047ca62d4f9bc65a6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Sep 2021 00:39:54 +0900 Subject: [PATCH 1764/2442] Fix test regressions --- .../Visual/Multiplayer/TestSceneDrawableRoom.cs | 2 ++ .../OnlinePlay/Lounge/Components/DrawableRoom.cs | 16 +++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index 3973dc57b2..b1f5781f6f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -130,6 +130,8 @@ namespace osu.Game.Tests.Visual.Multiplayer Type = { Value = MatchType.HeadToHead }, })); + AddUntilStep("wait for panel load", () => drawableRoom.ChildrenOfType().Any()); + AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType().Single().Alpha)); AddStep("set password", () => room.Password.Value = "password"); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 3a3c07fb19..80070aa6ba 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -65,6 +65,14 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components [BackgroundDependencyLoader] private void load(OverlayColourProvider colours) { + ButtonsContainer = new Container + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X + }; + InternalChildren = new[] { // This resolves internal 1px gaps due to applying the (parenting) corner radius and masking across multiple filling background sprites. @@ -208,13 +216,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components }, Children = new Drawable[] { - ButtonsContainer = new Container - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X - }, + ButtonsContainer, recentParticipantsList = new RecentParticipantsList { Anchor = Anchor.CentreRight, From ba99a808af624d4a2ae9c4a77dc0f3be8a25f535 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Sep 2021 01:21:19 +0900 Subject: [PATCH 1765/2442] Use a decoupled clock for triangles intro to avoid startup freezes on broken audio device --- osu.Game/Screens/Menu/IntroScreen.cs | 2 +- osu.Game/Screens/Menu/IntroTriangles.cs | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index 07a94fb97e..ac4a53f4a9 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -165,7 +165,7 @@ namespace osu.Game.Screens.Menu protected override BackgroundScreen CreateBackground() => new BackgroundScreenBlack(); - protected void StartTrack() + protected virtual void StartTrack() { // Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Menu. if (UsingThemedIntro) diff --git a/osu.Game/Screens/Menu/IntroTriangles.cs b/osu.Game/Screens/Menu/IntroTriangles.cs index 0ea83fe5e7..69333d4a12 100644 --- a/osu.Game/Screens/Menu/IntroTriangles.cs +++ b/osu.Game/Screens/Menu/IntroTriangles.cs @@ -41,6 +41,8 @@ namespace osu.Game.Screens.Menu private Sample welcome; + private DecoupleableInterpolatingFramedClock decoupledClock; + [BackgroundDependencyLoader] private void load() { @@ -56,10 +58,18 @@ namespace osu.Game.Screens.Menu { PrepareMenuLoad(); + decoupledClock = new DecoupleableInterpolatingFramedClock + { + IsCoupled = false + }; + + if (UsingThemedIntro) + decoupledClock.ChangeSource(Track); + LoadComponentAsync(new TrianglesIntroSequence(logo, background) { RelativeSizeAxes = Axes.Both, - Clock = new FramedClock(UsingThemedIntro ? Track : null), + Clock = decoupledClock, LoadMenu = LoadMenu }, t => { @@ -78,6 +88,12 @@ namespace osu.Game.Screens.Menu background.FadeOut(100); } + protected override void StartTrack() + { + if (UsingThemedIntro) + decoupledClock.Start(); + } + private class TrianglesIntroSequence : CompositeDrawable { private readonly OsuLogo logo; From 9b05bf3a2cf8db1fc9c18e06cbe9f7952110bef0 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 8 Sep 2021 11:43:59 -0700 Subject: [PATCH 1766/2442] Fix volume meter not being highlighted when hovering before show --- osu.Game/Overlays/Volume/VolumeMeter.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs index f4cbbf5a00..7249dd77e5 100644 --- a/osu.Game/Overlays/Volume/VolumeMeter.cs +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -355,6 +355,12 @@ namespace osu.Game.Overlays.Volume return base.OnMouseMove(e); } + protected override bool OnHover(HoverEvent e) + { + State = SelectionState.Selected; + return false; + } + protected override void OnHoverLost(HoverLostEvent e) { } From 2888623bdb417a707ddb76939d27f6b7d65bf985 Mon Sep 17 00:00:00 2001 From: Emil Olesen Date: Wed, 8 Sep 2021 22:02:24 +0200 Subject: [PATCH 1767/2442] Extended the width of the ResetSectionButton to be equal to Content.Width. Fixes #14685 --- .../Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs index ef5ccae1a0..2e49671669 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs @@ -67,7 +67,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input { Text = InputSettingsStrings.ResetSectionButton; RelativeSizeAxes = Axes.X; - Width = 0.5f; + Width = Content.Width; Anchor = Anchor.TopCentre; Origin = Anchor.TopCentre; Margin = new MarginPadding { Top = 15 }; From 46a2e6ce424b03a6a411ef485ee26352c5d80e65 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 8 Sep 2021 14:57:38 -0700 Subject: [PATCH 1768/2442] Specify minimum windows version --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8f922f74a7..d1ad63663f 100644 --- a/README.md +++ b/README.md @@ -31,12 +31,12 @@ If you are looking to install or test osu! without setting up a development envi **Latest build:** -| [Windows (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.12+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS(iOS 10+)](https://osu.ppy.sh/home/testflight) | [Android (5+)](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk) +| [Windows 8.1+ (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.12+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS(iOS 10+)](https://osu.ppy.sh/home/testflight) | [Android (5+)](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk) | ------------- | ------------- | ------------- | ------------- | ------------- | - The iOS testflight link may fill up (Apple has a hard limit of 10,000 users). We reset it occasionally when this happens. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements of link resets. -- When running on Windows 7 or 8.1, *[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/install/windows?tabs=net50&pivots=os-windows#dependencies)** may be required to correctly run .NET 5 applications if your operating system is not up-to-date with the latest service packs. +- When running on Windows 8.1, *[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/install/windows?tabs=net50&pivots=os-windows#dependencies)** may be required to correctly run .NET 5 applications if your operating system is not up-to-date with the latest service packs. If your platform is not listed above, there is still a chance you can manually build it by following the instructions below. ## Developing a custom ruleset From ff4d890a441e198e77cd508a36a218ea78760640 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 8 Sep 2021 15:01:53 -0700 Subject: [PATCH 1769/2442] Normalise format of listed OSes --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d1ad63663f..f4313eea8c 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ If you are looking to install or test osu! without setting up a development envi **Latest build:** -| [Windows 8.1+ (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.12+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS(iOS 10+)](https://osu.ppy.sh/home/testflight) | [Android (5+)](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk) +| [Windows 8.1+ (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.12+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS 10+](https://osu.ppy.sh/home/testflight) | [Android 5+](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk) | ------------- | ------------- | ------------- | ------------- | ------------- | - The iOS testflight link may fill up (Apple has a hard limit of 10,000 users). We reset it occasionally when this happens. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements of link resets. From 31bb2055f9b825b7afc1a0f4e660d4451d66fad3 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 8 Sep 2021 15:06:39 -0700 Subject: [PATCH 1770/2442] Remove stray space on table --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f4313eea8c..9dd5280d13 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ If you are looking to install or test osu! without setting up a development envi **Latest build:** -| [Windows 8.1+ (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.12+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS 10+](https://osu.ppy.sh/home/testflight) | [Android 5+](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk) +| [Windows 8.1+ (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.12+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS 10+](https://osu.ppy.sh/home/testflight) | [Android 5+](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk) | ------------- | ------------- | ------------- | ------------- | ------------- | - The iOS testflight link may fill up (Apple has a hard limit of 10,000 users). We reset it occasionally when this happens. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements of link resets. From 34a8607b20b6e95eeaaa7630d7d17a38f6ca5cae Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 8 Sep 2021 20:08:25 -0700 Subject: [PATCH 1771/2442] Remove unnecessary prerequisites for windows 8.1 --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 9dd5280d13..786ce2589d 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,6 @@ If you are looking to install or test osu! without setting up a development envi - The iOS testflight link may fill up (Apple has a hard limit of 10,000 users). We reset it occasionally when this happens. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements of link resets. -- When running on Windows 8.1, *[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/install/windows?tabs=net50&pivots=os-windows#dependencies)** may be required to correctly run .NET 5 applications if your operating system is not up-to-date with the latest service packs. If your platform is not listed above, there is still a chance you can manually build it by following the instructions below. ## Developing a custom ruleset From 9a0dbaa8e34152fefd7a18fe33e111cb8539b26a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Sep 2021 12:39:28 +0900 Subject: [PATCH 1772/2442] Remove default parameter from build benchmark project configuration --- .../.idea/runConfigurations/Benchmarks.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.idea/.idea.osu.Desktop/.idea/runConfigurations/Benchmarks.xml b/.idea/.idea.osu.Desktop/.idea/runConfigurations/Benchmarks.xml index 8fa7608b8e..498a710df9 100644 --- a/.idea/.idea.osu.Desktop/.idea/runConfigurations/Benchmarks.xml +++ b/.idea/.idea.osu.Desktop/.idea/runConfigurations/Benchmarks.xml @@ -1,8 +1,8 @@ - \ No newline at end of file From 210640af0924f5e77ccad3633d06f7b898cb86f7 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 9 Sep 2021 12:39:40 +0900 Subject: [PATCH 1773/2442] Fix overlay not refreshed in `TestSceneBeatmapSetOverlay` --- .../Visual/Online/TestSceneBeatmapSetOverlay.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index de7e4075f3..40c8fae50a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -11,6 +11,7 @@ using osu.Game.Users; using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Utils; namespace osu.Game.Tests.Visual.Online { @@ -240,7 +241,7 @@ namespace osu.Game.Tests.Visual.Online { AddStep("show explicit map", () => { - var beatmapSet = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet; + var beatmapSet = getBeatmapSet(); beatmapSet.OnlineInfo.HasExplicitContent = true; overlay.ShowBeatmapSet(beatmapSet); }); @@ -251,7 +252,7 @@ namespace osu.Game.Tests.Visual.Online { AddStep("show featured map", () => { - var beatmapSet = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet; + var beatmapSet = getBeatmapSet(); beatmapSet.OnlineInfo.TrackId = 1; overlay.ShowBeatmapSet(beatmapSet); }); @@ -319,6 +320,14 @@ namespace osu.Game.Tests.Visual.Online }; } + private BeatmapSetInfo getBeatmapSet() + { + var beatmapSet = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet; + // Overlay doesn't reload if the same beatmap set is set. + beatmapSet.OnlineBeatmapSetID = RNG.Next(); + return beatmapSet; + } + private void downloadAssert(bool shown) { AddAssert($"is download button {(shown ? "shown" : "hidden")}", () => overlay.Header.HeaderContent.DownloadButtonsVisible == shown); From 45a534a1ba0f1c836215c5a4b1578da733c50fdf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Sep 2021 12:39:45 +0900 Subject: [PATCH 1774/2442] Fix duplicated `GlobalSetup` attribute on `BenchmarkMod` --- osu.Game.Benchmarks/BenchmarkMod.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Benchmarks/BenchmarkMod.cs b/osu.Game.Benchmarks/BenchmarkMod.cs index 050ddf36d5..c5375e9f09 100644 --- a/osu.Game.Benchmarks/BenchmarkMod.cs +++ b/osu.Game.Benchmarks/BenchmarkMod.cs @@ -14,9 +14,9 @@ namespace osu.Game.Benchmarks [Params(1, 10, 100)] public int Times { get; set; } - [GlobalSetup] - public void GlobalSetup() + public override void SetUp() { + base.SetUp(); mod = new OsuModDoubleTime(); } From 278584de99e416aed21ef3034a88d072d61de2c4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Sep 2021 12:40:05 +0900 Subject: [PATCH 1775/2442] Disable optimisations validator (in line with framework project) --- osu.Game.Benchmarks/Program.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Benchmarks/Program.cs b/osu.Game.Benchmarks/Program.cs index c55075fea6..439ced53ab 100644 --- a/osu.Game.Benchmarks/Program.cs +++ b/osu.Game.Benchmarks/Program.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using BenchmarkDotNet.Configs; using BenchmarkDotNet.Running; namespace osu.Game.Benchmarks @@ -11,7 +12,7 @@ namespace osu.Game.Benchmarks { BenchmarkSwitcher .FromAssembly(typeof(Program).Assembly) - .Run(args); + .Run(args, DefaultConfig.Instance.WithOption(ConfigOptions.DisableOptimizationsValidator, true)); } } } From 52bb02baed7538157654dd7e9b470842862c6e70 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 9 Sep 2021 12:51:21 +0900 Subject: [PATCH 1776/2442] Prefer composite over inheritance for drawable parts --- .../BeatmapSet/BeatmapSetBadgePill.cs | 32 ------------- .../BeatmapSetBadgePillContainer.cs | 45 +++++++++++++++++++ .../BeatmapSet/ExplicitContentBeatmapPill.cs | 22 ++++----- .../BeatmapSet/FeaturedArtistBeatmapPill.cs | 22 ++++----- 4 files changed, 69 insertions(+), 52 deletions(-) delete mode 100644 osu.Game/Overlays/BeatmapSet/BeatmapSetBadgePill.cs create mode 100644 osu.Game/Overlays/BeatmapSet/BeatmapSetBadgePillContainer.cs diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetBadgePill.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetBadgePill.cs deleted file mode 100644 index 15c691103a..0000000000 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetBadgePill.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using JetBrains.Annotations; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using osuTK.Graphics; - -namespace osu.Game.Overlays.BeatmapSet -{ - public class BeatmapSetBadgePill : CircularContainer - { - public BeatmapSetBadgePill() - { - AutoSizeAxes = Axes.Both; - Masking = true; - } - - [BackgroundDependencyLoader(true)] - private void load([CanBeNull] OsuColour colours, [CanBeNull] OverlayColourProvider colourProvider) - { - Add(new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider?.Background5 ?? colours?.Gray2 ?? Color4.DarkGray, - }); - } - } -} diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetBadgePillContainer.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetBadgePillContainer.cs new file mode 100644 index 0000000000..62bbc35a2d --- /dev/null +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetBadgePillContainer.cs @@ -0,0 +1,45 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using JetBrains.Annotations; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osuTK.Graphics; + +namespace osu.Game.Overlays.BeatmapSet +{ + public class BeatmapSetBadgePillContainer : CircularContainer + { + protected override Container Content => contentContainer; + + private readonly Box background; + private readonly Container contentContainer; + + public BeatmapSetBadgePillContainer() + { + Masking = true; + AutoSizeAxes = Axes.Both; + InternalChildren = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + }, + contentContainer = new Container + { + AutoSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = 10f, Vertical = 2f }, + } + }; + } + + [BackgroundDependencyLoader(true)] + private void load([CanBeNull] OsuColour colours, [CanBeNull] OverlayColourProvider colourProvider) + { + background.Colour = colourProvider?.Background5 ?? colours?.Gray2 ?? Color4.DarkGray; + } + } +} diff --git a/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs b/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs index 60fa3ea900..4bd3f132f9 100644 --- a/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs +++ b/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs @@ -1,27 +1,29 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapSet { - public class ExplicitContentBeatmapPill : BeatmapSetBadgePill + public class ExplicitContentBeatmapPill : CompositeDrawable { - [BackgroundDependencyLoader] - private void load() + public ExplicitContentBeatmapPill() { - Add(new OsuSpriteText + AutoSizeAxes = Axes.Both; + InternalChild = new BeatmapSetBadgePillContainer { - Margin = new MarginPadding { Horizontal = 10f, Vertical = 2f }, - Text = BeatmapsetsStrings.NsfwBadgeLabel.ToUpper(), - Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), - Colour = OverlayColourProvider.Orange.Colour2, - }); + Child = new OsuSpriteText + { + Text = BeatmapsetsStrings.NsfwBadgeLabel.ToUpper(), + Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), + Colour = OverlayColourProvider.Orange.Colour2, + } + }; } } } diff --git a/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs b/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs index 7facc2953a..a9950b1b60 100644 --- a/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs +++ b/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs @@ -1,27 +1,29 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.BeatmapSet { - public class FeaturedArtistBeatmapPill : BeatmapSetBadgePill + public class FeaturedArtistBeatmapPill : CompositeDrawable { - [BackgroundDependencyLoader] - private void load() + public FeaturedArtistBeatmapPill() { - Add(new OsuSpriteText + AutoSizeAxes = Axes.Both; + InternalChild = new BeatmapSetBadgePillContainer { - Margin = new MarginPadding { Horizontal = 10f, Vertical = 2f }, - Text = BeatmapsetsStrings.FeaturedArtistBadgeLabel.ToUpper(), - Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), - Colour = OverlayColourProvider.Blue.Colour1 - }); + Child = new OsuSpriteText + { + Text = BeatmapsetsStrings.FeaturedArtistBadgeLabel.ToUpper(), + Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), + Colour = OverlayColourProvider.Blue.Colour1 + } + }; } } } From 41a2e6eeeba92bfd9e0b1d8e896f6c283d3d6e1c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Sep 2021 14:56:39 +0900 Subject: [PATCH 1777/2442] Fix cache not actually caching anything --- osu.Game/Configuration/SettingSourceAttribute.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs index 4346a583ec..5db502804d 100644 --- a/osu.Game/Configuration/SettingSourceAttribute.cs +++ b/osu.Game/Configuration/SettingSourceAttribute.cs @@ -168,14 +168,14 @@ namespace osu.Game.Configuration } } - private static readonly ConcurrentDictionary> property_info_cache = new ConcurrentDictionary>(); + private static readonly ConcurrentDictionary property_info_cache = new ConcurrentDictionary(); public static IEnumerable<(SettingSourceAttribute, PropertyInfo)> GetSettingsSourceProperties(this object obj) { var type = obj.GetType(); if (!property_info_cache.TryGetValue(type, out var properties)) - property_info_cache[type] = properties = getSettingsSourceProperties(type); + property_info_cache[type] = properties = getSettingsSourceProperties(type).ToArray(); return properties; } From efaf07dbc8db673737ab14d80f37f8e0fcf18f82 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Sep 2021 14:47:19 +0900 Subject: [PATCH 1778/2442] Add benchmark coverage of mod retrieval --- osu.Game.Benchmarks/BenchmarkRuleset.cs | 42 +++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 osu.Game.Benchmarks/BenchmarkRuleset.cs diff --git a/osu.Game.Benchmarks/BenchmarkRuleset.cs b/osu.Game.Benchmarks/BenchmarkRuleset.cs new file mode 100644 index 0000000000..1b315b5001 --- /dev/null +++ b/osu.Game.Benchmarks/BenchmarkRuleset.cs @@ -0,0 +1,42 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using BenchmarkDotNet.Attributes; +using osu.Game.Online.API; +using osu.Game.Rulesets.Osu; + +namespace osu.Game.Benchmarks +{ + public class BenchmarkRuleset : BenchmarkTest + { + private OsuRuleset ruleset; + private APIMod apiModDoubleTime; + private APIMod apiModDifficultyAdjust; + + public override void SetUp() + { + base.SetUp(); + ruleset = new OsuRuleset(); + apiModDoubleTime = new APIMod { Acronym = "DT" }; + apiModDifficultyAdjust = new APIMod { Acronym = "DA" }; + } + + [Benchmark] + public void BenchmarkToModDoubleTime() + { + apiModDoubleTime.ToMod(ruleset); + } + + [Benchmark] + public void BenchmarkToModDifficultyAdjust() + { + apiModDifficultyAdjust.ToMod(ruleset); + } + + [Benchmark] + public void BenchmarkGetAllMods() + { + ruleset.GetAllMods(); + } + } +} From 4708cb7317f21287ab7448d9209d6c7eedf4681a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Sep 2021 15:15:18 +0900 Subject: [PATCH 1779/2442] Fix enumerable not being consumed --- osu.Game.Benchmarks/BenchmarkRuleset.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Benchmarks/BenchmarkRuleset.cs b/osu.Game.Benchmarks/BenchmarkRuleset.cs index 1b315b5001..63c99dcb2b 100644 --- a/osu.Game.Benchmarks/BenchmarkRuleset.cs +++ b/osu.Game.Benchmarks/BenchmarkRuleset.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Engines; using osu.Game.Online.API; using osu.Game.Rulesets.Osu; @@ -36,7 +37,7 @@ namespace osu.Game.Benchmarks [Benchmark] public void BenchmarkGetAllMods() { - ruleset.GetAllMods(); + ruleset.GetAllMods().Consume(new Consumer()); } } } From b01cf5c937bad88ffea9ad1df75bf9f0dfb813dd Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 9 Sep 2021 15:33:47 +0900 Subject: [PATCH 1780/2442] Don't play hover/select sounds for UpdatableAvatar unless it's interactable --- osu.Game/Users/Drawables/UpdateableAvatar.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game/Users/Drawables/UpdateableAvatar.cs b/osu.Game/Users/Drawables/UpdateableAvatar.cs index df724404e9..a8726d0cab 100644 --- a/osu.Game/Users/Drawables/UpdateableAvatar.cs +++ b/osu.Game/Users/Drawables/UpdateableAvatar.cs @@ -69,14 +69,20 @@ namespace osu.Game.Users.Drawables if (user == null && !showGuestOnNull) return null; - var avatar = new ClickableAvatar(user) + if (!openOnClick) + { + return new DrawableAvatar(user) + { + RelativeSizeAxes = Axes.Both, + }; + } + + return new ClickableAvatar(user) { OpenOnClick = openOnClick, ShowUsernameTooltip = showUsernameTooltip, RelativeSizeAxes = Axes.Both, }; - - return avatar; } } } From e66d76d26e2979cd5a858aa2aa2b0061c36856ab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Sep 2021 15:43:55 +0900 Subject: [PATCH 1781/2442] Avoid settings copy if there are no settings --- osu.Game/Online/API/APIMod.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs index 4427c82a8b..596d567480 100644 --- a/osu.Game/Online/API/APIMod.cs +++ b/osu.Game/Online/API/APIMod.cs @@ -53,12 +53,15 @@ namespace osu.Game.Online.API if (resultMod == null) throw new InvalidOperationException($"There is no mod in the ruleset ({ruleset.ShortName}) matching the acronym {Acronym}."); - foreach (var (_, property) in resultMod.GetSettingsSourceProperties()) + if (Settings.Count > 0) { - if (!Settings.TryGetValue(property.Name.Underscore(), out object settingValue)) - continue; + foreach (var (_, property) in resultMod.GetSettingsSourceProperties()) + { + if (!Settings.TryGetValue(property.Name.Underscore(), out object settingValue)) + continue; - resultMod.CopyAdjustedSetting((IBindable)property.GetValue(resultMod), settingValue); + resultMod.CopyAdjustedSetting((IBindable)property.GetValue(resultMod), settingValue); + } } return resultMod; From 4d0530ca9d1b2d110fc407bac3cc112161367bfb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Sep 2021 16:34:49 +0900 Subject: [PATCH 1782/2442] Add new methods to ruleset for quicker mod lookups --- osu.Game.Benchmarks/BenchmarkRuleset.cs | 6 +++ .../Components/TournamentModIcon.cs | 2 +- osu.Game/Online/API/APIMod.cs | 2 +- .../Requests/Responses/APILegacyScoreInfo.cs | 2 +- .../BeatmapSet/LeaderboardModSelector.cs | 2 +- .../IncompatibilityDisplayingModButton.cs | 2 +- osu.Game/Rulesets/Ruleset.cs | 40 +++++++++++++++++++ 7 files changed, 51 insertions(+), 5 deletions(-) diff --git a/osu.Game.Benchmarks/BenchmarkRuleset.cs b/osu.Game.Benchmarks/BenchmarkRuleset.cs index 63c99dcb2b..f3d678a2b0 100644 --- a/osu.Game.Benchmarks/BenchmarkRuleset.cs +++ b/osu.Game.Benchmarks/BenchmarkRuleset.cs @@ -39,5 +39,11 @@ namespace osu.Game.Benchmarks { ruleset.GetAllMods().Consume(new Consumer()); } + + [Benchmark] + public void BenchmarkGetAllModsForReference() + { + ruleset.GetAllModsForReference().Consume(new Consumer()); + } } } diff --git a/osu.Game.Tournament/Components/TournamentModIcon.cs b/osu.Game.Tournament/Components/TournamentModIcon.cs index 43ac92d285..709e99b165 100644 --- a/osu.Game.Tournament/Components/TournamentModIcon.cs +++ b/osu.Game.Tournament/Components/TournamentModIcon.cs @@ -49,7 +49,7 @@ namespace osu.Game.Tournament.Components } var ruleset = rulesets.GetRuleset(ladderInfo.Ruleset.Value?.ID ?? 0); - var modIcon = ruleset?.CreateInstance().GetAllMods().FirstOrDefault(mod => mod.Acronym == modAcronym); + var modIcon = ruleset?.CreateInstance().GetModForAcronym(modAcronym); if (modIcon == null) return; diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs index 596d567480..f31f8bc92a 100644 --- a/osu.Game/Online/API/APIMod.cs +++ b/osu.Game/Online/API/APIMod.cs @@ -48,7 +48,7 @@ namespace osu.Game.Online.API public Mod ToMod(Ruleset ruleset) { - Mod resultMod = ruleset.GetAllMods().FirstOrDefault(m => m.Acronym == Acronym); + Mod resultMod = ruleset.GetModForAcronym(Acronym); if (resultMod == null) throw new InvalidOperationException($"There is no mod in the ruleset ({ruleset.ShortName}) matching the acronym {Acronym}."); diff --git a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs index 1b394185fd..e74db7de6e 100644 --- a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs @@ -23,7 +23,7 @@ namespace osu.Game.Online.API.Requests.Responses var rulesetInstance = ruleset.CreateInstance(); - var mods = Mods != null ? rulesetInstance.GetAllMods().Where(mod => Mods.Contains(mod.Acronym)).ToArray() : Array.Empty(); + var mods = Mods != null ? Mods.Select(acronym => rulesetInstance.GetModForAcronym(acronym)).Where(m => m != null).ToArray() : Array.Empty(); // all API scores provided by this class are considered to be legacy. mods = mods.Append(rulesetInstance.GetAllMods().OfType().Single()).ToArray(); diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index 5b903372fd..517da11c8c 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -54,7 +54,7 @@ namespace osu.Game.Overlays.BeatmapSet return; modsContainer.Add(new ModButton(new ModNoMod())); - modsContainer.AddRange(ruleset.NewValue.CreateInstance().GetAllMods().Where(m => m.UserPlayable).Select(m => new ModButton(m))); + modsContainer.AddRange(ruleset.NewValue.CreateInstance().GetAllModsForReference().Where(m => m.UserPlayable).Select(m => new ModButton(m))); modsContainer.ForEach(button => { diff --git a/osu.Game/Overlays/Mods/IncompatibilityDisplayingModButton.cs b/osu.Game/Overlays/Mods/IncompatibilityDisplayingModButton.cs index c8e44ee159..8d0746f24d 100644 --- a/osu.Game/Overlays/Mods/IncompatibilityDisplayingModButton.cs +++ b/osu.Game/Overlays/Mods/IncompatibilityDisplayingModButton.cs @@ -107,7 +107,7 @@ namespace osu.Game.Overlays.Mods var incompatibleTypes = mod.IncompatibleMods; - var allMods = ruleset.Value.CreateInstance().GetAllMods(); + var allMods = ruleset.Value.CreateInstance().GetAllModsForReference(); incompatibleMods.Value = allMods.Where(m => m.GetType() != mod.GetType() && incompatibleTypes.Any(t => t.IsInstanceOfType(m))).ToList(); incompatibleText.Text = incompatibleMods.Value.Any() ? "Incompatible with:" : "Compatible with all mods"; diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index de62cf8d33..2f65bd76a4 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; @@ -23,6 +24,7 @@ using osu.Game.Scoring; using osu.Game.Skinning; using osu.Game.Users; using JetBrains.Annotations; +using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Testing; @@ -38,6 +40,13 @@ namespace osu.Game.Rulesets { public RulesetInfo RulesetInfo { get; internal set; } + /// + /// Returns fresh instances of all mods. + /// + /// + /// This comes with considerable allocation overhead. If only accessing for reference purposes (ie. not changing bindables / settings) + /// use instead. + /// public IEnumerable GetAllMods() => Enum.GetValues(typeof(ModType)).Cast() // Confine all mods of each mod type into a single IEnumerable .SelectMany(GetModsFor) @@ -46,6 +55,37 @@ namespace osu.Game.Rulesets // Resolve MultiMods as their .Mods property .SelectMany(mod => (mod as MultiMod)?.Mods ?? new[] { mod }); + private static readonly ConcurrentDictionary mod_reference_cache = new ConcurrentDictionary(); + + /// + /// Returns all mods for a query-only purpose. + /// Bindables should not be considered usable when retrieving via this method (use instead). + /// + public IEnumerable GetAllModsForReference() + { + if (!(RulesetInfo.ID is int id)) + return GetAllMods(); + + if (!mod_reference_cache.TryGetValue(id, out var mods)) + mod_reference_cache[id] = mods = GetAllMods().ToArray(); + + return mods; + } + + /// + /// Returns a fresh instance of the mod matching the specified acronym. + /// + /// The acronym to query for . + public Mod GetModForAcronym(string acronym) + { + var type = GetAllModsForReference().FirstOrDefault(m => m.Acronym == acronym)?.GetType(); + + if (type != null) + return (Mod)Activator.CreateInstance(type); + + return null; + } + public abstract IEnumerable GetModsFor(ModType type); /// From 2edb8510081ee30e14f3c9feec7451319acd9e0a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Sep 2021 16:46:24 +0900 Subject: [PATCH 1783/2442] Add ability to lookup mod from a type specification --- osu.Game.Benchmarks/BenchmarkRuleset.cs | 13 +++++++++++++ .../Components/TournamentModIcon.cs | 1 - .../Requests/Responses/APILegacyScoreInfo.cs | 2 +- osu.Game/OsuGameBase.cs | 2 +- osu.Game/Rulesets/Ruleset.cs | 17 +++++++++++++++-- osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs | 2 +- osu.Game/Tests/Visual/PlayerTestScene.cs | 2 +- 7 files changed, 32 insertions(+), 7 deletions(-) diff --git a/osu.Game.Benchmarks/BenchmarkRuleset.cs b/osu.Game.Benchmarks/BenchmarkRuleset.cs index f3d678a2b0..aa59cb7fa3 100644 --- a/osu.Game.Benchmarks/BenchmarkRuleset.cs +++ b/osu.Game.Benchmarks/BenchmarkRuleset.cs @@ -4,6 +4,7 @@ using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Engines; using osu.Game.Online.API; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; namespace osu.Game.Benchmarks @@ -45,5 +46,17 @@ namespace osu.Game.Benchmarks { ruleset.GetAllModsForReference().Consume(new Consumer()); } + + [Benchmark] + public void BenchmarkGetForAcronym() + { + ruleset.GetModForAcronym("DT"); + } + + [Benchmark] + public void BenchmarkGetForType() + { + ruleset.GetMod(); + } } } diff --git a/osu.Game.Tournament/Components/TournamentModIcon.cs b/osu.Game.Tournament/Components/TournamentModIcon.cs index 709e99b165..b8486b5b2d 100644 --- a/osu.Game.Tournament/Components/TournamentModIcon.cs +++ b/osu.Game.Tournament/Components/TournamentModIcon.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.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; diff --git a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs index e74db7de6e..d1c6f0a55a 100644 --- a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs @@ -26,7 +26,7 @@ namespace osu.Game.Online.API.Requests.Responses var mods = Mods != null ? Mods.Select(acronym => rulesetInstance.GetModForAcronym(acronym)).Where(m => m != null).ToArray() : Array.Empty(); // all API scores provided by this class are considered to be legacy. - mods = mods.Append(rulesetInstance.GetAllMods().OfType().Single()).ToArray(); + mods = mods.Append(rulesetInstance.GetMod()).ToArray(); var scoreInfo = new ScoreInfo { diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index f4db0f2603..d52a07266c 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -166,7 +166,7 @@ namespace osu.Game public OsuGameBase() { - UseDevelopmentServer = DebugUtils.IsDebugBuild; + UseDevelopmentServer = false; Name = @"osu!"; } diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 2f65bd76a4..34e4606133 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -24,7 +24,6 @@ using osu.Game.Scoring; using osu.Game.Skinning; using osu.Game.Users; using JetBrains.Annotations; -using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Testing; @@ -86,6 +85,20 @@ namespace osu.Game.Rulesets return null; } + /// + /// Returns a fresh instance of the mod matching the specified type. + /// + public T GetMod() + where T : Mod + { + var type = GetAllModsForReference().FirstOrDefault(m => m is T)?.GetType(); + + if (type != null) + return (T)Activator.CreateInstance(type); + + return null; + } + public abstract IEnumerable GetModsFor(ModType type); /// @@ -166,7 +179,7 @@ namespace osu.Game.Rulesets } [CanBeNull] - public ModAutoplay GetAutoplayMod() => GetAllMods().OfType().FirstOrDefault(); + public ModAutoplay GetAutoplayMod() => GetMod(); public virtual ISkin CreateLegacySkinProvider([NotNull] ISkin skin, IBeatmap beatmap) => null; diff --git a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs index 2f17167297..010f33e3ed 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs @@ -67,7 +67,7 @@ namespace osu.Game.Scoring.Legacy // lazer replays get a really high version number. if (version < LegacyScoreEncoder.FIRST_LAZER_VERSION) - scoreInfo.Mods = scoreInfo.Mods.Append(currentRuleset.GetAllMods().OfType().Single()).ToArray(); + scoreInfo.Mods = scoreInfo.Mods.Append(currentRuleset.GetMod()).ToArray(); currentBeatmap = workingBeatmap.GetPlayableBeatmap(currentRuleset.RulesetInfo, scoreInfo.Mods); scoreInfo.Beatmap = currentBeatmap.BeatmapInfo; diff --git a/osu.Game/Tests/Visual/PlayerTestScene.cs b/osu.Game/Tests/Visual/PlayerTestScene.cs index 93491c800f..a31a6433ea 100644 --- a/osu.Game/Tests/Visual/PlayerTestScene.cs +++ b/osu.Game/Tests/Visual/PlayerTestScene.cs @@ -67,7 +67,7 @@ namespace osu.Game.Tests.Visual if (!AllowFail) { - var noFailMod = ruleset.GetAllMods().FirstOrDefault(m => m is ModNoFail); + var noFailMod = ruleset.GetMod(); if (noFailMod != null) SelectedMods.Value = new[] { noFailMod }; } From 9b34ffc3029e20027f32ac866587853d60d76eeb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Sep 2021 17:12:38 +0900 Subject: [PATCH 1784/2442] Undo big oopsie --- 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 d52a07266c..f4db0f2603 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -166,7 +166,7 @@ namespace osu.Game public OsuGameBase() { - UseDevelopmentServer = false; + UseDevelopmentServer = DebugUtils.IsDebugBuild; Name = @"osu!"; } From 29f947fa07ac5a550f8470a0b178117021144d9a Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 9 Sep 2021 19:25:30 +0900 Subject: [PATCH 1785/2442] Revert `ExplicitContentBeatmapPill`, don't try to reuse common code --- .../BeatmapSetBadgePillContainer.cs | 45 ------------------- .../BeatmapSet/ExplicitContentBeatmapPill.cs | 28 +++++++++--- .../BeatmapSet/FeaturedArtistBeatmapPill.cs | 28 +++++++++--- 3 files changed, 46 insertions(+), 55 deletions(-) delete mode 100644 osu.Game/Overlays/BeatmapSet/BeatmapSetBadgePillContainer.cs diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetBadgePillContainer.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetBadgePillContainer.cs deleted file mode 100644 index 62bbc35a2d..0000000000 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetBadgePillContainer.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using JetBrains.Annotations; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using osuTK.Graphics; - -namespace osu.Game.Overlays.BeatmapSet -{ - public class BeatmapSetBadgePillContainer : CircularContainer - { - protected override Container Content => contentContainer; - - private readonly Box background; - private readonly Container contentContainer; - - public BeatmapSetBadgePillContainer() - { - Masking = true; - AutoSizeAxes = Axes.Both; - InternalChildren = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both, - }, - contentContainer = new Container - { - AutoSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 10f, Vertical = 2f }, - } - }; - } - - [BackgroundDependencyLoader(true)] - private void load([CanBeNull] OsuColour colours, [CanBeNull] OverlayColourProvider colourProvider) - { - background.Colour = colourProvider?.Background5 ?? colours?.Gray2 ?? Color4.DarkGray; - } - } -} diff --git a/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs b/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs index 4bd3f132f9..ba78592ed2 100644 --- a/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs +++ b/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs @@ -1,9 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Resources.Localisation.Web; @@ -15,13 +17,29 @@ namespace osu.Game.Overlays.BeatmapSet public ExplicitContentBeatmapPill() { AutoSizeAxes = Axes.Both; - InternalChild = new BeatmapSetBadgePillContainer + } + + [BackgroundDependencyLoader(true)] + private void load(OsuColour colours, OverlayColourProvider colourProvider) + { + InternalChild = new CircularContainer { - Child = new OsuSpriteText + Masking = true, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] { - Text = BeatmapsetsStrings.NsfwBadgeLabel.ToUpper(), - Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), - Colour = OverlayColourProvider.Orange.Colour2, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider?.Background5 ?? colours.Gray2, + }, + new OsuSpriteText + { + Margin = new MarginPadding { Horizontal = 10f, Vertical = 2f }, + Text = BeatmapsetsStrings.NsfwBadgeLabel.ToUpper(), + Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), + Colour = OverlayColourProvider.Orange.Colour2, + } } }; } diff --git a/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs b/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs index a9950b1b60..fdee0799ff 100644 --- a/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs +++ b/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs @@ -1,9 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Resources.Localisation.Web; @@ -15,13 +17,29 @@ namespace osu.Game.Overlays.BeatmapSet public FeaturedArtistBeatmapPill() { AutoSizeAxes = Axes.Both; - InternalChild = new BeatmapSetBadgePillContainer + } + + [BackgroundDependencyLoader(true)] + private void load(OsuColour colours, OverlayColourProvider colourProvider) + { + InternalChild = new CircularContainer { - Child = new OsuSpriteText + Masking = true, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] { - Text = BeatmapsetsStrings.FeaturedArtistBadgeLabel.ToUpper(), - Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), - Colour = OverlayColourProvider.Blue.Colour1 + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider?.Background5 ?? colours.Gray2, + }, + new OsuSpriteText + { + Margin = new MarginPadding { Horizontal = 10f, Vertical = 2f }, + Text = BeatmapsetsStrings.FeaturedArtistBadgeLabel.ToUpper(), + Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), + Colour = OverlayColourProvider.Blue.Colour1, + } } }; } From a2c2646230bd6d92e057238d9c7298fa2f291220 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 9 Sep 2021 19:36:47 +0900 Subject: [PATCH 1786/2442] Use a counter instead of RNG --- osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index 40c8fae50a..f420ad976b 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -11,7 +11,6 @@ using osu.Game.Users; using System; using System.Collections.Generic; using System.Linq; -using osu.Framework.Utils; namespace osu.Game.Tests.Visual.Online { @@ -22,6 +21,8 @@ namespace osu.Game.Tests.Visual.Online protected override bool UseOnlineAPI => true; + private int nextBeatmapSetId = 1; + public TestSceneBeatmapSetOverlay() { Add(overlay = new TestBeatmapSetOverlay()); @@ -323,8 +324,8 @@ namespace osu.Game.Tests.Visual.Online private BeatmapSetInfo getBeatmapSet() { var beatmapSet = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet; - // Overlay doesn't reload if the same beatmap set is set. - beatmapSet.OnlineBeatmapSetID = RNG.Next(); + // Make sure the overlay is reloaded (see `BeatmapSetInfo.Equals`). + beatmapSet.OnlineBeatmapSetID = nextBeatmapSetId++; return beatmapSet; } From 99b6f0352cf05f733e374a1889a3c416a1b42332 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Sep 2021 22:04:00 +0900 Subject: [PATCH 1787/2442] Always start decoupled clock regardless of track source --- osu.Game/Screens/Menu/IntroTriangles.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/IntroTriangles.cs b/osu.Game/Screens/Menu/IntroTriangles.cs index 69333d4a12..36296487a8 100644 --- a/osu.Game/Screens/Menu/IntroTriangles.cs +++ b/osu.Game/Screens/Menu/IntroTriangles.cs @@ -90,8 +90,7 @@ namespace osu.Game.Screens.Menu protected override void StartTrack() { - if (UsingThemedIntro) - decoupledClock.Start(); + decoupledClock.Start(); } private class TrianglesIntroSequence : CompositeDrawable From 6c18df24ec432d848ba8c6b3b6e8d44691b48aad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 Sep 2021 22:04:16 +0900 Subject: [PATCH 1788/2442] Change how `UsingThemedIntro` is set to improve clarity --- osu.Game/Screens/Menu/IntroScreen.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index ac4a53f4a9..cfe14eab92 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -115,7 +115,9 @@ namespace osu.Game.Screens.Menu if (setInfo == null) return false; - return (initialBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0])) != null; + initialBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]); + + return UsingThemedIntro = initialBeatmap != null; } } @@ -184,7 +186,6 @@ namespace osu.Game.Screens.Menu { beatmap.Value = initialBeatmap; Track = initialBeatmap.Track; - UsingThemedIntro = !initialBeatmap.Track.IsDummyDevice; // ensure the track starts at maximum volume musicController.CurrentTrack.FinishTransforms(); From 2e00c7184206c190fedce24245dc0b852ecfb52e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Sep 2021 01:57:33 +0900 Subject: [PATCH 1789/2442] Add failing test coverage --- .../Online/TestSceneBeatmapListingOverlay.cs | 90 +++++++++++++++---- 1 file changed, 72 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs index 5bfb676f81..abadfac76a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs @@ -5,10 +5,10 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; @@ -17,10 +17,11 @@ using osu.Game.Overlays.BeatmapListing; using osu.Game.Rulesets; using osu.Game.Scoring; using osu.Game.Users; +using osuTK.Input; namespace osu.Game.Tests.Visual.Online { - public class TestSceneBeatmapListingOverlay : OsuTestScene + public class TestSceneBeatmapListingOverlay : OsuManualInputManagerTestScene { private readonly List setsForResponse = new List(); @@ -28,27 +29,33 @@ namespace osu.Game.Tests.Visual.Online private BeatmapListingSearchControl searchControl => overlay.ChildrenOfType().Single(); - [BackgroundDependencyLoader] - private void load() + [SetUpSteps] + public void SetUpSteps() { - Child = overlay = new BeatmapListingOverlay { State = { Value = Visibility.Visible } }; - - ((DummyAPIAccess)API).HandleRequest = req => + AddStep("setup overlay", () => { - if (!(req is SearchBeatmapSetsRequest searchBeatmapSetsRequest)) return false; - - searchBeatmapSetsRequest.TriggerSuccess(new SearchBeatmapSetsResponse - { - BeatmapSets = setsForResponse, - }); - - return true; - }; + Child = overlay = new BeatmapListingOverlay { State = { Value = Visibility.Visible } }; + setsForResponse.Clear(); + }); AddStep("initialize dummy", () => { + var api = (DummyAPIAccess)API; + + api.HandleRequest = req => + { + if (!(req is SearchBeatmapSetsRequest searchBeatmapSetsRequest)) return false; + + searchBeatmapSetsRequest.TriggerSuccess(new SearchBeatmapSetsResponse + { + BeatmapSets = setsForResponse, + }); + + return true; + }; + // non-supporter user - ((DummyAPIAccess)API).LocalUser.Value = new User + api.LocalUser.Value = new User { Username = "TestBot", Id = API.LocalUser.Value.Id + 1, @@ -56,6 +63,51 @@ namespace osu.Game.Tests.Visual.Online }); } + [Test] + public void TestHideViaBack() + { + AddAssert("is visible", () => overlay.State.Value == Visibility.Visible); + AddStep("hide", () => InputManager.Key(Key.Escape)); + AddUntilStep("is hidden", () => overlay.State.Value == Visibility.Hidden); + } + + [Test] + public void TestHideViaBackWithSearch() + { + AddAssert("is visible", () => overlay.State.Value == Visibility.Visible); + + AddStep("search something", () => overlay.ChildrenOfType().First().Text = "search"); + + AddStep("kill search", () => InputManager.Key(Key.Escape)); + + AddAssert("search textbox empty", () => string.IsNullOrEmpty(overlay.ChildrenOfType().First().Text)); + AddAssert("is visible", () => overlay.State.Value == Visibility.Visible); + + AddStep("hide", () => InputManager.Key(Key.Escape)); + AddUntilStep("is hidden", () => overlay.State.Value == Visibility.Hidden); + } + + [Test] + public void TestHideViaBackWithScrolledSearch() + { + AddAssert("is visible", () => overlay.State.Value == Visibility.Visible); + + AddStep("show many results", () => fetchFor(Enumerable.Repeat(CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet, 100).ToArray())); + + AddUntilStep("placeholder hidden", () => !overlay.ChildrenOfType().Any()); + + AddStep("scroll to bottom", () => overlay.ChildrenOfType().First().ScrollToEnd()); + + AddStep("kill search", () => InputManager.Key(Key.Escape)); + + AddUntilStep("search textbox empty", () => string.IsNullOrEmpty(overlay.ChildrenOfType().First().Text)); + AddUntilStep("is scrolled to top", () => overlay.ChildrenOfType().First().Current == 0); + AddAssert("is visible", () => overlay.State.Value == Visibility.Visible); + + AddStep("hide", () => InputManager.Key(Key.Escape)); + AddUntilStep("is hidden", () => overlay.State.Value == Visibility.Hidden); + } + [Test] public void TestNoBeatmapsPlaceholder() { @@ -193,13 +245,15 @@ namespace osu.Game.Tests.Visual.Online noPlaceholderShown(); } + private static int searchCount; + private void fetchFor(params BeatmapSetInfo[] beatmaps) { setsForResponse.Clear(); setsForResponse.AddRange(beatmaps.Select(b => new TestAPIBeatmapSet(b))); // trigger arbitrary change for fetching. - searchControl.Query.TriggerChange(); + searchControl.Query.Value = $"search {searchCount++}"; } private void setRankAchievedFilter(ScoreRank[] ranks) From c101d1f20570829380b9952d0dd99999118eb3bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Sep 2021 01:57:55 +0900 Subject: [PATCH 1790/2442] Fix beatmap listing overlay not hiding via keyboard control when scrolled Closes https://github.com/ppy/osu/issues/14684. --- .../Graphics/UserInterface/FocusedTextBox.cs | 2 +- .../BeatmapListingSearchControl.cs | 24 +++++++++++++------ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs index ed9f0710b0..6c8238a1b8 100644 --- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs +++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs @@ -70,7 +70,7 @@ namespace osu.Game.Graphics.UserInterface return base.OnKeyDown(e); } - public bool OnPressed(GlobalAction action) + public virtual bool OnPressed(GlobalAction action) { if (!HasFocus) return false; diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs index d1e2ac38df..07a0cfb57f 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs @@ -3,21 +3,22 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osuTK; -using osu.Framework.Bindables; using osu.Framework.Input.Events; -using osu.Game.Beatmaps.Drawables; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; +using osu.Game.Input.Bindings; using osu.Game.Resources.Localisation.Web; -using osuTK.Graphics; using osu.Game.Rulesets; using osu.Game.Scoring; +using osuTK; +using osuTK.Graphics; namespace osu.Game.Overlays.BeatmapListing { @@ -117,7 +118,7 @@ namespace osu.Game.Overlays.BeatmapListing textBox = new BeatmapSearchTextBox { RelativeSizeAxes = Axes.X, - TypingStarted = () => TypingStarted?.Invoke(), + TextChanged = () => TypingStarted?.Invoke(), }, new ReverseChildIDFillFlowContainer { @@ -167,7 +168,7 @@ namespace osu.Game.Overlays.BeatmapListing /// /// Any time the text box receives key events (even while masked). /// - public Action TypingStarted; + public Action TextChanged; protected override Color4 SelectionColour => Color4.Gray; @@ -181,7 +182,16 @@ namespace osu.Game.Overlays.BeatmapListing if (!base.OnKeyDown(e)) return false; - TypingStarted?.Invoke(); + TextChanged?.Invoke(); + return true; + } + + public override bool OnPressed(GlobalAction action) + { + if (!base.OnPressed(action)) + return false; + + TextChanged?.Invoke(); return true; } } From 3865988e48d1ef98ddcaceb947518af7eaa8f0b6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Sep 2021 02:15:13 +0900 Subject: [PATCH 1791/2442] Add test coverage for back button support in password popover --- .../TestSceneMultiplayerLoungeSubScreen.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs index 99f6ab1ae1..61565c88f4 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs @@ -51,6 +51,24 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("room join password correct", () => lastJoinedPassword == null); } + [Test] + public void TestPopoverHidesOnBackButton() + { + AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true)); + AddStep("select room", () => InputManager.Key(Key.Down)); + AddStep("attempt join room", () => InputManager.Key(Key.Enter)); + + AddUntilStep("password prompt appeared", () => InputManager.ChildrenOfType().Any()); + + AddAssert("textbox has focus", () => InputManager.FocusedDrawable is OsuPasswordTextBox); + + AddStep("hit escape", () => InputManager.Key(Key.Escape)); + AddAssert("textbox lost focus", () => InputManager.FocusedDrawable is SearchTextBox); + + AddStep("hit escape", () => InputManager.Key(Key.Escape)); + AddUntilStep("password prompt hidden", () => !InputManager.ChildrenOfType().Any()); + } + [Test] public void TestPopoverHidesOnLeavingScreen() { From 344bf2ab7c1038d4b8e64e7645f03bc93085713a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Sep 2021 02:15:30 +0900 Subject: [PATCH 1792/2442] Allow popovers to be closed via back button press Closes https://github.com/ppy/osu/issues/14669. --- .../Graphics/UserInterfaceV2/OsuPopover.cs | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs b/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs index c07a5de1e4..fac0661a15 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs @@ -4,14 +4,17 @@ using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Bindings; +using osu.Game.Input.Bindings; using osu.Game.Overlays; using osuTK; namespace osu.Game.Graphics.UserInterfaceV2 { - public class OsuPopover : Popover + public class OsuPopover : Popover, IKeyBindingHandler { private const float fade_duration = 250; private const double scale_duration = 500; @@ -51,5 +54,24 @@ namespace osu.Game.Graphics.UserInterfaceV2 this.ScaleTo(0.7f, scale_duration, Easing.OutQuint); this.FadeOut(fade_duration, Easing.OutQuint); } + + public bool OnPressed(GlobalAction action) + { + if (State.Value == Visibility.Hidden) + return false; + + if (action == GlobalAction.Back) + { + Hide(); + return true; + } + + return false; + } + + public void OnReleased(GlobalAction action) + { + throw new System.NotImplementedException(); + } } } From 32de13cb96f4dd48d8f66980e97af28fdd9eac86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 9 Sep 2021 21:33:02 +0200 Subject: [PATCH 1793/2442] Use consistent assertions for checking placeholder presence --- .../Visual/Online/TestSceneBeatmapListingOverlay.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs index abadfac76a..963809ebe1 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs @@ -94,7 +94,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("show many results", () => fetchFor(Enumerable.Repeat(CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet, 100).ToArray())); - AddUntilStep("placeholder hidden", () => !overlay.ChildrenOfType().Any()); + AddUntilStep("placeholder hidden", () => !overlay.ChildrenOfType().Any(d => d.IsPresent)); AddStep("scroll to bottom", () => overlay.ChildrenOfType().First().ScrollToEnd()); @@ -115,7 +115,7 @@ namespace osu.Game.Tests.Visual.Online AddUntilStep("placeholder shown", () => overlay.ChildrenOfType().SingleOrDefault()?.IsPresent == true); AddStep("fetch for 1 beatmap", () => fetchFor(CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet)); - AddUntilStep("placeholder hidden", () => !overlay.ChildrenOfType().Any()); + AddUntilStep("placeholder hidden", () => !overlay.ChildrenOfType().Any(d => d.IsPresent)); AddStep("fetch for 0 beatmaps", () => fetchFor()); AddUntilStep("placeholder shown", () => overlay.ChildrenOfType().SingleOrDefault()?.IsPresent == true); @@ -283,8 +283,8 @@ namespace osu.Game.Tests.Visual.Online private void noPlaceholderShown() { AddUntilStep("no placeholder shown", () => - !overlay.ChildrenOfType().Any() - && !overlay.ChildrenOfType().Any()); + !overlay.ChildrenOfType().Any(d => d.IsPresent) + && !overlay.ChildrenOfType().Any(d => d.IsPresent)); } private class TestAPIBeatmapSet : APIBeatmapSet From bf0150bab4670d05e3112e24db50d29ac1c814ce Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Thu, 9 Sep 2021 16:21:51 -0400 Subject: [PATCH 1794/2442] Clear UR bar display on keyboard input --- .../HUD/HitErrorMeters/BarHitErrorMeter.cs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 89f61785e8..5998c1b494 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -10,7 +10,9 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Bindings; using osu.Game.Graphics; +using osu.Game.Input.Bindings; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using osuTK; @@ -18,7 +20,7 @@ using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD.HitErrorMeters { - public class BarHitErrorMeter : HitErrorMeter + public class BarHitErrorMeter : HitErrorMeter, IKeyBindingHandler { private const int arrow_move_duration = 400; @@ -279,5 +281,22 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters this.FadeTo(0.8f, 150).Then().FadeOut(judgement_fade_duration).Expire(); } } + + public bool OnPressed(GlobalAction action) + { + switch (action) + { + case GlobalAction.SeekReplayBackward: + case GlobalAction.SeekReplayForward: + judgementsContainer.Clear(true); + return false; + } + + return false; + } + + public void OnReleased(GlobalAction action) + { + } } } From bde092f816c3e9570d54e7484baca3877ef0a7d1 Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Thu, 9 Sep 2021 20:08:16 -0400 Subject: [PATCH 1795/2442] Clear UR bar display on seek with mouse --- .../Play/HUD/HitErrorMeters/BarHitErrorMeter.cs | 12 +++++++++++- osu.Game/Screens/Play/SongProgress.cs | 2 ++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 5998c1b494..2854fabbae 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; +using osu.Framework.Testing; using osu.Game.Graphics; using osu.Game.Input.Bindings; using osu.Game.Rulesets.Judgements; @@ -143,6 +144,10 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters arrow.Alpha = 0; arrow.Delay(200).FadeInFromZero(600); + + var progressBar = Parent.ChildrenOfType().FirstOrDefault(); + if (progressBar != null) + progressBar.Bar.OnSeek += _ => handleSeek(); } private void createColourBars(OsuColour colours) @@ -282,13 +287,18 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters } } + private void handleSeek() + { + judgementsContainer.Clear(true); + } + public bool OnPressed(GlobalAction action) { switch (action) { case GlobalAction.SeekReplayBackward: case GlobalAction.SeekReplayForward: - judgementsContainer.Clear(true); + handleSeek(); return false; } diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index b27a9c5f5d..dff2dcc86b 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -35,6 +35,8 @@ namespace osu.Game.Screens.Play private readonly SongProgressGraph graph; private readonly SongProgressInfo info; + public SongProgressBar Bar => bar; + public Action RequestSeek; /// From ce6b022a9042d56f59c1c7fc8eae19377598bc87 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Sep 2021 10:59:30 +0900 Subject: [PATCH 1796/2442] Remove unused `IMod` specification from `APIMod` --- osu.Game/Online/API/APIMod.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs index f31f8bc92a..5514ce5e40 100644 --- a/osu.Game/Online/API/APIMod.cs +++ b/osu.Game/Online/API/APIMod.cs @@ -16,7 +16,7 @@ using osu.Game.Utils; namespace osu.Game.Online.API { [MessagePackObject] - public class APIMod : IMod, IEquatable + public class APIMod { [JsonProperty("acronym")] [Key(0)] @@ -67,8 +67,6 @@ namespace osu.Game.Online.API return resultMod; } - public bool Equals(IMod other) => other is APIMod them && Equals(them); - public bool Equals(APIMod other) { if (ReferenceEquals(null, other)) return false; From cf633973a91dc977c0dec6d8cf2f020b1dbc3807 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Sep 2021 11:09:13 +0900 Subject: [PATCH 1797/2442] Refactor exposed mod retrieval methods for better safety --- osu.Game.Benchmarks/BenchmarkRuleset.cs | 8 +-- .../Gameplay/TestSceneAllRulesetPlayers.cs | 2 +- .../SongSelect/TestSceneAdvancedStats.cs | 8 +-- .../TestSceneBeatmapMetadataDisplay.cs | 2 +- .../UserInterface/TestSceneModFlowDisplay.cs | 2 +- .../TestSceneModSelectOverlay.cs | 4 +- .../TestSceneTournamentModDisplay.cs | 2 +- .../Components/TournamentModIcon.cs | 2 +- osu.Game/Online/API/APIMod.cs | 2 +- .../Requests/Responses/APILegacyScoreInfo.cs | 4 +- .../BeatmapSet/LeaderboardModSelector.cs | 2 +- .../IncompatibilityDisplayingModButton.cs | 4 +- osu.Game/Rulesets/Mods/IMod.cs | 19 +++++- osu.Game/Rulesets/Mods/Mod.cs | 7 --- osu.Game/Rulesets/Ruleset.cs | 63 ++++++++++--------- osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs | 2 +- .../Tests/Beatmaps/LegacyModConversionTest.cs | 2 +- osu.Game/Tests/TestScoreInfo.cs | 2 +- osu.Game/Tests/Visual/PlayerTestScene.cs | 2 +- 19 files changed, 75 insertions(+), 64 deletions(-) diff --git a/osu.Game.Benchmarks/BenchmarkRuleset.cs b/osu.Game.Benchmarks/BenchmarkRuleset.cs index aa59cb7fa3..2835ec9499 100644 --- a/osu.Game.Benchmarks/BenchmarkRuleset.cs +++ b/osu.Game.Benchmarks/BenchmarkRuleset.cs @@ -38,25 +38,25 @@ namespace osu.Game.Benchmarks [Benchmark] public void BenchmarkGetAllMods() { - ruleset.GetAllMods().Consume(new Consumer()); + ruleset.CreateAllMods().Consume(new Consumer()); } [Benchmark] public void BenchmarkGetAllModsForReference() { - ruleset.GetAllModsForReference().Consume(new Consumer()); + ruleset.AllMods.Consume(new Consumer()); } [Benchmark] public void BenchmarkGetForAcronym() { - ruleset.GetModForAcronym("DT"); + ruleset.CreateModFromAcronym("DT"); } [Benchmark] public void BenchmarkGetForType() { - ruleset.GetMod(); + ruleset.CreateMod(); } } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneAllRulesetPlayers.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneAllRulesetPlayers.cs index b7dcad3825..8487eecdfa 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneAllRulesetPlayers.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneAllRulesetPlayers.cs @@ -71,7 +71,7 @@ namespace osu.Game.Tests.Visual.Gameplay var working = CreateWorkingBeatmap(rulesetInfo); Beatmap.Value = working; - SelectedMods.Value = new[] { ruleset.GetAllMods().First(m => m is ModNoFail) }; + SelectedMods.Value = new[] { ruleset.CreateAllMods().First(m => m is ModNoFail) }; Player = CreatePlayer(ruleset); diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs index 40b2f66d74..5e22bde1d5 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs @@ -89,7 +89,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("select EZ mod", () => { var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance(); - SelectedMods.Value = new[] { ruleset.GetAllMods().OfType().Single() }; + SelectedMods.Value = new[] { ruleset.CreateAllMods().OfType().Single() }; }); AddAssert("circle size bar is blue", () => barIsBlue(advancedStats.FirstValue)); @@ -106,7 +106,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("select HR mod", () => { var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance(); - SelectedMods.Value = new[] { ruleset.GetAllMods().OfType().Single() }; + SelectedMods.Value = new[] { ruleset.CreateAllMods().OfType().Single() }; }); AddAssert("circle size bar is red", () => barIsRed(advancedStats.FirstValue)); @@ -123,7 +123,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("select unchanged Difficulty Adjust mod", () => { var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance(); - var difficultyAdjustMod = ruleset.GetAllMods().OfType().Single(); + var difficultyAdjustMod = ruleset.CreateAllMods().OfType().Single(); difficultyAdjustMod.ReadFromDifficulty(advancedStats.Beatmap.BaseDifficulty); SelectedMods.Value = new[] { difficultyAdjustMod }; }); @@ -142,7 +142,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("select changed Difficulty Adjust mod", () => { var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance(); - var difficultyAdjustMod = ruleset.GetAllMods().OfType().Single(); + var difficultyAdjustMod = ruleset.CreateAllMods().OfType().Single(); var originalDifficulty = advancedStats.Beatmap.BaseDifficulty; difficultyAdjustMod.ReadFromDifficulty(originalDifficulty); diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs index 66ac700c51..f91d3f595b 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -92,7 +92,7 @@ namespace osu.Game.Tests.Visual.SongSelect { AddStep("setup display", () => { - var randomMods = Ruleset.Value.CreateInstance().GetAllMods().OrderBy(_ => RNG.Next()).Take(5).ToList(); + var randomMods = Ruleset.Value.CreateInstance().CreateAllMods().OrderBy(_ => RNG.Next()).Take(5).ToList(); OsuLogo logo = new OsuLogo { Scale = new Vector2(0.15f) }; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModFlowDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModFlowDisplay.cs index 8f057c663b..10eab148de 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModFlowDisplay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModFlowDisplay.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.UserInterface Width = 200, Current = { - Value = new OsuRuleset().GetAllMods().ToArray(), + Value = new OsuRuleset().CreateAllMods().ToArray(), } }; }); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 9e253e089d..28e5644998 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -158,8 +158,8 @@ namespace osu.Game.Tests.Visual.UserInterface var mania = new ManiaRuleset(); testModsWithSameBaseType( - mania.GetAllMods().Single(m => m.GetType() == typeof(ManiaModFadeIn)), - mania.GetAllMods().Single(m => m.GetType() == typeof(ManiaModHidden))); + mania.CreateAllMods().Single(m => m.GetType() == typeof(ManiaModFadeIn)), + mania.CreateAllMods().Single(m => m.GetType() == typeof(ManiaModHidden))); } [Test] diff --git a/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs b/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs index b4d9fa4222..cc1568f429 100644 --- a/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs @@ -45,7 +45,7 @@ namespace osu.Game.Tournament.Tests.Components private void success(APIBeatmap apiBeatmap) { beatmap = apiBeatmap.ToBeatmap(rulesets); - var mods = rulesets.GetRuleset(Ladder.Ruleset.Value.ID ?? 0).CreateInstance().GetAllMods(); + var mods = rulesets.GetRuleset(Ladder.Ruleset.Value.ID ?? 0).CreateInstance().CreateAllMods(); foreach (var mod in mods) { diff --git a/osu.Game.Tournament/Components/TournamentModIcon.cs b/osu.Game.Tournament/Components/TournamentModIcon.cs index b8486b5b2d..7c4e9c69a2 100644 --- a/osu.Game.Tournament/Components/TournamentModIcon.cs +++ b/osu.Game.Tournament/Components/TournamentModIcon.cs @@ -48,7 +48,7 @@ namespace osu.Game.Tournament.Components } var ruleset = rulesets.GetRuleset(ladderInfo.Ruleset.Value?.ID ?? 0); - var modIcon = ruleset?.CreateInstance().GetModForAcronym(modAcronym); + var modIcon = ruleset?.CreateInstance().CreateModFromAcronym(modAcronym); if (modIcon == null) return; diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs index 5514ce5e40..5e552dac09 100644 --- a/osu.Game/Online/API/APIMod.cs +++ b/osu.Game/Online/API/APIMod.cs @@ -48,7 +48,7 @@ namespace osu.Game.Online.API public Mod ToMod(Ruleset ruleset) { - Mod resultMod = ruleset.GetModForAcronym(Acronym); + Mod resultMod = ruleset.CreateModFromAcronym(Acronym); if (resultMod == null) throw new InvalidOperationException($"There is no mod in the ruleset ({ruleset.ShortName}) matching the acronym {Acronym}."); diff --git a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs index d1c6f0a55a..567df524b1 100644 --- a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs @@ -23,10 +23,10 @@ namespace osu.Game.Online.API.Requests.Responses var rulesetInstance = ruleset.CreateInstance(); - var mods = Mods != null ? Mods.Select(acronym => rulesetInstance.GetModForAcronym(acronym)).Where(m => m != null).ToArray() : Array.Empty(); + var mods = Mods != null ? Mods.Select(acronym => rulesetInstance.CreateModFromAcronym(acronym)).Where(m => m != null).ToArray() : Array.Empty(); // all API scores provided by this class are considered to be legacy. - mods = mods.Append(rulesetInstance.GetMod()).ToArray(); + mods = mods.Append(rulesetInstance.CreateMod()).ToArray(); var scoreInfo = new ScoreInfo { diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index 517da11c8c..2683d7bc6d 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -54,7 +54,7 @@ namespace osu.Game.Overlays.BeatmapSet return; modsContainer.Add(new ModButton(new ModNoMod())); - modsContainer.AddRange(ruleset.NewValue.CreateInstance().GetAllModsForReference().Where(m => m.UserPlayable).Select(m => new ModButton(m))); + modsContainer.AddRange(ruleset.NewValue.CreateInstance().AllMods.Where(m => m.UserPlayable).Select(m => new ModButton(m.CreateInstance()))); modsContainer.ForEach(button => { diff --git a/osu.Game/Overlays/Mods/IncompatibilityDisplayingModButton.cs b/osu.Game/Overlays/Mods/IncompatibilityDisplayingModButton.cs index 8d0746f24d..0f51439252 100644 --- a/osu.Game/Overlays/Mods/IncompatibilityDisplayingModButton.cs +++ b/osu.Game/Overlays/Mods/IncompatibilityDisplayingModButton.cs @@ -107,9 +107,9 @@ namespace osu.Game.Overlays.Mods var incompatibleTypes = mod.IncompatibleMods; - var allMods = ruleset.Value.CreateInstance().GetAllModsForReference(); + var allMods = ruleset.Value.CreateInstance().AllMods; - incompatibleMods.Value = allMods.Where(m => m.GetType() != mod.GetType() && incompatibleTypes.Any(t => t.IsInstanceOfType(m))).ToList(); + incompatibleMods.Value = allMods.Where(m => m.GetType() != mod.GetType() && incompatibleTypes.Any(t => t.IsInstanceOfType(m))).Select(m => m.CreateInstance()).ToList(); incompatibleText.Text = incompatibleMods.Value.Any() ? "Incompatible with:" : "Compatible with all mods"; } } diff --git a/osu.Game/Rulesets/Mods/IMod.cs b/osu.Game/Rulesets/Mods/IMod.cs index a5e19f293c..ac17e49e60 100644 --- a/osu.Game/Rulesets/Mods/IMod.cs +++ b/osu.Game/Rulesets/Mods/IMod.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; -using Newtonsoft.Json; +using osu.Framework.Graphics.Sprites; namespace osu.Game.Rulesets.Mods { @@ -11,7 +11,22 @@ namespace osu.Game.Rulesets.Mods /// /// The shortened name of this mod. /// - [JsonProperty("acronym")] string Acronym { get; } + + /// + /// The icon of this mod. + /// + IconUsage? Icon { get; } + + /// + /// Whether this mod is playable by an end user. + /// Should be false for cases where the user is not interacting with the game (so it can be excluded from multiplayer selection, for example). + /// + bool UserPlayable { get; } + + /// + /// Create a fresh instance based on this mod. + /// + Mod CreateInstance() => ((Mod)this).DeepClone(); } } diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 1199d8a956..fedee857c3 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -33,9 +33,6 @@ namespace osu.Game.Rulesets.Mods /// public abstract string Acronym { get; } - /// - /// The icon of this mod. - /// [JsonIgnore] public virtual IconUsage? Icon => null; @@ -106,10 +103,6 @@ namespace osu.Game.Rulesets.Mods [JsonIgnore] public virtual bool HasImplementation => this is IApplicableMod; - /// - /// Whether this mod is playable by an end user. - /// Should be false for cases where the user is not interacting with the game (so it can be excluded from mutliplayer selection, for example). - /// [JsonIgnore] public virtual bool UserPlayable => true; diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 34e4606133..82707ad382 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -39,45 +39,48 @@ namespace osu.Game.Rulesets { public RulesetInfo RulesetInfo { get; internal set; } + private static readonly ConcurrentDictionary mod_reference_cache = new ConcurrentDictionary(); + + /// + /// A queryable source containing all available mods. + /// Call for consumption purposes. + /// + public IEnumerable AllMods + { + get + { + if (!(RulesetInfo.ID is int id)) + return CreateAllMods(); + + if (!mod_reference_cache.TryGetValue(id, out var mods)) + mod_reference_cache[id] = mods = CreateAllMods().Cast().ToArray(); + + return mods; + } + } + /// /// Returns fresh instances of all mods. /// /// /// This comes with considerable allocation overhead. If only accessing for reference purposes (ie. not changing bindables / settings) - /// use instead. + /// use instead. /// - public IEnumerable GetAllMods() => Enum.GetValues(typeof(ModType)).Cast() - // Confine all mods of each mod type into a single IEnumerable - .SelectMany(GetModsFor) - // Filter out all null mods - .Where(mod => mod != null) - // Resolve MultiMods as their .Mods property - .SelectMany(mod => (mod as MultiMod)?.Mods ?? new[] { mod }); - - private static readonly ConcurrentDictionary mod_reference_cache = new ConcurrentDictionary(); - - /// - /// Returns all mods for a query-only purpose. - /// Bindables should not be considered usable when retrieving via this method (use instead). - /// - public IEnumerable GetAllModsForReference() - { - if (!(RulesetInfo.ID is int id)) - return GetAllMods(); - - if (!mod_reference_cache.TryGetValue(id, out var mods)) - mod_reference_cache[id] = mods = GetAllMods().ToArray(); - - return mods; - } + public IEnumerable CreateAllMods() => Enum.GetValues(typeof(ModType)).Cast() + // Confine all mods of each mod type into a single IEnumerable + .SelectMany(GetModsFor) + // Filter out all null mods + .Where(mod => mod != null) + // Resolve MultiMods as their .Mods property + .SelectMany(mod => (mod as MultiMod)?.Mods ?? new[] { mod }); /// /// Returns a fresh instance of the mod matching the specified acronym. /// /// The acronym to query for . - public Mod GetModForAcronym(string acronym) + public Mod CreateModFromAcronym(string acronym) { - var type = GetAllModsForReference().FirstOrDefault(m => m.Acronym == acronym)?.GetType(); + var type = AllMods.FirstOrDefault(m => m.Acronym == acronym)?.GetType(); if (type != null) return (Mod)Activator.CreateInstance(type); @@ -88,10 +91,10 @@ namespace osu.Game.Rulesets /// /// Returns a fresh instance of the mod matching the specified type. /// - public T GetMod() + public T CreateMod() where T : Mod { - var type = GetAllModsForReference().FirstOrDefault(m => m is T)?.GetType(); + var type = AllMods.FirstOrDefault(m => m is T)?.GetType(); if (type != null) return (T)Activator.CreateInstance(type); @@ -179,7 +182,7 @@ namespace osu.Game.Rulesets } [CanBeNull] - public ModAutoplay GetAutoplayMod() => GetMod(); + public ModAutoplay GetAutoplayMod() => CreateMod(); public virtual ISkin CreateLegacySkinProvider([NotNull] ISkin skin, IBeatmap beatmap) => null; diff --git a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs index 010f33e3ed..2e1a29372d 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs @@ -67,7 +67,7 @@ namespace osu.Game.Scoring.Legacy // lazer replays get a really high version number. if (version < LegacyScoreEncoder.FIRST_LAZER_VERSION) - scoreInfo.Mods = scoreInfo.Mods.Append(currentRuleset.GetMod()).ToArray(); + scoreInfo.Mods = scoreInfo.Mods.Append(currentRuleset.CreateMod()).ToArray(); currentBeatmap = workingBeatmap.GetPlayableBeatmap(currentRuleset.RulesetInfo, scoreInfo.Mods); scoreInfo.Beatmap = currentBeatmap.BeatmapInfo; diff --git a/osu.Game/Tests/Beatmaps/LegacyModConversionTest.cs b/osu.Game/Tests/Beatmaps/LegacyModConversionTest.cs index 54a83f4305..b7803f3420 100644 --- a/osu.Game/Tests/Beatmaps/LegacyModConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/LegacyModConversionTest.cs @@ -34,7 +34,7 @@ namespace osu.Game.Tests.Beatmaps protected void TestToLegacy(LegacyMods expectedLegacyMods, Type[] providedModTypes) { var ruleset = CreateRuleset(); - var modInstances = ruleset.GetAllMods() + var modInstances = ruleset.CreateAllMods() .Where(mod => providedModTypes.Contains(mod.GetType())) .ToArray(); var actualLegacyMods = ruleset.ConvertToLegacyMods(modInstances); diff --git a/osu.Game/Tests/TestScoreInfo.cs b/osu.Game/Tests/TestScoreInfo.cs index 8ce71ace69..5ce6aae647 100644 --- a/osu.Game/Tests/TestScoreInfo.cs +++ b/osu.Game/Tests/TestScoreInfo.cs @@ -28,7 +28,7 @@ namespace osu.Game.Tests RulesetID = ruleset.ID ?? 0; Mods = excessMods - ? ruleset.CreateInstance().GetAllMods().ToArray() + ? ruleset.CreateInstance().CreateAllMods().ToArray() : new Mod[] { new TestModHardRock(), new TestModDoubleTime() }; TotalScore = 2845370; diff --git a/osu.Game/Tests/Visual/PlayerTestScene.cs b/osu.Game/Tests/Visual/PlayerTestScene.cs index a31a6433ea..b34f7e2d5f 100644 --- a/osu.Game/Tests/Visual/PlayerTestScene.cs +++ b/osu.Game/Tests/Visual/PlayerTestScene.cs @@ -67,7 +67,7 @@ namespace osu.Game.Tests.Visual if (!AllowFail) { - var noFailMod = ruleset.GetMod(); + var noFailMod = ruleset.CreateMod(); if (noFailMod != null) SelectedMods.Value = new[] { noFailMod }; } From 76e877f1607d0aebcbfe3b5f91ded6ef10d941db Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Sep 2021 11:22:08 +0900 Subject: [PATCH 1798/2442] Disable APIMod/Mod cross equality support --- .../Mods/ModSettingsEqualityComparison.cs | 33 ++++++++++++++----- osu.Game/Online/API/APIMod.cs | 5 ++- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Mods/ModSettingsEqualityComparison.cs b/osu.Game.Tests/Mods/ModSettingsEqualityComparison.cs index 7a5789f01a..ce6b3a68a5 100644 --- a/osu.Game.Tests/Mods/ModSettingsEqualityComparison.cs +++ b/osu.Game.Tests/Mods/ModSettingsEqualityComparison.cs @@ -3,6 +3,7 @@ using NUnit.Framework; using osu.Game.Online.API; +using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; namespace osu.Game.Tests.Mods @@ -11,26 +12,42 @@ namespace osu.Game.Tests.Mods public class ModSettingsEqualityComparison { [Test] - public void Test() + public void TestAPIMod() { + var apiMod1 = new APIMod(new OsuModDoubleTime { SpeedChange = { Value = 1.25 } }); + var apiMod2 = new APIMod(new OsuModDoubleTime { SpeedChange = { Value = 1.26 } }); + var apiMod3 = new APIMod(new OsuModDoubleTime { SpeedChange = { Value = 1.26 } }); + + Assert.That(apiMod1, Is.Not.EqualTo(apiMod2)); + Assert.That(apiMod2, Is.EqualTo(apiMod2)); + Assert.That(apiMod2, Is.EqualTo(apiMod3)); + Assert.That(apiMod3, Is.EqualTo(apiMod2)); + } + + [Test] + public void TestMod() + { + var ruleset = new OsuRuleset(); + var mod1 = new OsuModDoubleTime { SpeedChange = { Value = 1.25 } }; var mod2 = new OsuModDoubleTime { SpeedChange = { Value = 1.26 } }; var mod3 = new OsuModDoubleTime { SpeedChange = { Value = 1.26 } }; - var apiMod1 = new APIMod(mod1); - var apiMod2 = new APIMod(mod2); - var apiMod3 = new APIMod(mod3); + + var doubleConvertedMod1 = new APIMod(mod1).ToMod(ruleset); + var doulbeConvertedMod2 = new APIMod(mod2).ToMod(ruleset); + var doulbeConvertedMod3 = new APIMod(mod3).ToMod(ruleset); Assert.That(mod1, Is.Not.EqualTo(mod2)); - Assert.That(apiMod1, Is.Not.EqualTo(apiMod2)); + Assert.That(doubleConvertedMod1, Is.Not.EqualTo(doulbeConvertedMod2)); Assert.That(mod2, Is.EqualTo(mod2)); - Assert.That(apiMod2, Is.EqualTo(apiMod2)); + Assert.That(doulbeConvertedMod2, Is.EqualTo(doulbeConvertedMod2)); Assert.That(mod2, Is.EqualTo(mod3)); - Assert.That(apiMod2, Is.EqualTo(apiMod3)); + Assert.That(doulbeConvertedMod2, Is.EqualTo(doulbeConvertedMod3)); Assert.That(mod3, Is.EqualTo(mod2)); - Assert.That(apiMod3, Is.EqualTo(apiMod2)); + Assert.That(doulbeConvertedMod3, Is.EqualTo(doulbeConvertedMod2)); } } } diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs index 5e552dac09..62f9976c0f 100644 --- a/osu.Game/Online/API/APIMod.cs +++ b/osu.Game/Online/API/APIMod.cs @@ -16,7 +16,7 @@ using osu.Game.Utils; namespace osu.Game.Online.API { [MessagePackObject] - public class APIMod + public class APIMod : IEquatable { [JsonProperty("acronym")] [Key(0)] @@ -72,8 +72,7 @@ namespace osu.Game.Online.API if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; - return Acronym == other.Acronym && - Settings.SequenceEqual(other.Settings, ModSettingsEqualityComparer.Default); + return Acronym == other.Acronym && Settings.SequenceEqual(other.Settings, ModSettingsEqualityComparer.Default); } public override string ToString() From f3299737985e1c3b7d3536762a829c7423a6ca05 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 10 Sep 2021 09:37:27 +0700 Subject: [PATCH 1799/2442] fix supporter promo background colour --- osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index c2b855a0f8..91ff314c15 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -53,6 +53,7 @@ namespace osu.Game.Overlays.Changelog Colour = Color4.Black.Opacity(0.25f), Offset = new Vector2(0, 1), Radius = 3, + Hollow = true, }, Children = new Drawable[] { From 9cf79a80c2235470911297a220057be860eadaa1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Sep 2021 12:00:41 +0900 Subject: [PATCH 1800/2442] Replace many more calls to `CreateAllMods` with more specific calls --- .../Visual/Gameplay/TestSceneAllRulesetPlayers.cs | 3 +-- .../Visual/SongSelect/TestSceneAdvancedStats.cs | 8 ++++---- .../Visual/UserInterface/TestSceneModSelectOverlay.cs | 4 ++-- .../Components/TestSceneTournamentModDisplay.cs | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneAllRulesetPlayers.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneAllRulesetPlayers.cs index 8487eecdfa..00b5c38e20 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneAllRulesetPlayers.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneAllRulesetPlayers.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.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Game.Configuration; @@ -71,7 +70,7 @@ namespace osu.Game.Tests.Visual.Gameplay var working = CreateWorkingBeatmap(rulesetInfo); Beatmap.Value = working; - SelectedMods.Value = new[] { ruleset.CreateAllMods().First(m => m is ModNoFail) }; + SelectedMods.Value = new[] { ruleset.CreateMod() }; Player = CreatePlayer(ruleset); diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs index 5e22bde1d5..dcc2111ad3 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs @@ -89,7 +89,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("select EZ mod", () => { var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance(); - SelectedMods.Value = new[] { ruleset.CreateAllMods().OfType().Single() }; + SelectedMods.Value = new[] { ruleset.CreateMod() }; }); AddAssert("circle size bar is blue", () => barIsBlue(advancedStats.FirstValue)); @@ -106,7 +106,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("select HR mod", () => { var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance(); - SelectedMods.Value = new[] { ruleset.CreateAllMods().OfType().Single() }; + SelectedMods.Value = new[] { ruleset.CreateMod() }; }); AddAssert("circle size bar is red", () => barIsRed(advancedStats.FirstValue)); @@ -123,7 +123,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("select unchanged Difficulty Adjust mod", () => { var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance(); - var difficultyAdjustMod = ruleset.CreateAllMods().OfType().Single(); + var difficultyAdjustMod = ruleset.CreateMod(); difficultyAdjustMod.ReadFromDifficulty(advancedStats.Beatmap.BaseDifficulty); SelectedMods.Value = new[] { difficultyAdjustMod }; }); @@ -142,7 +142,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("select changed Difficulty Adjust mod", () => { var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance(); - var difficultyAdjustMod = ruleset.CreateAllMods().OfType().Single(); + var difficultyAdjustMod = ruleset.CreateMod(); var originalDifficulty = advancedStats.Beatmap.BaseDifficulty; difficultyAdjustMod.ReadFromDifficulty(originalDifficulty); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 28e5644998..4f7aec3b67 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -158,8 +158,8 @@ namespace osu.Game.Tests.Visual.UserInterface var mania = new ManiaRuleset(); testModsWithSameBaseType( - mania.CreateAllMods().Single(m => m.GetType() == typeof(ManiaModFadeIn)), - mania.CreateAllMods().Single(m => m.GetType() == typeof(ManiaModHidden))); + mania.CreateMod(), + mania.CreateMod()); } [Test] diff --git a/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs b/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs index cc1568f429..47e7ed9b61 100644 --- a/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs @@ -45,7 +45,7 @@ namespace osu.Game.Tournament.Tests.Components private void success(APIBeatmap apiBeatmap) { beatmap = apiBeatmap.ToBeatmap(rulesets); - var mods = rulesets.GetRuleset(Ladder.Ruleset.Value.ID ?? 0).CreateInstance().CreateAllMods(); + var mods = rulesets.GetRuleset(Ladder.Ruleset.Value.ID ?? 0).CreateInstance().AllMods; foreach (var mod in mods) { From 719392de394716856211931284b2fafe7d43c52e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Sep 2021 12:05:10 +0900 Subject: [PATCH 1801/2442] Change `CreateInstance` to use `Activator.CreateInstance` instead of clone --- osu.Game/Rulesets/Mods/IMod.cs | 2 +- osu.Game/Rulesets/Ruleset.cs | 14 ++------------ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/osu.Game/Rulesets/Mods/IMod.cs b/osu.Game/Rulesets/Mods/IMod.cs index ac17e49e60..ca5053aaca 100644 --- a/osu.Game/Rulesets/Mods/IMod.cs +++ b/osu.Game/Rulesets/Mods/IMod.cs @@ -27,6 +27,6 @@ namespace osu.Game.Rulesets.Mods /// /// Create a fresh instance based on this mod. /// - Mod CreateInstance() => ((Mod)this).DeepClone(); + Mod CreateInstance() => (Mod)Activator.CreateInstance(GetType()); } } diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 82707ad382..b0c3836774 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -80,12 +80,7 @@ namespace osu.Game.Rulesets /// The acronym to query for . public Mod CreateModFromAcronym(string acronym) { - var type = AllMods.FirstOrDefault(m => m.Acronym == acronym)?.GetType(); - - if (type != null) - return (Mod)Activator.CreateInstance(type); - - return null; + return AllMods.FirstOrDefault(m => m.Acronym == acronym)?.CreateInstance(); } /// @@ -94,12 +89,7 @@ namespace osu.Game.Rulesets public T CreateMod() where T : Mod { - var type = AllMods.FirstOrDefault(m => m is T)?.GetType(); - - if (type != null) - return (T)Activator.CreateInstance(type); - - return null; + return AllMods.FirstOrDefault(m => m is T)?.CreateInstance() as T; } public abstract IEnumerable GetModsFor(ModType type); From 8acf82944f2f83b84bd9bf0af18d3603bea8f1bb Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 10 Sep 2021 10:17:03 +0700 Subject: [PATCH 1802/2442] use hex colour directly instead of transparency --- osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index 91ff314c15..d3462c1787 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -53,14 +53,13 @@ namespace osu.Game.Overlays.Changelog Colour = Color4.Black.Opacity(0.25f), Offset = new Vector2(0, 1), Radius = 3, - Hollow = true, }, Children = new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.3f), + Colour = Color4Extensions.FromHex("#222027"), }, new Container { From 2838a3a9614ecbf218e719a24444a6015e7e9c69 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 10 Sep 2021 12:25:41 +0900 Subject: [PATCH 1803/2442] Rewrite conditional to be more 'balanced' --- osu.Game/Users/Drawables/UpdateableAvatar.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/osu.Game/Users/Drawables/UpdateableAvatar.cs b/osu.Game/Users/Drawables/UpdateableAvatar.cs index a8726d0cab..24d82c2784 100644 --- a/osu.Game/Users/Drawables/UpdateableAvatar.cs +++ b/osu.Game/Users/Drawables/UpdateableAvatar.cs @@ -69,20 +69,22 @@ namespace osu.Game.Users.Drawables if (user == null && !showGuestOnNull) return null; - if (!openOnClick) + if (openOnClick) + { + return new ClickableAvatar(user) + { + OpenOnClick = true, + ShowUsernameTooltip = showUsernameTooltip, + RelativeSizeAxes = Axes.Both, + }; + } + else { return new DrawableAvatar(user) { RelativeSizeAxes = Axes.Both, }; } - - return new ClickableAvatar(user) - { - OpenOnClick = openOnClick, - ShowUsernameTooltip = showUsernameTooltip, - RelativeSizeAxes = Axes.Both, - }; } } } From c3531e1361bcab20620eaec176792d5118da6f0d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Sep 2021 12:42:53 +0900 Subject: [PATCH 1804/2442] Move more specification from `Mod` to `IMod` --- osu.Game/Rulesets/Mods/IMod.cs | 15 +++++++++++++++ osu.Game/Rulesets/Mods/Mod.cs | 12 ------------ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/osu.Game/Rulesets/Mods/IMod.cs b/osu.Game/Rulesets/Mods/IMod.cs index ca5053aaca..d5d1de91de 100644 --- a/osu.Game/Rulesets/Mods/IMod.cs +++ b/osu.Game/Rulesets/Mods/IMod.cs @@ -13,6 +13,21 @@ namespace osu.Game.Rulesets.Mods /// string Acronym { get; } + /// + /// The name of this mod. + /// + string Name { get; } + + /// + /// The user readable description of this mod. + /// + string Description { get; } + + /// + /// The type of this mod. + /// + ModType Type { get; } + /// /// The icon of this mod. /// diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index fedee857c3..7136795461 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -22,29 +22,17 @@ namespace osu.Game.Rulesets.Mods [ExcludeFromDynamicCompile] public abstract class Mod : IMod, IEquatable, IDeepCloneable { - /// - /// The name of this mod. - /// [JsonIgnore] public abstract string Name { get; } - /// - /// The shortened name of this mod. - /// public abstract string Acronym { get; } [JsonIgnore] public virtual IconUsage? Icon => null; - /// - /// The type of this mod. - /// [JsonIgnore] public virtual ModType Type => ModType.Fun; - /// - /// The user readable description of this mod. - /// [JsonIgnore] public abstract string Description { get; } From 464797fecf8afdd604d31012f6ff378da49326e0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Sep 2021 12:43:12 +0900 Subject: [PATCH 1805/2442] Allow `ModIcon` to be constructed using an `IMod` --- .../Visual/UserInterface/TestSceneModIcon.cs | 13 +++++++++++++ osu.Game/Rulesets/UI/ModIcon.cs | 10 +++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModIcon.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModIcon.cs index e7fa7d9235..513eb2fafc 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModIcon.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModIcon.cs @@ -1,7 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using NUnit.Framework; +using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.UI; @@ -17,5 +19,16 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("create mod icon", () => Child = icon = new ModIcon(new OsuModDoubleTime())); AddStep("change mod", () => icon.Mod = new OsuModEasy()); } + + [Test] + public void TestInterfaceModType() + { + ModIcon icon = null; + + var ruleset = new OsuRuleset(); + + AddStep("create mod icon", () => Child = icon = new ModIcon(ruleset.AllMods.First(m => m.Acronym == "DT"))); + AddStep("change mod", () => icon.Mod = ruleset.AllMods.First(m => m.Acronym == "EZ")); + } } } diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index 725cfa9c26..79bada0490 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -30,12 +30,12 @@ namespace osu.Game.Rulesets.UI private const float size = 80; - public virtual LocalisableString TooltipText => showTooltip ? mod.IconTooltip : null; + public virtual LocalisableString TooltipText => showTooltip ? ((mod as Mod)?.IconTooltip ?? mod.Name) : null; - private Mod mod; + private IMod mod; private readonly bool showTooltip; - public Mod Mod + public IMod Mod { get => mod; set @@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.UI /// /// The mod to be displayed /// Whether a tooltip describing the mod should display on hover. - public ModIcon(Mod mod, bool showTooltip = true) + public ModIcon(IMod mod, bool showTooltip = true) { this.mod = mod ?? throw new ArgumentNullException(nameof(mod)); this.showTooltip = showTooltip; @@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.UI updateMod(mod); } - private void updateMod(Mod value) + private void updateMod(IMod value) { modAcronym.Text = value.Acronym; modIcon.Icon = value.Icon ?? FontAwesome.Solid.Question; From 28e93291364cbe78d94aceaee284eb5a488f34db Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Sep 2021 12:43:21 +0900 Subject: [PATCH 1806/2442] Update `LeaderboardModSelector` to avoid creating mod instances --- osu.Game/Online/API/Requests/GetScoresRequest.cs | 6 +++--- osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index bf3441d2a0..b4e0e44b2c 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -18,9 +18,9 @@ namespace osu.Game.Online.API.Requests private readonly BeatmapInfo beatmap; private readonly BeatmapLeaderboardScope scope; private readonly RulesetInfo ruleset; - private readonly IEnumerable mods; + private readonly IEnumerable mods; - public GetScoresRequest(BeatmapInfo beatmap, RulesetInfo ruleset, BeatmapLeaderboardScope scope = BeatmapLeaderboardScope.Global, IEnumerable mods = null) + public GetScoresRequest(BeatmapInfo beatmap, RulesetInfo ruleset, BeatmapLeaderboardScope scope = BeatmapLeaderboardScope.Global, IEnumerable mods = null) { if (!beatmap.OnlineBeatmapID.HasValue) throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}."); @@ -31,7 +31,7 @@ namespace osu.Game.Online.API.Requests this.beatmap = beatmap; this.scope = scope; this.ruleset = ruleset ?? throw new ArgumentNullException(nameof(ruleset)); - this.mods = mods ?? Array.Empty(); + this.mods = mods ?? Array.Empty(); Success += onSuccess; } diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index 2683d7bc6d..6349f115cb 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -19,7 +19,7 @@ namespace osu.Game.Overlays.BeatmapSet { public class LeaderboardModSelector : CompositeDrawable { - public readonly BindableList SelectedMods = new BindableList(); + public readonly BindableList SelectedMods = new BindableList(); public readonly Bindable Ruleset = new Bindable(); private readonly FillFlowContainer modsContainer; @@ -54,7 +54,7 @@ namespace osu.Game.Overlays.BeatmapSet return; modsContainer.Add(new ModButton(new ModNoMod())); - modsContainer.AddRange(ruleset.NewValue.CreateInstance().AllMods.Where(m => m.UserPlayable).Select(m => new ModButton(m.CreateInstance()))); + modsContainer.AddRange(ruleset.NewValue.CreateInstance().AllMods.Where(m => m.UserPlayable).Select(m => new ModButton(m))); modsContainer.ForEach(button => { @@ -76,7 +76,7 @@ namespace osu.Game.Overlays.BeatmapSet updateHighlighted(); } - private void selectionChanged(Mod mod, bool selected) + private void selectionChanged(IMod mod, bool selected) { if (selected) SelectedMods.Add(mod); @@ -101,9 +101,9 @@ namespace osu.Game.Overlays.BeatmapSet private const int duration = 200; public readonly BindableBool Highlighted = new BindableBool(); - public Action OnSelectionChanged; + public Action OnSelectionChanged; - public ModButton(Mod mod) + public ModButton(IMod mod) : base(mod) { Scale = new Vector2(0.4f); From 212c3c699c3a3661904c6b502afd49cea327353e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Sep 2021 12:58:12 +0900 Subject: [PATCH 1807/2442] Reword xmldoc slightly --- osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs index bf8063cfaf..3658dbab83 100644 --- a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs @@ -92,8 +92,8 @@ namespace osu.Game.Beatmaps public BeatmapSetOnlineLanguage Language { get; set; } /// - /// The song ID of this beatmap set. - /// Non-null only if the song is from a featured artist. + /// The track ID of this beatmap set. + /// Non-null only if the track is linked to a featured artist track entry. /// public int? TrackId { get; set; } } From e636692596e8043826a1c461b4f5afd7c53f2589 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 10 Sep 2021 11:00:55 +0700 Subject: [PATCH 1808/2442] change background to background 5 --- osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index d3462c1787..6780cf4016 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -28,6 +28,7 @@ namespace osu.Game.Overlays.Changelog private readonly FillFlowContainer textContainer; private readonly Container imageContainer; + private readonly Box background; public ChangelogSupporterPromo() { @@ -56,10 +57,9 @@ namespace osu.Game.Overlays.Changelog }, Children = new Drawable[] { - new Box + background = new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex("#222027"), }, new Container { @@ -92,8 +92,10 @@ namespace osu.Game.Overlays.Changelog } [BackgroundDependencyLoader] - private void load(OsuColour colour, TextureStore textures) + private void load(OsuColour colour, TextureStore textures, OverlayColourProvider colourProvider) { + background.Colour = colourProvider.Background5; + SupporterPromoLinkFlowContainer supportLinkText; textContainer.Children = new Drawable[] { From 85b699182e1f9254a45f141988a22bf60898c54d Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 10 Sep 2021 13:01:54 +0900 Subject: [PATCH 1809/2442] Rename variable to be more descriptive --- osu.Game/Users/Drawables/UpdateableAvatar.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Users/Drawables/UpdateableAvatar.cs b/osu.Game/Users/Drawables/UpdateableAvatar.cs index 24d82c2784..6d48104131 100644 --- a/osu.Game/Users/Drawables/UpdateableAvatar.cs +++ b/osu.Game/Users/Drawables/UpdateableAvatar.cs @@ -44,7 +44,7 @@ namespace osu.Game.Users.Drawables protected override double LoadDelay => 200; - private readonly bool openOnClick; + private readonly bool isInteractive; private readonly bool showUsernameTooltip; private readonly bool showGuestOnNull; @@ -52,12 +52,12 @@ namespace osu.Game.Users.Drawables /// Construct a new UpdateableAvatar. /// /// The initial user to display. - /// Whether to open the user's profile when clicked. - /// Whether to show the username rather than "view profile" on the tooltip. + /// If set to true, hover/click sounds will play and clicking the avatar will open the user's profile. + /// Whether to show the username rather than "view profile" on the tooltip. (note: this only applies if is also true) /// Whether to show a default guest representation on null user (as opposed to nothing). - public UpdateableAvatar(User user = null, bool openOnClick = true, bool showUsernameTooltip = false, bool showGuestOnNull = true) + public UpdateableAvatar(User user = null, bool isInteractive = true, bool showUsernameTooltip = false, bool showGuestOnNull = true) { - this.openOnClick = openOnClick; + this.isInteractive = isInteractive; this.showUsernameTooltip = showUsernameTooltip; this.showGuestOnNull = showGuestOnNull; @@ -69,7 +69,7 @@ namespace osu.Game.Users.Drawables if (user == null && !showGuestOnNull) return null; - if (openOnClick) + if (isInteractive) { return new ClickableAvatar(user) { From 110f495345176873676462ee07349b9498f5120b Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 10 Sep 2021 11:11:55 +0700 Subject: [PATCH 1810/2442] move internal children to bdl --- .../Changelog/ChangelogSupporterPromo.cs | 148 +++++++++--------- 1 file changed, 71 insertions(+), 77 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index 6780cf4016..1b0a62dc4a 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -26,10 +26,6 @@ namespace osu.Game.Overlays.Changelog private const float image_container_width = 164; private const float heart_size = 75; - private readonly FillFlowContainer textContainer; - private readonly Container imageContainer; - private readonly Box background; - public ChangelogSupporterPromo() { RelativeSizeAxes = Axes.X; @@ -39,6 +35,12 @@ namespace osu.Game.Overlays.Changelog Vertical = 20, Horizontal = 50, }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colour, TextureStore textures, OverlayColourProvider colourProvider) + { + SupporterPromoLinkFlowContainer supportLinkText; InternalChildren = new Drawable[] { @@ -57,9 +59,10 @@ namespace osu.Game.Overlays.Changelog }, Children = new Drawable[] { - background = new Box + new Box { RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background5, }, new Container { @@ -68,7 +71,7 @@ namespace osu.Game.Overlays.Changelog Padding = new MarginPadding { Horizontal = 75 }, Children = new Drawable[] { - textContainer = new FillFlowContainer + new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -76,93 +79,84 @@ namespace osu.Game.Overlays.Changelog Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Padding = new MarginPadding { Right = 50 + image_container_width }, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = ChangelogStrings.SupportHeading, + Font = OsuFont.GetFont(size: 20, weight: FontWeight.Light), + Margin = new MarginPadding { Bottom = 20 }, + }, + supportLinkText = new SupporterPromoLinkFlowContainer(t => + { + t.Font = t.Font.With(size: 14); + t.Colour = colour.PinkLighter; + }) + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }, + new OsuTextFlowContainer(t => + { + t.Font = t.Font.With(size: 12); + t.Colour = colour.PinkLighter; + }) + { + Text = ChangelogStrings.SupportText2.ToString(), + Margin = new MarginPadding { Top = 10 }, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + } + }, }, - imageContainer = new Container + new Container { RelativeSizeAxes = Axes.Y, Width = image_container_width, Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, + Children = new Drawable[] + { + new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Margin = new MarginPadding { Bottom = 28 }, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fill, + Texture = textures.Get(@"Online/supporter-pippi"), + }, + new Container + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Size = new Vector2(heart_size), + Margin = new MarginPadding { Top = 70 }, + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = colour.Pink, + Radius = 10, + Roundness = heart_size / 2, + }, + Child = new Sprite + { + Size = new Vector2(heart_size), + Texture = textures.Get(@"Online/supporter-heart"), + }, + }, + } } } }, } }, }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colour, TextureStore textures, OverlayColourProvider colourProvider) - { - background.Colour = colourProvider.Background5; - - SupporterPromoLinkFlowContainer supportLinkText; - textContainer.Children = new Drawable[] - { - new OsuSpriteText - { - Text = ChangelogStrings.SupportHeading, - Font = OsuFont.GetFont(size: 20, weight: FontWeight.Light), - Margin = new MarginPadding { Bottom = 20 }, - }, - supportLinkText = new SupporterPromoLinkFlowContainer(t => - { - t.Font = t.Font.With(size: 14); - t.Colour = colour.PinkLighter; - }) - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - }, - new OsuTextFlowContainer(t => - { - t.Font = t.Font.With(size: 12); - t.Colour = colour.PinkLighter; - }) - { - Text = ChangelogStrings.SupportText2.ToString(), - Margin = new MarginPadding { Top = 10 }, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - } - }; supportLinkText.AddText("Support further development of osu! and "); supportLinkText.AddLink("become an osu!supporter", @"https://osu.ppy.sh/home/support", t => t.Font = t.Font.With(weight: FontWeight.Bold)); supportLinkText.AddText(" today!"); - - imageContainer.Children = new Drawable[] - { - new Sprite - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Margin = new MarginPadding { Bottom = 28 }, - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fill, - Texture = textures.Get(@"Online/supporter-pippi"), - }, - new Container - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Size = new Vector2(heart_size), - Margin = new MarginPadding { Top = 70 }, - Masking = true, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = colour.Pink, - Radius = 10, - Roundness = heart_size / 2, - }, - Child = new Sprite - { - Size = new Vector2(heart_size), - Texture = textures.Get(@"Online/supporter-heart"), - }, - }, - }; } private class SupporterPromoLinkFlowContainer : LinkFlowContainer From 8d1e43423eb5870746db19a4c178d6005e0ffc80 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 10 Sep 2021 14:18:40 +0900 Subject: [PATCH 1811/2442] Update calls to use new variable name --- osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs | 2 +- osu.Game/Overlays/Toolbar/ToolbarUserButton.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index cf930e985c..f5720cffb0 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -61,7 +61,7 @@ namespace osu.Game.Overlays.Profile.Header Origin = Anchor.CentreLeft, Children = new Drawable[] { - avatar = new UpdateableAvatar(openOnClick: false, showGuestOnNull: false) + avatar = new UpdateableAvatar(isInteractive: false, showGuestOnNull: false) { Size = new Vector2(avatar_size), Masking = true, diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs index 165c095514..5d4430caa2 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs @@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Toolbar Add(new OpaqueBackground { Depth = 1 }); - Flow.Add(avatar = new UpdateableAvatar(openOnClick: false) + Flow.Add(avatar = new UpdateableAvatar(isInteractive: false) { Masking = true, Size = new Vector2(32), From 84c152e7b6f7d3320a2dcc4f14f15b8485b29430 Mon Sep 17 00:00:00 2001 From: rednir Date: Fri, 10 Sep 2021 08:01:38 +0100 Subject: [PATCH 1812/2442] break when already found user Co-authored-by: Salman Ahmed --- osu.Game/Online/Chat/ChannelManager.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 34c6d048a3..3737451140 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -268,8 +268,12 @@ namespace osu.Game.Online.Chat // Check if the user has joined requested channel already. var alreadyJoinedChannel = JoinedChannels.FirstOrDefault( c => c.Type == ChannelType.PM && c.Users.Count == 1 && c.Name.Equals(content, StringComparison.OrdinalIgnoreCase)); + if (alreadyJoinedChannel != null) + { CurrentChannel.Value = alreadyJoinedChannel; + break; + } var request = new GetUserRequest(content); request.Success += OpenPrivateChannel; From 5ec615c7830af66959e2b99b9a36df0b1aebd205 Mon Sep 17 00:00:00 2001 From: rednir Date: Fri, 10 Sep 2021 08:02:15 +0100 Subject: [PATCH 1813/2442] display user in error message Co-authored-by: Salman Ahmed --- osu.Game/Online/Chat/ChannelManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 3737451140..9ecbc40ce2 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -277,7 +277,7 @@ namespace osu.Game.Online.Chat var request = new GetUserRequest(content); request.Success += OpenPrivateChannel; - request.Failure += _ => target.AddNewMessages(new ErrorMessage("User not found.")); + request.Failure += _ => target.AddNewMessages(new ErrorMessage($"User '{content}' was not found.")); api.Queue(request); break; From acb181ff2b6174aa04f0f4b88cc798ea557ad56a Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Fri, 10 Sep 2021 08:15:43 +0100 Subject: [PATCH 1814/2442] rename `alreadyJoinedChannel` -> `privateChannel` --- osu.Game/Online/Chat/ChannelManager.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 9ecbc40ce2..347f0af605 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -266,12 +266,12 @@ namespace osu.Game.Online.Chat } // Check if the user has joined requested channel already. - var alreadyJoinedChannel = JoinedChannels.FirstOrDefault( + var privateChannel = JoinedChannels.FirstOrDefault( c => c.Type == ChannelType.PM && c.Users.Count == 1 && c.Name.Equals(content, StringComparison.OrdinalIgnoreCase)); - if (alreadyJoinedChannel != null) + if (privateChannel != null) { - CurrentChannel.Value = alreadyJoinedChannel; + CurrentChannel.Value = privateChannel; break; } From 5a069546656e88d48501d7e4536f9fed42871c1e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Sep 2021 18:16:10 +0900 Subject: [PATCH 1815/2442] Add test coverage of game exit scenario --- .../Visual/Navigation/TestSceneScreenNavigation.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index b536233ff0..cc64d37116 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -15,6 +15,7 @@ using osu.Game.Overlays.Mods; using osu.Game.Overlays.Toolbar; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Screens.Menu; using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.Play; @@ -388,6 +389,19 @@ namespace osu.Game.Tests.Visual.Navigation AddAssert("now playing is hidden", () => nowPlayingOverlay.State.Value == Visibility.Hidden); } + [Test] + public void TestExitGameFromSongSelect() + { + PushAndConfirm(() => new TestPlaySongSelect()); + exitViaEscapeAndConfirm(); + + pushEscape(); // returns to osu! logo + + AddStep("Hold escape", () => InputManager.PressKey(Key.Escape)); + AddUntilStep("Wait for intro", () => Game.ScreenStack.CurrentScreen is IntroTriangles); + AddUntilStep("Wait for game exit", () => Game.ScreenStack.CurrentScreen == null); + } + private void pushEscape() => AddStep("Press escape", () => InputManager.Key(Key.Escape)); From 94702ee7e3bb1df48a9a07ab63e07bfb059b9f7e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Sep 2021 18:10:03 +0900 Subject: [PATCH 1816/2442] Fix triangles intro attempting to restart track after it is disposed --- osu.Game/Screens/Menu/IntroTriangles.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/IntroTriangles.cs b/osu.Game/Screens/Menu/IntroTriangles.cs index 36296487a8..a8ca17cec1 100644 --- a/osu.Game/Screens/Menu/IntroTriangles.cs +++ b/osu.Game/Screens/Menu/IntroTriangles.cs @@ -42,6 +42,7 @@ namespace osu.Game.Screens.Menu private Sample welcome; private DecoupleableInterpolatingFramedClock decoupledClock; + private TrianglesIntroSequence intro; [BackgroundDependencyLoader] private void load() @@ -66,7 +67,7 @@ namespace osu.Game.Screens.Menu if (UsingThemedIntro) decoupledClock.ChangeSource(Track); - LoadComponentAsync(new TrianglesIntroSequence(logo, background) + LoadComponentAsync(intro = new TrianglesIntroSequence(logo, background) { RelativeSizeAxes = Axes.Both, Clock = decoupledClock, @@ -82,6 +83,14 @@ namespace osu.Game.Screens.Menu } } + public override void OnSuspending(IScreen next) + { + base.OnSuspending(next); + + // important as there is a clock attached to a track which will likely be disposed before returning to this screen. + intro.Expire(); + } + public override void OnResuming(IScreen last) { base.OnResuming(last); From 0e5659acb29789a6cde29fdd0fa84a4f8c4f563d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 11 Sep 2021 14:10:29 +0200 Subject: [PATCH 1817/2442] Remove leftover exception throw --- osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs b/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs index fac0661a15..2cb696be0a 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs @@ -71,7 +71,6 @@ namespace osu.Game.Graphics.UserInterfaceV2 public void OnReleased(GlobalAction action) { - throw new System.NotImplementedException(); } } } From c166f1a06ab09e2309284429f69f9f673ec594d0 Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Sat, 11 Sep 2021 14:18:09 +0100 Subject: [PATCH 1818/2442] change error message based on exception message --- osu.Game/Online/Chat/ChannelManager.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 347f0af605..ffbd34abde 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -277,7 +277,9 @@ namespace osu.Game.Online.Chat var request = new GetUserRequest(content); request.Success += OpenPrivateChannel; - request.Failure += _ => target.AddNewMessages(new ErrorMessage($"User '{content}' was not found.")); + request.Failure += e => target.AddNewMessages( + new ErrorMessage(e.InnerException?.Message == "NotFound" ? $"User '{content}' was not found." : "Could not fetch user.")); + api.Queue(request); break; From 1b264a2dd0ec1a08bfd0662f3e47d792a40b91db Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Sat, 11 Sep 2021 16:22:29 +0100 Subject: [PATCH 1819/2442] make user lookup string public --- osu.Game/Online/API/Requests/GetUserRequest.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetUserRequest.cs b/osu.Game/Online/API/Requests/GetUserRequest.cs index fe954372bf..730e4e02ed 100644 --- a/osu.Game/Online/API/Requests/GetUserRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRequest.cs @@ -8,7 +8,7 @@ namespace osu.Game.Online.API.Requests { public class GetUserRequest : APIRequest { - private readonly string lookup; + public readonly string Lookup; public readonly RulesetInfo Ruleset; private readonly LookupType lookupType; @@ -26,7 +26,7 @@ namespace osu.Game.Online.API.Requests /// The ruleset to get the user's info for. public GetUserRequest(long? userId = null, RulesetInfo ruleset = null) { - lookup = userId.ToString(); + Lookup = userId.ToString(); lookupType = LookupType.Id; Ruleset = ruleset; } @@ -38,12 +38,12 @@ namespace osu.Game.Online.API.Requests /// The ruleset to get the user's info for. public GetUserRequest(string username = null, RulesetInfo ruleset = null) { - lookup = username; + Lookup = username; lookupType = LookupType.Username; Ruleset = ruleset; } - protected override string Target => lookup != null ? $@"users/{lookup}/{Ruleset?.ShortName}?key={lookupType.ToString().ToLower()}" : $@"me/{Ruleset?.ShortName}"; + protected override string Target => Lookup != null ? $@"users/{Lookup}/{Ruleset?.ShortName}?key={lookupType.ToString().ToLower()}" : $@"me/{Ruleset?.ShortName}"; private enum LookupType { From 7924a990a346a4f98e5884e4b2d071da6d0b21f6 Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Sat, 11 Sep 2021 16:22:35 +0100 Subject: [PATCH 1820/2442] add tests for /chat command --- .../Visual/Online/TestSceneChatOverlay.cs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 7cfca31167..90263e9deb 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -1,6 +1,7 @@ // 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; @@ -85,6 +86,21 @@ namespace osu.Game.Tests.Visual.Online case JoinChannelRequest joinChannel: joinChannel.TriggerSuccess(); return true; + + case GetUserRequest getUser: + if (getUser.Lookup.Equals("some body", StringComparison.OrdinalIgnoreCase)) + { + getUser.TriggerSuccess(new User() + { + Username = "some body", + Id = 1, + }); + } + else + { + getUser.TriggerFailure(new Exception()); + } + return true; } return false; @@ -322,6 +338,27 @@ namespace osu.Game.Tests.Visual.Online AddAssert("Current channel is channel 1", () => currentChannel == channel1); } + [Test] + public void TestChatCommand() + { + AddStep("Join channel 1", () => channelManager.JoinChannel(channel1)); + AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); + + AddStep("Open chat with user.", () => channelManager.PostCommand("chat some body")); + AddAssert("PM channel is selected", () => + channelManager.CurrentChannel.Value.Type == ChannelType.PM && channelManager.CurrentChannel.Value.Users.Single().Username == "some body"); + + AddStep("Open chat with non-existant user", () => channelManager.PostCommand("chat nobody")); + AddAssert("Last message is error", () => channelManager.CurrentChannel.Value.Messages.Last().GetType() == typeof(ErrorMessage)); + + // Make sure no unnecessary requests are made when the PM chanenl is already open. + AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); + AddStep("Unregister request handling", () => ((DummyAPIAccess)API).HandleRequest = null); + AddStep("Open chat with user.", () => channelManager.PostCommand("chat some body")); + AddAssert("PM channel is selected", () => + channelManager.CurrentChannel.Value.Type == ChannelType.PM && channelManager.CurrentChannel.Value.Users.Single().Username == "some body"); + } + private void pressChannelHotkey(int number) { var channelKey = Key.Number0 + number; From 605933c46722181edeb4c4280df9cc5df4661f92 Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Sat, 11 Sep 2021 16:23:17 +0100 Subject: [PATCH 1821/2442] typo --- osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 90263e9deb..e546575319 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -351,7 +351,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("Open chat with non-existant user", () => channelManager.PostCommand("chat nobody")); AddAssert("Last message is error", () => channelManager.CurrentChannel.Value.Messages.Last().GetType() == typeof(ErrorMessage)); - // Make sure no unnecessary requests are made when the PM chanenl is already open. + // Make sure no unnecessary requests are made when the PM channel is already open. AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); AddStep("Unregister request handling", () => ((DummyAPIAccess)API).HandleRequest = null); AddStep("Open chat with user.", () => channelManager.PostCommand("chat some body")); From eeaa8a838076040a0bb1e7f659e83ebbbaad71bd Mon Sep 17 00:00:00 2001 From: Davran Dilshat Date: Sat, 11 Sep 2021 16:47:20 +0100 Subject: [PATCH 1822/2442] code quality --- osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index e546575319..9200b5b3dd 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -90,7 +90,7 @@ namespace osu.Game.Tests.Visual.Online case GetUserRequest getUser: if (getUser.Lookup.Equals("some body", StringComparison.OrdinalIgnoreCase)) { - getUser.TriggerSuccess(new User() + getUser.TriggerSuccess(new User { Username = "some body", Id = 1, @@ -100,6 +100,7 @@ namespace osu.Game.Tests.Visual.Online { getUser.TriggerFailure(new Exception()); } + return true; } From b9c127c07ef27aebaffed87e94457aaa057ce9ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 11 Sep 2021 15:54:49 +0200 Subject: [PATCH 1823/2442] Improve content transitions in beatmap listing --- osu.Game/Overlays/BeatmapListingOverlay.cs | 27 +++++++++++----------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 6861d17f26..935a89b99b 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -75,6 +75,7 @@ namespace osu.Game.Overlays { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, + Masking = true, Padding = new MarginPadding { Horizontal = 20 }, Children = new Drawable[] { @@ -186,21 +187,16 @@ namespace osu.Game.Overlays if (lastContent != null) { - var transform = lastContent.FadeOut(100, Easing.OutQuint); + lastContent.FadeOut(100, Easing.OutQuint); - if (lastContent == notFoundContent || lastContent == supporterRequiredContent) - { - // the placeholders may be used multiple times, so don't expire/dispose them. - transform.Schedule(() => panelTarget.Remove(lastContent)); - } - else - { - // Consider the case when the new content is smaller than the last content. - // If the auto-size computation is delayed until fade out completes, the background remain high for too long making the resulting transition to the smaller height look weird. - // At the same time, if the last content's height is bypassed immediately, there is a period where the new content is at Alpha = 0 when the auto-sized height will be 0. - // To resolve both of these issues, the bypass is delayed until a point when the content transitions (fade-in and fade-out) overlap and it looks good to do so. - lastContent.Delay(25).Schedule(() => lastContent.BypassAutoSizeAxes = Axes.Y).Then().Schedule(() => lastContent.Expire()); - } + // Consider the case when the new content is smaller than the last content. + // If the auto-size computation is delayed until fade out completes, the background remain high for too long making the resulting transition to the smaller height look weird. + // At the same time, if the last content's height is bypassed immediately, there is a period where the new content is at Alpha = 0 when the auto-sized height will be 0. + // To resolve both of these issues, the bypass is delayed until a point when the content transitions (fade-in and fade-out) overlap and it looks good to do so. + var sequence = lastContent.Delay(25).Schedule(() => lastContent.BypassAutoSizeAxes = Axes.Y); + + if (lastContent != notFoundContent && lastContent != supporterRequiredContent) + sequence.Then().Schedule(() => lastContent.Expire()); } if (!content.IsAlive) @@ -208,6 +204,9 @@ namespace osu.Game.Overlays content.FadeInFromZero(200, Easing.OutQuint); currentContent = content; + // currentContent may be one of the placeholders, and still have BypassAutoSizeAxes set to Y from the last fade-out. + // restore to the initial state. + currentContent.BypassAutoSizeAxes = Axes.None; } protected override void Dispose(bool isDisposing) From b8a6925175cc7b40036d10d1e3d74b12feaf331b Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Sat, 11 Sep 2021 17:41:07 -0400 Subject: [PATCH 1824/2442] Use already-resolved LoungeSubScreen instead of nested delegates --- osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index bedee39a26..972c7aed47 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.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 osu.Framework.Allocation; using osu.Framework.Audio; @@ -123,7 +122,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge } } - public Popover GetPopover() => new PasswordEntryPopover(Room) { JoinRequested = lounge.Join }; + public Popover GetPopover() => new PasswordEntryPopover(Room) { Lounge = lounge }; public MenuItem[] ContextMenuItems => new MenuItem[] { @@ -179,7 +178,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { private readonly Room room; - public Action, Action> JoinRequested; + public LoungeSubScreen Lounge; public PasswordEntryPopover(Room room) { @@ -220,7 +219,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge }; Child = shakeContainer; - joinButton.Action = () => JoinRequested?.Invoke(room, passwordTextbox.Text, null, joinFailed); + joinButton.Action = () => Lounge?.Join(room, passwordTextbox.Text, null, joinFailed); } private void joinFailed(string error) @@ -239,7 +238,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge base.LoadComplete(); Schedule(() => GetContainingInputManager().ChangeFocus(passwordTextbox)); - passwordTextbox.OnCommit += (_, __) => JoinRequested?.Invoke(room, passwordTextbox.Text, null, joinFailed); + passwordTextbox.OnCommit += (_, __) => Lounge?.Join(room, passwordTextbox.Text, null, joinFailed); } } } From 6cdc8424527c3c9ccb697f9c9031207d6a3a57bb Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Sat, 11 Sep 2021 17:42:49 -0400 Subject: [PATCH 1825/2442] Remove placeholder text response Weird UX, doesn't feel right compared to the rest of lazer --- osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index 972c7aed47..8cf34cd9f3 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -225,8 +225,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private void joinFailed(string error) { passwordTextbox.Text = string.Empty; - passwordTextbox.PlaceholderText = "incorrect password"; - passwordTextbox.Colour = Color4.Red; shakeContainer.OnShakeFinish += () => passwordTextbox.Colour = Color4.White; From e018071be47e31457c6969d0cbac618e37932b3a Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Sat, 11 Sep 2021 19:50:41 -0400 Subject: [PATCH 1826/2442] Remove OnShakeFinish event --- osu.Game/Graphics/Containers/ShakeContainer.cs | 7 ------- osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs | 2 -- 2 files changed, 9 deletions(-) diff --git a/osu.Game/Graphics/Containers/ShakeContainer.cs b/osu.Game/Graphics/Containers/ShakeContainer.cs index ffa8ef585e..dca9df1e98 100644 --- a/osu.Game/Graphics/Containers/ShakeContainer.cs +++ b/osu.Game/Graphics/Containers/ShakeContainer.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 osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -27,11 +26,6 @@ namespace osu.Game.Graphics.Containers /// public float ShakeMagnitude = 8; - /// - /// Fired when finishes - /// - public event Action OnShakeFinish; - /// /// Shake the contents of this container. /// @@ -56,7 +50,6 @@ namespace osu.Game.Graphics.Containers } sequence.MoveToX(0, ShakeDuration / 2, Easing.InSine); - sequence.Finally(_ => OnShakeFinish?.Invoke()); } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index 8cf34cd9f3..db1566999e 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -226,8 +226,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { passwordTextbox.Text = string.Empty; - shakeContainer.OnShakeFinish += () => passwordTextbox.Colour = Color4.White; - shakeContainer.Shake(); } From e511c2ef2b0be57fbdc4ffb8d899eaa3ddac891a Mon Sep 17 00:00:00 2001 From: rednir Date: Sun, 12 Sep 2021 08:50:53 +0100 Subject: [PATCH 1827/2442] add comment --- osu.Game/Online/Chat/ChannelManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index ffbd34abde..853c28c7c8 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -265,7 +265,8 @@ namespace osu.Game.Online.Chat break; } - // Check if the user has joined requested channel already. + // Check if the user has joined the requested channel already. + // This uses the channel name for comparison as the PM user's username is unavailable after a restart. var privateChannel = JoinedChannels.FirstOrDefault( c => c.Type == ChannelType.PM && c.Users.Count == 1 && c.Name.Equals(content, StringComparison.OrdinalIgnoreCase)); From c4627bed6dbf713369273026f2ee946ad68895af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 12 Sep 2021 12:56:36 +0200 Subject: [PATCH 1828/2442] Print username in case of generic network failure too --- osu.Game/Online/Chat/ChannelManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 853c28c7c8..47d5955fb0 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -279,7 +279,7 @@ namespace osu.Game.Online.Chat var request = new GetUserRequest(content); request.Success += OpenPrivateChannel; request.Failure += e => target.AddNewMessages( - new ErrorMessage(e.InnerException?.Message == "NotFound" ? $"User '{content}' was not found." : "Could not fetch user.")); + new ErrorMessage(e.InnerException?.Message == @"NotFound" ? $"User '{content}' was not found." : $"Could not fetch user '{content}'.")); api.Queue(request); break; From 3467b1f60c9d3f3c3fae1c75cda9702316fec4d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 12 Sep 2021 13:00:52 +0200 Subject: [PATCH 1829/2442] Retouch chat command test slightly --- osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 9200b5b3dd..609e637914 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -345,17 +345,17 @@ namespace osu.Game.Tests.Visual.Online AddStep("Join channel 1", () => channelManager.JoinChannel(channel1)); AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); - AddStep("Open chat with user.", () => channelManager.PostCommand("chat some body")); + AddStep("Open chat with user", () => channelManager.PostCommand("chat some body")); AddAssert("PM channel is selected", () => channelManager.CurrentChannel.Value.Type == ChannelType.PM && channelManager.CurrentChannel.Value.Users.Single().Username == "some body"); - AddStep("Open chat with non-existant user", () => channelManager.PostCommand("chat nobody")); - AddAssert("Last message is error", () => channelManager.CurrentChannel.Value.Messages.Last().GetType() == typeof(ErrorMessage)); + AddStep("Open chat with non-existent user", () => channelManager.PostCommand("chat nobody")); + AddAssert("Last message is error", () => channelManager.CurrentChannel.Value.Messages.Last() is ErrorMessage); // Make sure no unnecessary requests are made when the PM channel is already open. AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); AddStep("Unregister request handling", () => ((DummyAPIAccess)API).HandleRequest = null); - AddStep("Open chat with user.", () => channelManager.PostCommand("chat some body")); + AddStep("Open chat with user", () => channelManager.PostCommand("chat some body")); AddAssert("PM channel is selected", () => channelManager.CurrentChannel.Value.Type == ChannelType.PM && channelManager.CurrentChannel.Value.Users.Single().Username == "some body"); } From 8357efc74fbaaf0e244ab8ce52d4a44e41995d6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 11 Sep 2021 20:43:17 +0200 Subject: [PATCH 1830/2442] Make `EditorTestScene` go through `EditorLoader` --- osu.Game/Screens/Edit/EditorLoader.cs | 28 +++++++++++---------- osu.Game/Tests/Visual/EditorTestScene.cs | 31 +++++++++++++++++++++--- 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorLoader.cs b/osu.Game/Screens/Edit/EditorLoader.cs index aec7d32939..6bbfa92c3b 100644 --- a/osu.Game/Screens/Edit/EditorLoader.cs +++ b/osu.Game/Screens/Edit/EditorLoader.cs @@ -34,6 +34,20 @@ namespace osu.Game.Screens.Edit [CanBeNull] private ScheduledDelegate scheduledDifficultySwitch; + [BackgroundDependencyLoader] + private void load() + { + AddRangeInternal(new Drawable[] + { + new LoadingSpinner(true) + { + State = { Value = Visibility.Visible }, + } + }); + } + + protected virtual Editor CreateEditor() => new Editor(this); + protected override void LogoArriving(OsuLogo logo, bool resuming) { base.LogoArriving(logo, resuming); @@ -47,18 +61,6 @@ namespace osu.Game.Screens.Edit } } - [BackgroundDependencyLoader] - private void load() - { - AddRangeInternal(new Drawable[] - { - new LoadingSpinner(true) - { - State = { Value = Visibility.Visible }, - } - }); - } - public void ScheduleDifficultySwitch(BeatmapInfo beatmapInfo) { scheduledDifficultySwitch?.Cancel(); @@ -81,7 +83,7 @@ namespace osu.Game.Screens.Edit private void pushEditor() { - this.Push(new Editor(this)); + this.Push(CreateEditor()); ValidForResume = false; } diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index a393802309..9fb9df39a8 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -16,6 +16,7 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Edit; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Compose.Components.Timeline; +using osu.Game.Screens.Menu; using osu.Game.Skinning; namespace osu.Game.Tests.Visual @@ -24,7 +25,9 @@ namespace osu.Game.Tests.Visual { protected EditorBeatmap EditorBeatmap; - protected TestEditor Editor { get; private set; } + private TestEditorLoader editorLoader; + + protected TestEditor Editor => editorLoader.Editor; protected EditorClock EditorClock { get; private set; } @@ -33,9 +36,19 @@ namespace osu.Game.Tests.Visual /// protected virtual bool IsolateSavingFromDatabase => true; + // required for screen transitions to work properly + // (see comment in EditorLoader.LogoArriving). + [Cached] + private OsuLogo logo = new OsuLogo + { + Alpha = 0 + }; + [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio, RulesetStore rulesets) { + Add(logo); + var working = CreateWorkingBeatmap(Ruleset.Value); Beatmap.Value = working; @@ -59,7 +72,7 @@ namespace osu.Game.Tests.Visual protected virtual void LoadEditor() { - LoadScreen(Editor = CreateEditor()); + LoadScreen(editorLoader = new TestEditorLoader()); } /// @@ -70,7 +83,14 @@ namespace osu.Game.Tests.Visual protected sealed override Ruleset CreateRuleset() => CreateEditorRuleset(); - protected virtual TestEditor CreateEditor() => new TestEditor(); + protected class TestEditorLoader : EditorLoader + { + public TestEditor Editor { get; private set; } + + protected sealed override Editor CreateEditor() => Editor = CreateTestEditor(this); + + protected virtual TestEditor CreateTestEditor(EditorLoader loader) => new TestEditor(loader); + } protected class TestEditor : Editor { @@ -87,6 +107,11 @@ namespace osu.Game.Tests.Visual public new void Paste() => base.Paste(); public new bool HasUnsavedChanges => base.HasUnsavedChanges; + + public TestEditor(EditorLoader loader = null) + : base(loader) + { + } } private class TestBeatmapManager : BeatmapManager From 5ae2f419304d95505308888f10a64e5a867e699d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 11 Sep 2021 21:07:34 +0200 Subject: [PATCH 1831/2442] Make difficulty switching test scene use `EditorTestScene` --- .../Editing/TestSceneDifficultySwitching.cs | 54 +++++++------------ 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs index 0f3d413a7d..587c37c6f5 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs @@ -9,26 +9,20 @@ using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Dialog; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Components.Menus; -using osu.Game.Screens.Menu; using osu.Game.Tests.Beatmaps.IO; using osuTK.Input; namespace osu.Game.Tests.Visual.Editing { - public class TestSceneDifficultySwitching : ScreenTestScene + public class TestSceneDifficultySwitching : EditorTestScene { - private BeatmapSetInfo importedBeatmapSet; - private Editor editor; + protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); - // required for screen transitions to work properly - // (see comment in EditorLoader.LogoArriving). - [Cached] - private OsuLogo logo = new OsuLogo - { - Alpha = 0 - }; + protected override bool IsolateSavingFromDatabase => false; [Resolved] private OsuGameBase game { get; set; } @@ -36,20 +30,18 @@ namespace osu.Game.Tests.Visual.Editing [Resolved] private BeatmapManager beatmaps { get; set; } - [BackgroundDependencyLoader] - private void load() => Add(logo); + private BeatmapSetInfo importedBeatmapSet; - [SetUpSteps] - public void SetUp() + public override void SetUpSteps() { AddStep("import test beatmap", () => importedBeatmapSet = ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).Result); + base.SetUpSteps(); + } - AddStep("set current beatmap", () => Beatmap.Value = beatmaps.GetWorkingBeatmap(importedBeatmapSet.Beatmaps.First())); - AddStep("push loader", () => Stack.Push(new EditorLoader())); - - AddUntilStep("wait for editor push", () => Stack.CurrentScreen is Editor); - AddStep("store editor", () => editor = (Editor)Stack.CurrentScreen); - AddUntilStep("wait for editor to load", () => editor.IsLoaded); + protected override void LoadEditor() + { + Beatmap.Value = beatmaps.GetWorkingBeatmap(importedBeatmapSet.Beatmaps.First()); + base.LoadEditor(); } [Test] @@ -72,11 +64,7 @@ namespace osu.Game.Tests.Visual.Editing BeatmapInfo targetDifficulty = null; PromptForSaveDialog saveDialog = null; - AddStep("remove first hitobject", () => - { - var editorBeatmap = editor.ChildrenOfType().Single(); - editorBeatmap.RemoveAt(0); - }); + AddStep("remove first hitobject", () => EditorBeatmap.RemoveAt(0)); AddStep("set target difficulty", () => targetDifficulty = importedBeatmapSet.Beatmaps.Last(beatmap => !beatmap.Equals(Beatmap.Value.BeatmapInfo))); switchToDifficulty(() => targetDifficulty); @@ -105,11 +93,7 @@ namespace osu.Game.Tests.Visual.Editing BeatmapInfo targetDifficulty = null; PromptForSaveDialog saveDialog = null; - AddStep("remove first hitobject", () => - { - var editorBeatmap = editor.ChildrenOfType().Single(); - editorBeatmap.RemoveAt(0); - }); + AddStep("remove first hitobject", () => EditorBeatmap.RemoveAt(0)); AddStep("set target difficulty", () => targetDifficulty = importedBeatmapSet.Beatmaps.Last(beatmap => !beatmap.Equals(Beatmap.Value.BeatmapInfo))); switchToDifficulty(() => targetDifficulty); @@ -134,10 +118,10 @@ namespace osu.Game.Tests.Visual.Editing private void switchToDifficulty(Func difficulty) { - AddUntilStep("wait for menubar to load", () => editor.ChildrenOfType().Any()); + AddUntilStep("wait for menubar to load", () => Editor.ChildrenOfType().Any()); AddStep("open file menu", () => { - var menuBar = editor.ChildrenOfType().Single(); + var menuBar = Editor.ChildrenOfType().Single(); var fileMenu = menuBar.ChildrenOfType().First(); InputManager.MoveMouseTo(fileMenu); InputManager.Click(MouseButton.Left); @@ -146,7 +130,7 @@ namespace osu.Game.Tests.Visual.Editing AddStep("open difficulty menu", () => { var difficultySelector = - editor.ChildrenOfType().Single(item => item.Item.Text.Value.ToString().Contains("Change difficulty")); + Editor.ChildrenOfType().Single(item => item.Item.Text.Value.ToString().Contains("Change difficulty")); InputManager.MoveMouseTo(difficultySelector); }); AddWaitStep("wait for open", 3); @@ -154,7 +138,7 @@ namespace osu.Game.Tests.Visual.Editing AddStep("switch to target difficulty", () => { var difficultyMenuItem = - editor.ChildrenOfType() + Editor.ChildrenOfType() .Last(item => item.Item is DifficultyMenuItem difficultyItem && difficultyItem.Beatmap.Equals(difficulty.Invoke())); InputManager.MoveMouseTo(difficultyMenuItem); InputManager.Click(MouseButton.Left); From f8a681d810b2fc7c582a7941480bb11f50b8d977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 12 Sep 2021 13:40:06 +0200 Subject: [PATCH 1832/2442] Delegate `Editor{Beatmap,Clock}` to `Editor` directly --- osu.Game/Tests/Visual/EditorTestScene.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index 9fb9df39a8..3bfffeb00e 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -23,13 +23,12 @@ namespace osu.Game.Tests.Visual { public abstract class EditorTestScene : ScreenTestScene { - protected EditorBeatmap EditorBeatmap; - private TestEditorLoader editorLoader; protected TestEditor Editor => editorLoader.Editor; - protected EditorClock EditorClock { get; private set; } + protected EditorBeatmap EditorBeatmap => Editor.ChildrenOfType().Single(); + protected EditorClock EditorClock => Editor.ChildrenOfType().Single(); /// /// Whether any saves performed by the editor should be isolate (and not persist) to the underlying . @@ -66,8 +65,6 @@ namespace osu.Game.Tests.Visual AddStep("load editor", LoadEditor); AddUntilStep("wait for editor to load", () => EditorComponentsReady); - AddStep("get beatmap", () => EditorBeatmap = Editor.ChildrenOfType().Single()); - AddStep("get clock", () => EditorClock = Editor.ChildrenOfType().Single()); } protected virtual void LoadEditor() From 22fa9a303e469cc8bea35e57e187adc9cf0fd192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 12 Sep 2021 13:55:48 +0200 Subject: [PATCH 1833/2442] Expose test helper for switching between difficulties --- .../Editing/TestSceneDifficultySwitching.cs | 32 +------------------ osu.Game/Screens/Edit/Editor.cs | 4 +-- osu.Game/Tests/Visual/EditorTestScene.cs | 2 ++ 3 files changed, 5 insertions(+), 33 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs index 587c37c6f5..a439555fde 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs @@ -7,14 +7,11 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Testing; using osu.Game.Beatmaps; -using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Dialog; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Screens.Edit; -using osu.Game.Screens.Edit.Components.Menus; using osu.Game.Tests.Beatmaps.IO; -using osuTK.Input; namespace osu.Game.Tests.Visual.Editing { @@ -116,34 +113,7 @@ namespace osu.Game.Tests.Visual.Editing AddAssert("stack empty", () => Stack.CurrentScreen == null); } - private void switchToDifficulty(Func difficulty) - { - AddUntilStep("wait for menubar to load", () => Editor.ChildrenOfType().Any()); - AddStep("open file menu", () => - { - var menuBar = Editor.ChildrenOfType().Single(); - var fileMenu = menuBar.ChildrenOfType().First(); - InputManager.MoveMouseTo(fileMenu); - InputManager.Click(MouseButton.Left); - }); - - AddStep("open difficulty menu", () => - { - var difficultySelector = - Editor.ChildrenOfType().Single(item => item.Item.Text.Value.ToString().Contains("Change difficulty")); - InputManager.MoveMouseTo(difficultySelector); - }); - AddWaitStep("wait for open", 3); - - AddStep("switch to target difficulty", () => - { - var difficultyMenuItem = - Editor.ChildrenOfType() - .Last(item => item.Item is DifficultyMenuItem difficultyItem && difficultyItem.Beatmap.Equals(difficulty.Invoke())); - InputManager.MoveMouseTo(difficultyMenuItem); - InputManager.Click(MouseButton.Left); - }); - } + private void switchToDifficulty(Func difficulty) => AddStep("switch to difficulty", () => Editor.SwitchToDifficulty(difficulty.Invoke())); private void confirmEditingBeatmap(Func targetDifficulty) { diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 1b9a94da58..28ae7e620e 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -737,10 +737,10 @@ namespace osu.Game.Screens.Edit private DifficultyMenuItem createDifficultyMenuItem(BeatmapInfo beatmapInfo) { bool isCurrentDifficulty = playableBeatmap.BeatmapInfo.Equals(beatmapInfo); - return new DifficultyMenuItem(beatmapInfo, isCurrentDifficulty, switchToDifficulty); + return new DifficultyMenuItem(beatmapInfo, isCurrentDifficulty, SwitchToDifficulty); } - private void switchToDifficulty(BeatmapInfo beatmapInfo) => loader?.ScheduleDifficultySwitch(beatmapInfo); + protected void SwitchToDifficulty(BeatmapInfo beatmapInfo) => loader?.ScheduleDifficultySwitch(beatmapInfo); private void cancelExit() => loader?.CancelPendingDifficultySwitch(); diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index 3bfffeb00e..1e26036116 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -103,6 +103,8 @@ namespace osu.Game.Tests.Visual public new void Paste() => base.Paste(); + public new void SwitchToDifficulty(BeatmapInfo beatmapInfo) => base.SwitchToDifficulty(beatmapInfo); + public new bool HasUnsavedChanges => base.HasUnsavedChanges; public TestEditor(EditorLoader loader = null) From eae5d62fa5e0c9b372cdfe4259a77cefcfe7fa7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 12 Sep 2021 15:50:41 +0200 Subject: [PATCH 1834/2442] Store editor beatmap locally before editor exit --- .../Visual/Editing/TestSceneEditorBeatmapCreation.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs index b6ae91844a..440d66ff9f 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs @@ -11,6 +11,7 @@ using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; +using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Setup; using osu.Game.Tests.Resources; using SharpCompress.Archives; @@ -55,6 +56,9 @@ namespace osu.Game.Tests.Visual.Editing [Test] public void TestExitWithoutSave() { + EditorBeatmap editorBeatmap = null; + + AddStep("store editor beatmap", () => editorBeatmap = EditorBeatmap); AddStep("exit without save", () => { Editor.Exit(); @@ -62,7 +66,7 @@ namespace osu.Game.Tests.Visual.Editing }); AddUntilStep("wait for exit", () => !Editor.IsCurrentScreen()); - AddAssert("new beatmap not persisted", () => beatmapManager.QueryBeatmapSet(s => s.ID == EditorBeatmap.BeatmapInfo.BeatmapSet.ID)?.DeletePending == true); + AddAssert("new beatmap not persisted", () => beatmapManager.QueryBeatmapSet(s => s.ID == editorBeatmap.BeatmapInfo.BeatmapSet.ID)?.DeletePending == true); } [Test] From 925b455330b923d6f6059337e9ccb006be270c61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 12 Sep 2021 16:40:52 +0200 Subject: [PATCH 1835/2442] Add "samples match playback rate" setting to beatmap info --- osu.Game/Beatmaps/BeatmapInfo.cs | 5 + ...11_AddSamplesMatchPlaybackRate.Designer.cs | 515 ++++++++++++++++++ ...10912144011_AddSamplesMatchPlaybackRate.cs | 23 + .../Migrations/OsuDbContextModelSnapshot.cs | 2 + 4 files changed, 545 insertions(+) create mode 100644 osu.Game/Migrations/20210912144011_AddSamplesMatchPlaybackRate.Designer.cs create mode 100644 osu.Game/Migrations/20210912144011_AddSamplesMatchPlaybackRate.cs diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 3eb766a667..7dd1dd2cf4 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -93,6 +93,11 @@ namespace osu.Game.Beatmaps public bool WidescreenStoryboard { get; set; } public bool EpilepsyWarning { get; set; } + /// + /// Whether or not sound samples should change rate when playing with speed-changing mods. + /// + public bool SamplesMatchPlaybackRate { get; set; } + public CountdownType Countdown { get; set; } = CountdownType.Normal; /// diff --git a/osu.Game/Migrations/20210912144011_AddSamplesMatchPlaybackRate.Designer.cs b/osu.Game/Migrations/20210912144011_AddSamplesMatchPlaybackRate.Designer.cs new file mode 100644 index 0000000000..6e53d7fae0 --- /dev/null +++ b/osu.Game/Migrations/20210912144011_AddSamplesMatchPlaybackRate.Designer.cs @@ -0,0 +1,515 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using osu.Game.Database; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20210912144011_AddSamplesMatchPlaybackRate")] + partial class AddSamplesMatchPlaybackRate + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.6-servicing-10079"); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("ApproachRate"); + + b.Property("CircleSize"); + + b.Property("DrainRate"); + + b.Property("OverallDifficulty"); + + b.Property("SliderMultiplier"); + + b.Property("SliderTickRate"); + + b.HasKey("ID"); + + b.ToTable("BeatmapDifficulty"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("AudioLeadIn"); + + b.Property("BPM"); + + b.Property("BaseDifficultyID"); + + b.Property("BeatDivisor"); + + b.Property("BeatmapSetInfoID"); + + b.Property("Countdown"); + + b.Property("CountdownOffset"); + + b.Property("DistanceSpacing"); + + b.Property("EpilepsyWarning"); + + b.Property("GridSize"); + + b.Property("Hash"); + + b.Property("Hidden"); + + b.Property("Length"); + + b.Property("LetterboxInBreaks"); + + b.Property("MD5Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + + b.Property("Path"); + + b.Property("RulesetID"); + + b.Property("SamplesMatchPlaybackRate"); + + b.Property("SpecialStyle"); + + b.Property("StackLeniency"); + + b.Property("StarDifficulty"); + + b.Property("Status"); + + b.Property("StoredBookmarks"); + + b.Property("TimelineZoom"); + + b.Property("Version"); + + b.Property("WidescreenStoryboard"); + + b.HasKey("ID"); + + b.HasIndex("BaseDifficultyID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("Hash"); + + b.HasIndex("MD5Hash"); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("BeatmapInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Artist"); + + b.Property("ArtistUnicode"); + + b.Property("AudioFile"); + + b.Property("AuthorID") + .HasColumnName("AuthorID"); + + b.Property("AuthorString") + .HasColumnName("Author"); + + b.Property("BackgroundFile"); + + b.Property("PreviewTime"); + + b.Property("Source"); + + b.Property("Tags"); + + b.Property("Title"); + + b.Property("TitleUnicode"); + + b.HasKey("ID"); + + b.ToTable("BeatmapMetadata"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BeatmapSetInfoID"); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("FileInfoID"); + + b.ToTable("BeatmapSetFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapSetID"); + + b.Property("Protected"); + + b.Property("Status"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapSetID") + .IsUnique(); + + b.ToTable("BeatmapSetInfo"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Key") + .HasColumnName("Key"); + + b.Property("RulesetID"); + + b.Property("SkinInfoID"); + + b.Property("StringValue") + .HasColumnName("Value"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("SkinInfoID"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("osu.Game.IO.FileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Hash"); + + b.Property("ReferenceCount"); + + b.HasKey("ID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("ReferenceCount"); + + b.ToTable("FileInfo"); + }); + + modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntAction") + .HasColumnName("Action"); + + b.Property("KeysString") + .HasColumnName("Keys"); + + b.Property("RulesetID"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("IntAction"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("KeyBinding"); + }); + + modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Available"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.Property("ShortName"); + + b.HasKey("ID"); + + b.HasIndex("Available"); + + b.HasIndex("ShortName") + .IsUnique(); + + b.ToTable("RulesetInfo"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("ScoreInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("ScoreInfoID"); + + b.ToTable("ScoreFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Accuracy") + .HasColumnType("DECIMAL(1,4)"); + + b.Property("BeatmapInfoID"); + + b.Property("Combo"); + + b.Property("Date"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MaxCombo"); + + b.Property("ModsJson") + .HasColumnName("Mods"); + + b.Property("OnlineScoreID"); + + b.Property("PP"); + + b.Property("Rank"); + + b.Property("RulesetID"); + + b.Property("StatisticsJson") + .HasColumnName("Statistics"); + + b.Property("TotalScore"); + + b.Property("UserID") + .HasColumnName("UserID"); + + b.Property("UserString") + .HasColumnName("User"); + + b.HasKey("ID"); + + b.HasIndex("BeatmapInfoID"); + + b.HasIndex("OnlineScoreID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("ScoreInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("SkinInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("SkinInfoID"); + + b.ToTable("SkinFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Creator"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.ToTable("SkinInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") + .WithMany("Beatmaps") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") + .WithMany("Files") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Settings") + .HasForeignKey("SkinInfoID"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Scoring.ScoreInfo") + .WithMany("Files") + .HasForeignKey("ScoreInfoID"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") + .WithMany("Scores") + .HasForeignKey("BeatmapInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Files") + .HasForeignKey("SkinInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/osu.Game/Migrations/20210912144011_AddSamplesMatchPlaybackRate.cs b/osu.Game/Migrations/20210912144011_AddSamplesMatchPlaybackRate.cs new file mode 100644 index 0000000000..bf3f855d5f --- /dev/null +++ b/osu.Game/Migrations/20210912144011_AddSamplesMatchPlaybackRate.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace osu.Game.Migrations +{ + public partial class AddSamplesMatchPlaybackRate : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "SamplesMatchPlaybackRate", + table: "BeatmapInfo", + nullable: false, + defaultValue: false); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "SamplesMatchPlaybackRate", + table: "BeatmapInfo"); + } + } +} diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs index 470907ada6..036c26cb0a 100644 --- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs +++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs @@ -81,6 +81,8 @@ namespace osu.Game.Migrations b.Property("RulesetID"); + b.Property("SamplesMatchPlaybackRate"); + b.Property("SpecialStyle"); b.Property("StackLeniency"); From cd181452be90a7fc96a888bfac732fca7e921615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 12 Sep 2021 16:45:27 +0200 Subject: [PATCH 1836/2442] Add decoding support for `SamplesMatchPlaybackRate` --- osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 1 + osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 8560a36fb4..a4bf8c92e3 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -64,6 +64,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.IsFalse(beatmapInfo.LetterboxInBreaks); Assert.IsFalse(beatmapInfo.SpecialStyle); Assert.IsFalse(beatmapInfo.WidescreenStoryboard); + Assert.IsFalse(beatmapInfo.SamplesMatchPlaybackRate); Assert.AreEqual(CountdownType.None, beatmapInfo.Countdown); Assert.AreEqual(0, beatmapInfo.CountdownOffset); } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index accefb2583..4b5eaafa4a 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -180,6 +180,10 @@ namespace osu.Game.Beatmaps.Formats beatmap.BeatmapInfo.EpilepsyWarning = Parsing.ParseInt(pair.Value) == 1; break; + case @"SamplesMatchPlaybackRate": + beatmap.BeatmapInfo.SamplesMatchPlaybackRate = Parsing.ParseInt(pair.Value) == 1; + break; + case @"Countdown": beatmap.BeatmapInfo.Countdown = (CountdownType)Enum.Parse(typeof(CountdownType), pair.Value); break; From af7c2b93e63b78d3a119ec8580432b02b7211765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 12 Sep 2021 16:47:38 +0200 Subject: [PATCH 1837/2442] Add encoding support for `SamplesMatchPlaybackRate` --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 75d9a56f3e..aef13b8872 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -105,8 +105,8 @@ namespace osu.Game.Beatmaps.Formats if (beatmap.BeatmapInfo.RulesetID == 3) writer.WriteLine(FormattableString.Invariant($"SpecialStyle: {(beatmap.BeatmapInfo.SpecialStyle ? '1' : '0')}")); writer.WriteLine(FormattableString.Invariant($"WidescreenStoryboard: {(beatmap.BeatmapInfo.WidescreenStoryboard ? '1' : '0')}")); - // if (b.SamplesMatchPlaybackRate) - // writer.WriteLine(@"SamplesMatchPlaybackRate: 1"); + if (beatmap.BeatmapInfo.SamplesMatchPlaybackRate) + writer.WriteLine(@"SamplesMatchPlaybackRate: 1"); } private void handleEditor(TextWriter writer) From 345cde251db4b35d0308083a37255dc7bff24c21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 12 Sep 2021 16:54:17 +0200 Subject: [PATCH 1838/2442] Add "samples match playback rate" to editor setup screen --- osu.Game/Screens/Edit/Setup/DesignSection.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game/Screens/Edit/Setup/DesignSection.cs b/osu.Game/Screens/Edit/Setup/DesignSection.cs index 90f95a668e..d5d93db050 100644 --- a/osu.Game/Screens/Edit/Setup/DesignSection.cs +++ b/osu.Game/Screens/Edit/Setup/DesignSection.cs @@ -25,6 +25,7 @@ namespace osu.Game.Screens.Edit.Setup private LabelledSwitchButton widescreenSupport; private LabelledSwitchButton epilepsyWarning; private LabelledSwitchButton letterboxDuringBreaks; + private LabelledSwitchButton samplesMatchPlaybackRate; public override LocalisableString Title => "Design"; @@ -79,6 +80,12 @@ namespace osu.Game.Screens.Edit.Setup Label = "Letterbox during breaks", Description = "Adds horizontal letterboxing to give a cinematic look during breaks.", Current = { Value = Beatmap.BeatmapInfo.LetterboxInBreaks } + }, + samplesMatchPlaybackRate = new LabelledSwitchButton + { + Label = "Samples match playback rate", + Description = "When enabled, all samples will speed up or slow down when rate-changing mods are enabled.", + Current = { Value = Beatmap.BeatmapInfo.SamplesMatchPlaybackRate } } }; } @@ -96,6 +103,7 @@ namespace osu.Game.Screens.Edit.Setup widescreenSupport.Current.BindValueChanged(_ => updateBeatmap()); epilepsyWarning.Current.BindValueChanged(_ => updateBeatmap()); letterboxDuringBreaks.Current.BindValueChanged(_ => updateBeatmap()); + samplesMatchPlaybackRate.Current.BindValueChanged(_ => updateBeatmap()); } private void updateCountdownSettingsVisibility() => CountdownSettings.FadeTo(EnableCountdown.Current.Value ? 1 : 0); @@ -115,6 +123,7 @@ namespace osu.Game.Screens.Edit.Setup Beatmap.BeatmapInfo.WidescreenStoryboard = widescreenSupport.Current.Value; Beatmap.BeatmapInfo.EpilepsyWarning = epilepsyWarning.Current.Value; Beatmap.BeatmapInfo.LetterboxInBreaks = letterboxDuringBreaks.Current.Value; + Beatmap.BeatmapInfo.SamplesMatchPlaybackRate = samplesMatchPlaybackRate.Current.Value; } } } From 1be8cb452f87afd29db37fec2b8b68e5fb296f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 12 Sep 2021 16:57:21 +0200 Subject: [PATCH 1839/2442] Make new beatmaps' samples match playback rate by default --- osu.Game/Beatmaps/BeatmapManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 27aa874dc9..bd85017d58 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -129,6 +129,7 @@ namespace osu.Game.Beatmaps Ruleset = ruleset, Metadata = metadata, WidescreenStoryboard = true, + SamplesMatchPlaybackRate = true, } } }; From fdd48c3e71cf997e3acfe626a9f0ac40f7281d61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 12 Sep 2021 17:41:50 +0200 Subject: [PATCH 1840/2442] Refactor note colouring test scene --- .../TestSceneTimingBasedNoteColouring.cs | 58 +++++++++++-------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneTimingBasedNoteColouring.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneTimingBasedNoteColouring.cs index e14ad92842..8405b14e1f 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneTimingBasedNoteColouring.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneTimingBasedNoteColouring.cs @@ -13,6 +13,7 @@ using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Configuration; using osu.Framework.Bindables; +using osu.Framework.Testing; namespace osu.Game.Rulesets.Mania.Tests { @@ -22,14 +23,42 @@ namespace osu.Game.Rulesets.Mania.Tests [Resolved] private RulesetConfigCache configCache { get; set; } - private readonly Bindable configTimingBasedNoteColouring = new Bindable(); + private Bindable configTimingBasedNoteColouring; - protected override void LoadComplete() + private ManualClock clock; + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("setup hierarchy", () => Child = new Container + { + Clock = new FramedClock(clock = new ManualClock()), + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Children = new[] + { + Ruleset.Value.CreateInstance().CreateDrawableRulesetWith(createTestBeatmap()) + } + }); + AddStep("retrieve config bindable", () => + { + var config = (ManiaRulesetConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance()); + configTimingBasedNoteColouring = config.GetBindable(ManiaRulesetSetting.TimingBasedNoteColouring); + }); + } + + [Test] + public void TestSimple() + { + AddStep("enable", () => configTimingBasedNoteColouring.Value = true); + AddStep("disable", () => configTimingBasedNoteColouring.Value = false); + } + + private ManiaBeatmap createTestBeatmap() { const double beat_length = 500; - var ruleset = new ManiaRuleset(); - var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 }) { HitObjects = @@ -45,7 +74,7 @@ namespace osu.Game.Rulesets.Mania.Tests new Note { StartTime = beat_length } }, ControlPointInfo = new ControlPointInfo(), - BeatmapInfo = { Ruleset = ruleset.RulesetInfo }, + BeatmapInfo = { Ruleset = Ruleset.Value }, }; foreach (var note in beatmap.HitObjects) @@ -57,24 +86,7 @@ namespace osu.Game.Rulesets.Mania.Tests { BeatLength = beat_length }); - - Child = new Container - { - Clock = new FramedClock(new ManualClock()), - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Children = new[] - { - ruleset.CreateDrawableRulesetWith(beatmap) - } - }; - - var config = (ManiaRulesetConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance()); - config.BindWith(ManiaRulesetSetting.TimingBasedNoteColouring, configTimingBasedNoteColouring); - - AddStep("Enable", () => configTimingBasedNoteColouring.Value = true); - AddStep("Disable", () => configTimingBasedNoteColouring.Value = false); + return beatmap; } } } From b05963cc503d9474fa77bcb50cb35c0115e6c550 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Sun, 12 Sep 2021 16:08:17 +0000 Subject: [PATCH 1841/2442] update to fix review issues --- .../Difficulty/Skills/Speed.cs | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 21477a09da..1f708737bf 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -6,6 +6,7 @@ using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Objects; +using osu.Framework.Utils; namespace osu.Game.Rulesets.Osu.Difficulty.Skills { @@ -44,11 +45,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { } - private bool isRatioEqual(double ratio, double a, double b) - { - return a + 15 > ratio * b && a - 15 < ratio * b; - } - /// /// Calculates a rhythm multiplier for the difficulty of the tap associated with historic data of the current . /// @@ -65,15 +61,19 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills for (int i = Previous.Count - 2; i > 0; i--) { - double currHistoricalDecay = Math.Max(0, (history_time_max - (current.StartTime - Previous[i - 1].StartTime))) / history_time_max; // scales note 0 to 1 from history to now + DifficultyHitObject currObj = Previous[i - 1]; + DifficultyHitObject prevObj = Previous[i]; + DifficultyHitObject prevPrevObj = Previous[i + 1]; + + double currHistoricalDecay = Math.Max(0, (history_time_max - (current.StartTime - currObj.StartTime))) / history_time_max; // scales note 0 to 1 from history to now if (currHistoricalDecay != 0) { currHistoricalDecay = Math.Min(currHistoricalDecay, (double)(Previous.Count - i) / Previous.Count); // either we're limited by time or limited by object count. - double currDelta = ((OsuDifficultyHitObject)Previous[i - 1]).StrainTime; - double prevDelta = ((OsuDifficultyHitObject)Previous[i]).StrainTime; - double prevPrevDelta = ((OsuDifficultyHitObject)Previous[i + 1]).StrainTime; + double currDelta = ((OsuDifficultyHitObject)currObj).StrainTime; + double prevDelta = ((OsuDifficultyHitObject)prevObj).StrainTime; + double prevPrevDelta = ((OsuDifficultyHitObject)prevPrevObj).StrainTime; double effectiveRatio = Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta); if (effectiveRatio > 0.5) @@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills if (firstDeltaSwitch) { - if (isRatioEqual(1.0, prevDelta, currDelta)) + if (Precision.AlmostEquals(prevDelta, currDelta, 15)) { islandSize++; // island is still progressing, count size. } @@ -174,10 +174,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills currentRhythm = calculateRhythmBonus(current); - currentTapStrain *= strainDecay(current.DeltaTime); + double decay = strainDecay(current.DeltaTime); + + currentTapStrain *= decay; currentTapStrain += tapStrainOf(current, speedBonus) * skillMultiplier; - currentMovementStrain *= strainDecay(current.DeltaTime); + currentMovementStrain *= decay; currentMovementStrain += movementStrainOf(current, speedBonus) * skillMultiplier; return currentMovementStrain + currentTapStrain * currentRhythm; From 1edf608260c350ffe3164ea99be484ef15609fbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 12 Sep 2021 17:48:08 +0200 Subject: [PATCH 1842/2442] Add failing test case --- .../TestSceneTimingBasedNoteColouring.cs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneTimingBasedNoteColouring.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneTimingBasedNoteColouring.cs index 8405b14e1f..449a6ff23d 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneTimingBasedNoteColouring.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneTimingBasedNoteColouring.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Allocation; @@ -14,6 +15,9 @@ using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Configuration; using osu.Framework.Bindables; using osu.Framework.Testing; +using osu.Framework.Utils; +using osu.Game.Rulesets.Mania.Objects.Drawables; +using osu.Game.Rulesets.Mania.UI; namespace osu.Game.Rulesets.Mania.Tests { @@ -26,6 +30,7 @@ namespace osu.Game.Rulesets.Mania.Tests private Bindable configTimingBasedNoteColouring; private ManualClock clock; + private DrawableManiaRuleset drawableRuleset; [SetUpSteps] public void SetUpSteps() @@ -38,7 +43,7 @@ namespace osu.Game.Rulesets.Mania.Tests Origin = Anchor.Centre, Children = new[] { - Ruleset.Value.CreateInstance().CreateDrawableRulesetWith(createTestBeatmap()) + drawableRuleset = (DrawableManiaRuleset)Ruleset.Value.CreateInstance().CreateDrawableRulesetWith(createTestBeatmap()) } }); AddStep("retrieve config bindable", () => @@ -55,6 +60,28 @@ namespace osu.Game.Rulesets.Mania.Tests AddStep("disable", () => configTimingBasedNoteColouring.Value = false); } + [Test] + public void TestToggleOffScreen() + { + AddStep("enable", () => configTimingBasedNoteColouring.Value = true); + + seekTo(10000); + AddStep("disable", () => configTimingBasedNoteColouring.Value = false); + seekTo(0); + AddAssert("all notes not coloured", () => this.ChildrenOfType().All(note => note.Colour == Colour4.White)); + + seekTo(10000); + AddStep("enable again", () => configTimingBasedNoteColouring.Value = true); + seekTo(0); + AddAssert("some notes coloured", () => this.ChildrenOfType().Any(note => note.Colour != Colour4.White)); + } + + private void seekTo(double time) + { + AddStep($"seek to {time}", () => clock.CurrentTime = time); + AddUntilStep("wait for seek", () => Precision.AlmostEquals(drawableRuleset.FrameStableClock.CurrentTime, time, 1)); + } + private ManiaBeatmap createTestBeatmap() { const double beat_length = 500; From 922fa96d411d6119a24020243785d8d912d8ea1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 12 Sep 2021 17:51:15 +0200 Subject: [PATCH 1843/2442] Fix notes not updating snap colour on application --- osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index 33d872dfb6..d53c28868d 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -66,6 +66,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables StartTimeBindable.BindValueChanged(_ => updateSnapColour(), true); } + protected override void OnApply() + { + base.OnApply(); + updateSnapColour(); + } + protected override void OnDirectionChanged(ValueChangedEvent e) { base.OnDirectionChanged(e); From 44163dc9ec1ae5b2cc43c7ebbdde55670bf4f2df Mon Sep 17 00:00:00 2001 From: Xexxar Date: Sun, 12 Sep 2021 18:14:05 +0000 Subject: [PATCH 1844/2442] updated to use deltaTime not stainTime for more accuracy --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 1f708737bf..7b99ceac6f 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -71,8 +71,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { currHistoricalDecay = Math.Min(currHistoricalDecay, (double)(Previous.Count - i) / Previous.Count); // either we're limited by time or limited by object count. - double currDelta = ((OsuDifficultyHitObject)currObj).StrainTime; - double prevDelta = ((OsuDifficultyHitObject)prevObj).StrainTime; + double currDelta = Math.Max(25, currObj.DeltaTime); + double prevDelta = Math.Max(25, prevObj.DeltaTime); double prevPrevDelta = ((OsuDifficultyHitObject)prevPrevObj).StrainTime; double effectiveRatio = Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta); From 447001931cf67b3896c3934d1c23209b2be3ddc7 Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Sun, 12 Sep 2021 14:36:11 -0400 Subject: [PATCH 1845/2442] Resolve LoungeSubScreen from PasswordEntryPopover This is preferred over passing down the already-resolved LoungeSubScreen --- osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index db1566999e..bec0ea419c 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -122,7 +122,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge } } - public Popover GetPopover() => new PasswordEntryPopover(Room) { Lounge = lounge }; + public Popover GetPopover() => new PasswordEntryPopover(Room); public MenuItem[] ContextMenuItems => new MenuItem[] { @@ -178,7 +178,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { private readonly Room room; - public LoungeSubScreen Lounge; + [Resolved(canBeNull: true)] + private LoungeSubScreen lounge { get; set; } public PasswordEntryPopover(Room room) { @@ -219,7 +220,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge }; Child = shakeContainer; - joinButton.Action = () => Lounge?.Join(room, passwordTextbox.Text, null, joinFailed); + joinButton.Action = () => lounge?.Join(room, passwordTextbox.Text, null, joinFailed); } private void joinFailed(string error) @@ -234,7 +235,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge base.LoadComplete(); Schedule(() => GetContainingInputManager().ChangeFocus(passwordTextbox)); - passwordTextbox.OnCommit += (_, __) => Lounge?.Join(room, passwordTextbox.Text, null, joinFailed); + passwordTextbox.OnCommit += (_, __) => lounge?.Join(room, passwordTextbox.Text, null, joinFailed); } } } From 5969e2b8529e7917f86770bfe1d4e692e6d58751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 13 Sep 2021 00:13:07 +0200 Subject: [PATCH 1846/2442] Add TODO comment about lack of in-gameplay support --- osu.Game/Beatmaps/BeatmapInfo.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 7dd1dd2cf4..8cb5da8083 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -95,6 +95,7 @@ namespace osu.Game.Beatmaps /// /// Whether or not sound samples should change rate when playing with speed-changing mods. + /// TODO: only read/write supported for now, requires implementation in gameplay. /// public bool SamplesMatchPlaybackRate { get; set; } From 7fe0eefb789a52d47a19286db53dacc4a2b2eaaa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Sep 2021 14:12:18 +0900 Subject: [PATCH 1847/2442] Add inline comment regarding team switch sample logic Feels a bit convoluted without this. Don't really have a better suggestion for now so a comment will do. --- .../Screens/OnlinePlay/Multiplayer/Participants/TeamDisplay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/TeamDisplay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/TeamDisplay.cs index 915cf30963..833fbd6605 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/TeamDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/TeamDisplay.cs @@ -105,6 +105,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants if (newTeam == displayedTeam) return; + // only play the sample if an already valid team changes to another valid team. + // this avoids playing a sound for each user if the match type is changed to/from a team mode. if (newTeam != null && displayedTeam != null) sampleTeamSwap?.Play(); From 684c39dad04a87d0cbaa7cbb341b9bb5133fec9f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Sep 2021 14:12:37 +0900 Subject: [PATCH 1848/2442] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 7378450c38..d4331a5e65 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index d80dd075ee..941656bb70 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 8ce757974e..73e0030114 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From 52c69d2f2290815ee5406079423849ed6eaf6961 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Sep 2021 14:17:45 +0900 Subject: [PATCH 1849/2442] Adjust value to not be full width (but allow for more accommodations with localised versions) --- .../Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs index 2e49671669..fae0318359 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs @@ -67,7 +67,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input { Text = InputSettingsStrings.ResetSectionButton; RelativeSizeAxes = Axes.X; - Width = Content.Width; + Width = 0.8f; Anchor = Anchor.TopCentre; Origin = Anchor.TopCentre; Margin = new MarginPadding { Top = 15 }; From caf7ef6519bb928f44769fbddbb862aa1fd40809 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Sep 2021 15:00:33 +0900 Subject: [PATCH 1850/2442] Add missing screen level mod application settings for some screens Closes #7480. But based on discussion in there this solution may change. --- .../Settings/Sections/Maintenance/DirectorySelectScreen.cs | 2 ++ osu.Game/Screens/Import/FileImportScreen.cs | 2 ++ osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs | 2 ++ 3 files changed, 6 insertions(+) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs index e509cac2f1..1d67968ab1 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs @@ -24,6 +24,8 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance private OsuDirectorySelector directorySelector; + public override bool AllowTrackAdjustments => false; + /// /// Text to display in the header to inform the user of what they are selecting. /// diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index 7e1d55b3e2..606174193d 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -23,6 +23,8 @@ namespace osu.Game.Screens.Import { public override bool HideOverlaysOnEnter => true; + public override bool AllowTrackAdjustments => false; + private OsuFileSelector fileSelector; private Container contentContainer; private TextFlowContainer currentFileText; diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index fc20b21b60..62bfd2cfed 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -24,6 +24,8 @@ namespace osu.Game.Screens.OnlinePlay [Cached] protected readonly OverlayColourProvider ColourProvider = new OverlayColourProvider(OverlayColourScheme.Plum); + public override bool AllowTrackAdjustments => false; + public override bool CursorVisible => (screenStack?.CurrentScreen as IOnlinePlaySubScreen)?.CursorVisible ?? true; // this is required due to PlayerLoader eventually being pushed to the main stack From 0dc31a476f9ccf0eafdf7c1f16fe96da287977bb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 13 Sep 2021 16:39:05 +0900 Subject: [PATCH 1851/2442] Invert condition to reduce nesting --- .../Difficulty/OsuPerformanceCalculator.cs | 46 +++++++++---------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index ad7376a044..f9a3423eab 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -190,38 +190,36 @@ namespace osu.Game.Rulesets.Osu.Difficulty private double computeFlashlightValue() { - double flashlightValue = 0.0; + if (!mods.Any(h => h is OsuModFlashlight)) + return 0.0; - if (mods.Any(h => h is OsuModFlashlight)) - { - double rawFlashlight = Attributes.FlashlightStrain; + double rawFlashlight = Attributes.FlashlightStrain; - if (mods.Any(m => m is OsuModTouchDevice)) - rawFlashlight = Math.Pow(rawFlashlight, 0.8); + if (mods.Any(m => m is OsuModTouchDevice)) + rawFlashlight = Math.Pow(rawFlashlight, 0.8); - flashlightValue = Math.Pow(rawFlashlight, 2.0) * 25.0; + double flashlightValue = Math.Pow(rawFlashlight, 2.0) * 25.0; - // Add an additional bonus for HDFL. - if (mods.Any(h => h is OsuModHidden)) - flashlightValue *= 1.3; + // Add an additional bonus for HDFL. + if (mods.Any(h => h is OsuModHidden)) + flashlightValue *= 1.3; - // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. - if (countMiss > 0) - flashlightValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), Math.Pow(countMiss, .875)); + // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. + if (countMiss > 0) + flashlightValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), Math.Pow(countMiss, .875)); - // Combo scaling. - if (Attributes.MaxCombo > 0) - flashlightValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); + // Combo scaling. + if (Attributes.MaxCombo > 0) + flashlightValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); - // Account for shorter maps having a higher ratio of 0 combo/100 combo flashlight radius. - flashlightValue *= 0.7 + 0.1 * Math.Min(1.0, totalHits / 200.0) + - (totalHits > 200 ? 0.2 * Math.Min(1.0, (totalHits - 200) / 200.0) : 0.0); + // Account for shorter maps having a higher ratio of 0 combo/100 combo flashlight radius. + flashlightValue *= 0.7 + 0.1 * Math.Min(1.0, totalHits / 200.0) + + (totalHits > 200 ? 0.2 * Math.Min(1.0, (totalHits - 200) / 200.0) : 0.0); - // Scale the flashlight value with accuracy _slightly_. - flashlightValue *= 0.5 + accuracy / 2.0; - // It is important to also consider accuracy difficulty when doing that. - flashlightValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500; - } + // Scale the flashlight value with accuracy _slightly_. + flashlightValue *= 0.5 + accuracy / 2.0; + // It is important to also consider accuracy difficulty when doing that. + flashlightValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500; return flashlightValue; } From aa71e3f3d4e6206798a563c3e4af1291e6626323 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Sep 2021 18:32:48 +0900 Subject: [PATCH 1852/2442] Update nested game tests in line with framework changes --- .../TestSceneOsuGame.cs | 6 ++---- .../osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs | 6 ++---- .../TestSceneOsuGame.cs | 6 ++---- .../osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs | 6 ++---- osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs | 6 ++---- osu.Game/Tests/Visual/OsuGameTestScene.cs | 5 +---- 6 files changed, 11 insertions(+), 24 deletions(-) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/TestSceneOsuGame.cs b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/TestSceneOsuGame.cs index 9c512a01ea..536fdfc6df 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/TestSceneOsuGame.cs +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/TestSceneOsuGame.cs @@ -15,9 +15,6 @@ namespace osu.Game.Rulesets.EmptyFreeform.Tests [BackgroundDependencyLoader] private void load(GameHost host, OsuGameBase gameBase) { - OsuGame game = new OsuGame(); - game.SetHost(host); - Children = new Drawable[] { new Box @@ -25,8 +22,9 @@ namespace osu.Game.Rulesets.EmptyFreeform.Tests RelativeSizeAxes = Axes.Both, Colour = Color4.Black, }, - game }; + + AddGame(new OsuGame()); } } } diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs index 270d906b01..3cdf44e6f1 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs @@ -15,9 +15,6 @@ namespace osu.Game.Rulesets.Pippidon.Tests [BackgroundDependencyLoader] private void load(GameHost host, OsuGameBase gameBase) { - OsuGame game = new OsuGame(); - game.SetHost(host); - Children = new Drawable[] { new Box @@ -25,8 +22,9 @@ namespace osu.Game.Rulesets.Pippidon.Tests RelativeSizeAxes = Axes.Both, Colour = Color4.Black, }, - game }; + + AddGame(new OsuGame()); } } } diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/TestSceneOsuGame.cs b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/TestSceneOsuGame.cs index aed6abb6bf..4d3f5086d9 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/TestSceneOsuGame.cs +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/TestSceneOsuGame.cs @@ -15,9 +15,6 @@ namespace osu.Game.Rulesets.EmptyScrolling.Tests [BackgroundDependencyLoader] private void load(GameHost host, OsuGameBase gameBase) { - OsuGame game = new OsuGame(); - game.SetHost(host); - Children = new Drawable[] { new Box @@ -25,8 +22,9 @@ namespace osu.Game.Rulesets.EmptyScrolling.Tests RelativeSizeAxes = Axes.Both, Colour = Color4.Black, }, - game }; + + AddGame(new OsuGame()); } } } diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs index 270d906b01..3cdf44e6f1 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/TestSceneOsuGame.cs @@ -15,9 +15,6 @@ namespace osu.Game.Rulesets.Pippidon.Tests [BackgroundDependencyLoader] private void load(GameHost host, OsuGameBase gameBase) { - OsuGame game = new OsuGame(); - game.SetHost(host); - Children = new Drawable[] { new Box @@ -25,8 +22,9 @@ namespace osu.Game.Rulesets.Pippidon.Tests RelativeSizeAxes = Axes.Both, Colour = Color4.Black, }, - game }; + + AddGame(new OsuGame()); } } } diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs index b8232837b5..e38b1353d9 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs @@ -97,9 +97,6 @@ namespace osu.Game.Tests.Visual.Navigation { AddStep("create game", () => { - game = new OsuGame(); - game.SetHost(host); - Children = new Drawable[] { new Box @@ -107,8 +104,9 @@ namespace osu.Game.Tests.Visual.Navigation RelativeSizeAxes = Axes.Both, Colour = Color4.Black, }, - game }; + + AddGame(game = new OsuGame()); }); AddUntilStep("wait for load", () => game.IsLoaded); diff --git a/osu.Game/Tests/Visual/OsuGameTestScene.cs b/osu.Game/Tests/Visual/OsuGameTestScene.cs index f38aaa9358..b56c8a15af 100644 --- a/osu.Game/Tests/Visual/OsuGameTestScene.cs +++ b/osu.Game/Tests/Visual/OsuGameTestScene.cs @@ -81,10 +81,7 @@ namespace osu.Game.Tests.Visual protected void CreateGame() { - Game = new TestOsuGame(LocalStorage, API); - Game.SetHost(host); - - Add(Game); + AddGame(Game = new TestOsuGame(LocalStorage, API)); } protected void PushAndConfirm(Func newScreen) From 24ae530a8087d35119a9b4533571cf4e6dbf9112 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Sep 2021 18:32:58 +0900 Subject: [PATCH 1853/2442] Add test coverage of double dispose of `OsuGame` --- osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index cc64d37116..0d1719c1d2 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -400,6 +400,7 @@ namespace osu.Game.Tests.Visual.Navigation AddStep("Hold escape", () => InputManager.PressKey(Key.Escape)); AddUntilStep("Wait for intro", () => Game.ScreenStack.CurrentScreen is IntroTriangles); AddUntilStep("Wait for game exit", () => Game.ScreenStack.CurrentScreen == null); + AddStep("test dispose doesn't crash", () => Game.Dispose()); } private void pushEscape() => From 0f5ed81a7a51467b1c10556856bc602ecda0622a Mon Sep 17 00:00:00 2001 From: AbstractQbit <38468635+AbstractQbit@users.noreply.github.com> Date: Mon, 13 Sep 2021 13:31:13 +0300 Subject: [PATCH 1854/2442] Fix dimmed checked nub artifact This adds transition that extends nub's border to fill it. Fill fade can be removed, but combined effect looks nicer imo, and the fill is still needed because if removed, border becomes invisible for some reason. --- osu.Game/Graphics/UserInterface/Nub.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Graphics/UserInterface/Nub.cs b/osu.Game/Graphics/UserInterface/Nub.cs index 664f32b083..4e5c78be77 100644 --- a/osu.Game/Graphics/UserInterface/Nub.cs +++ b/osu.Game/Graphics/UserInterface/Nub.cs @@ -47,6 +47,7 @@ namespace osu.Game.Graphics.UserInterface }; Current.ValueChanged += filled => fill.FadeTo(filled.NewValue ? 1 : 0, 200, Easing.OutQuint); + Current.ValueChanged += filled => this.TransformTo(nameof(BorderThickness), filled.NewValue ? 7 : border_width, 200, Easing.OutQuint); } [BackgroundDependencyLoader] From d999c29d3ac873f6991cf19030d9e74dc8bc0b49 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Sep 2021 19:38:43 +0900 Subject: [PATCH 1855/2442] Remove unused `GameHost` DI in `OsuGameTestScene` --- osu.Game/Tests/Visual/OsuGameTestScene.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game/Tests/Visual/OsuGameTestScene.cs b/osu.Game/Tests/Visual/OsuGameTestScene.cs index b56c8a15af..881c4bab02 100644 --- a/osu.Game/Tests/Visual/OsuGameTestScene.cs +++ b/osu.Game/Tests/Visual/OsuGameTestScene.cs @@ -30,8 +30,6 @@ namespace osu.Game.Tests.Visual /// public abstract class OsuGameTestScene : OsuManualInputManagerTestScene { - private GameHost host; - protected TestOsuGame Game; protected override bool UseFreshStoragePerRun => true; @@ -39,10 +37,8 @@ namespace osu.Game.Tests.Visual protected override bool CreateNestedActionContainer => false; [BackgroundDependencyLoader] - private void load(GameHost host) + private void load() { - this.host = host; - Child = new Box { RelativeSizeAxes = Axes.Both, @@ -55,7 +51,7 @@ namespace osu.Game.Tests.Visual { AddStep("Create new game instance", () => { - if (Game != null) + if (Game?.Parent != null) { Remove(Game); Game.Dispose(); From e8d4e2e6da97c2a7b7dc41f78705bc6ad8a72894 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Sep 2021 19:38:53 +0900 Subject: [PATCH 1856/2442] Fix tests being blocked by notification overlay popup --- .../Navigation/TestSceneScreenNavigation.cs | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 0d1719c1d2..2c416ee758 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -77,7 +77,13 @@ namespace osu.Game.Tests.Visual.Navigation AddStep("press enter", () => InputManager.Key(Key.Enter)); - AddUntilStep("wait for player", () => (player = Game.ScreenStack.CurrentScreen as Player) != null); + AddUntilStep("wait for player", () => + { + // dismiss any notifications that may appear (ie. muted notification). + clickMouseInCentre(); + return (player = Game.ScreenStack.CurrentScreen as Player) != null; + }); + AddAssert("retry count is 0", () => player.RestartCount == 0); AddStep("attempt to retry", () => player.ChildrenOfType().First().Action()); @@ -104,7 +110,14 @@ namespace osu.Game.Tests.Visual.Navigation AddStep("set mods", () => Game.SelectedMods.Value = new Mod[] { new OsuModNoFail(), new OsuModDoubleTime { SpeedChange = { Value = 2 } } }); AddStep("press enter", () => InputManager.Key(Key.Enter)); - AddUntilStep("wait for player", () => (player = Game.ScreenStack.CurrentScreen as Player) != null); + + AddUntilStep("wait for player", () => + { + // dismiss any notifications that may appear (ie. muted notification). + clickMouseInCentre(); + return (player = Game.ScreenStack.CurrentScreen as Player) != null; + }); + AddUntilStep("wait for track playing", () => beatmap().Track.IsRunning); AddStep("seek to near end", () => player.ChildrenOfType().First().Seek(beatmap().Beatmap.HitObjects[^1].StartTime - 1000)); AddUntilStep("wait for pass", () => (results = Game.ScreenStack.CurrentScreen as ResultsScreen) != null && results.IsLoaded); @@ -131,7 +144,13 @@ namespace osu.Game.Tests.Visual.Navigation AddStep("press enter", () => InputManager.Key(Key.Enter)); - AddUntilStep("wait for player", () => (player = Game.ScreenStack.CurrentScreen as Player) != null); + AddUntilStep("wait for player", () => + { + // dismiss any notifications that may appear (ie. muted notification). + clickMouseInCentre(); + return (player = Game.ScreenStack.CurrentScreen as Player) != null; + }); + AddUntilStep("wait for fail", () => player.HasFailed); AddUntilStep("wait for track stop", () => !Game.MusicController.IsPlaying); @@ -403,6 +422,12 @@ namespace osu.Game.Tests.Visual.Navigation AddStep("test dispose doesn't crash", () => Game.Dispose()); } + private void clickMouseInCentre() + { + InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.Centre); + InputManager.Click(MouseButton.Left); + } + private void pushEscape() => AddStep("Press escape", () => InputManager.Key(Key.Escape)); From 4d2373ffb9e8216394936b31a92b9fd72c7788e0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Sep 2021 20:08:46 +0900 Subject: [PATCH 1857/2442] Combine similar value changed calls --- osu.Game/Graphics/UserInterface/Nub.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/Nub.cs b/osu.Game/Graphics/UserInterface/Nub.cs index 4e5c78be77..bd3b2e1715 100644 --- a/osu.Game/Graphics/UserInterface/Nub.cs +++ b/osu.Game/Graphics/UserInterface/Nub.cs @@ -46,8 +46,11 @@ namespace osu.Game.Graphics.UserInterface }, }; - Current.ValueChanged += filled => fill.FadeTo(filled.NewValue ? 1 : 0, 200, Easing.OutQuint); - Current.ValueChanged += filled => this.TransformTo(nameof(BorderThickness), filled.NewValue ? 7 : border_width, 200, Easing.OutQuint); + Current.ValueChanged += filled => + { + fill.FadeTo(filled.NewValue ? 1 : 0, 200, Easing.OutQuint); + this.TransformTo(nameof(BorderThickness), filled.NewValue ? 7 : border_width, 200, Easing.OutQuint); + }; } [BackgroundDependencyLoader] From 9c1fc2ec6536e5b1ac363d8717e224560609f370 Mon Sep 17 00:00:00 2001 From: AbstractQbit <38468635+AbstractQbit@users.noreply.github.com> Date: Mon, 13 Sep 2021 14:19:33 +0300 Subject: [PATCH 1858/2442] Tweak filled nub border width value --- osu.Game/Graphics/UserInterface/Nub.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/Nub.cs b/osu.Game/Graphics/UserInterface/Nub.cs index bd3b2e1715..6807d007bb 100644 --- a/osu.Game/Graphics/UserInterface/Nub.cs +++ b/osu.Game/Graphics/UserInterface/Nub.cs @@ -49,7 +49,7 @@ namespace osu.Game.Graphics.UserInterface Current.ValueChanged += filled => { fill.FadeTo(filled.NewValue ? 1 : 0, 200, Easing.OutQuint); - this.TransformTo(nameof(BorderThickness), filled.NewValue ? 7 : border_width, 200, Easing.OutQuint); + this.TransformTo(nameof(BorderThickness), filled.NewValue ? 8.5f : border_width, 200, Easing.OutQuint); }; } From e9f7258f2b3110d4845699602e6dbdab49713895 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Mon, 13 Sep 2021 14:50:40 +0100 Subject: [PATCH 1859/2442] adjust hitwindow nerf to be harsher --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index e69f15188d..2640d4ac41 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -60,8 +60,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } // Cap deltatime to the OD 300 hitwindow. - // 0.77 is derived from making sure 260bpm OD8 streams aren't nerfed harshly - var hitWindowNerf = deltaTime / (greatWindow * 2 * 0.77); + // 0.93 is derived from making sure 260bpm OD8 streams aren't nerfed harshly + var hitWindowNerf = deltaTime / (greatWindow * 2 * 0.93); deltaTime /= Math.Clamp(hitWindowNerf, 0.92, 1); double speedBonus = 1.0; From 7267602b951db6c708d379331bf7173528615c24 Mon Sep 17 00:00:00 2001 From: AbstractQbit <38468635+AbstractQbit@users.noreply.github.com> Date: Mon, 13 Sep 2021 17:14:39 +0300 Subject: [PATCH 1860/2442] Fix icon orientation for horizontal bar hit error meter --- .../HUD/HitErrorMeters/BarHitErrorMeter.cs | 20 +++++++++++++------ osu.Game/Skinning/LegacySkin.cs | 1 + 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 89f61785e8..a557e0acd7 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -33,6 +33,8 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters private const float chevron_size = 8; private SpriteIcon arrow; + private SpriteIcon iconEarly; + private SpriteIcon iconLate; private Container colourBarsEarly; private Container colourBarsLate; @@ -97,25 +99,21 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters RelativeSizeAxes = Axes.Both, Height = 0.5f, }, - new SpriteIcon + iconEarly = new SpriteIcon { Y = -10, Size = new Vector2(10), Icon = FontAwesome.Solid.ShippingFast, Anchor = Anchor.TopCentre, Origin = Anchor.Centre, - // undo any layout rotation to display the icon the correct orientation - Rotation = -Rotation, }, - new SpriteIcon + iconLate = new SpriteIcon { Y = 10, Size = new Vector2(10), Icon = FontAwesome.Solid.Bicycle, Anchor = Anchor.BottomCentre, Origin = Anchor.Centre, - // undo any layout rotation to display the icon the correct orientation - Rotation = -Rotation, } } }, @@ -130,6 +128,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters }; createColourBars(colours); + OrientIcons(); } protected override void LoadComplete() @@ -143,6 +142,15 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters arrow.Delay(200).FadeInFromZero(600); } + /// + /// Method to undo any layout rotation to display icons in the correct orientation. + /// + public void OrientIcons() + { + iconEarly.Rotation = -Rotation; + iconLate.Rotation = -Rotation; + } + private void createColourBars(OsuColour colours) { var windows = HitWindows.GetAllAvailableWindows().ToArray(); diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index b09620411b..41f5b7ae46 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -376,6 +376,7 @@ namespace osu.Game.Skinning hitError.Anchor = Anchor.BottomCentre; hitError.Origin = Anchor.CentreLeft; hitError.Rotation = -90; + if (hitError is BarHitErrorMeter barHitError) barHitError.OrientIcons(); } if (songProgress != null) From 3c75094f4350181f3e1e9a5dc070231a94906280 Mon Sep 17 00:00:00 2001 From: AbstractQbit <38468635+AbstractQbit@users.noreply.github.com> Date: Mon, 13 Sep 2021 19:41:55 +0300 Subject: [PATCH 1861/2442] Move `BarHitErrorMeter`'s icon reorintation to `Update()` --- .../Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs | 9 ++++----- osu.Game/Skinning/LegacySkin.cs | 1 - 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index a557e0acd7..7562df5a3b 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -128,7 +128,6 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters }; createColourBars(colours); - OrientIcons(); } protected override void LoadComplete() @@ -142,11 +141,11 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters arrow.Delay(200).FadeInFromZero(600); } - /// - /// Method to undo any layout rotation to display icons in the correct orientation. - /// - public void OrientIcons() + protected override void Update() { + base.Update(); + + // undo any layout rotation to display icons in the correct orientation iconEarly.Rotation = -Rotation; iconLate.Rotation = -Rotation; } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 41f5b7ae46..b09620411b 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -376,7 +376,6 @@ namespace osu.Game.Skinning hitError.Anchor = Anchor.BottomCentre; hitError.Origin = Anchor.CentreLeft; hitError.Rotation = -90; - if (hitError is BarHitErrorMeter barHitError) barHitError.OrientIcons(); } if (songProgress != null) From 80e54d51f2bfe0e367bdd7f65f4a793185bafcd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 11 Sep 2021 16:28:46 +0200 Subject: [PATCH 1862/2442] Add failing test for preserving editor clock time --- .../Editing/TestSceneDifficultySwitching.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs index a439555fde..0954bf1b49 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs @@ -55,6 +55,22 @@ namespace osu.Game.Tests.Visual.Editing AddAssert("stack empty", () => Stack.CurrentScreen == null); } + [Test] + public void TestClockPositionPreservedBetweenSwitches() + { + BeatmapInfo targetDifficulty = null; + AddStep("seek editor to 00:05:00", () => EditorClock.Seek(5000)); + + AddStep("set target difficulty", () => targetDifficulty = importedBeatmapSet.Beatmaps.Last(beatmap => !beatmap.Equals(Beatmap.Value.BeatmapInfo))); + switchToDifficulty(() => targetDifficulty); + confirmEditingBeatmap(() => targetDifficulty); + AddAssert("editor clock at 00:05:00", () => EditorClock.CurrentTime == 5000); + + AddStep("exit editor", () => Stack.Exit()); + // ensure editor loader didn't resume. + AddAssert("stack empty", () => Stack.CurrentScreen == null); + } + [Test] public void TestPreventSwitchDueToUnsavedChanges() { @@ -118,7 +134,7 @@ namespace osu.Game.Tests.Visual.Editing private void confirmEditingBeatmap(Func targetDifficulty) { AddUntilStep("current beatmap is correct", () => Beatmap.Value.BeatmapInfo.Equals(targetDifficulty.Invoke())); - AddUntilStep("current screen is editor", () => Stack.CurrentScreen is Editor); + AddUntilStep("current screen is editor", () => Stack.CurrentScreen == Editor && Editor?.IsLoaded == true); } } } From 3fc72271f103918a622d509d86a0fbad01172685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 11 Sep 2021 18:36:57 +0200 Subject: [PATCH 1863/2442] Restore editor clock time after difficulty switch --- osu.Game/Screens/Edit/Editor.cs | 13 ++++++++++++- osu.Game/Screens/Edit/EditorLoader.cs | 12 ++++++++++-- osu.Game/Screens/Edit/EditorState.cs | 19 +++++++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 osu.Game/Screens/Edit/EditorState.cs diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 28ae7e620e..502ac7a70f 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -476,6 +476,8 @@ namespace osu.Game.Screens.Edit }); resetTrack(true); + if (loader?.State != null) + restoreState(loader.State); } public override bool OnExiting(IScreen next) @@ -740,7 +742,16 @@ namespace osu.Game.Screens.Edit return new DifficultyMenuItem(beatmapInfo, isCurrentDifficulty, SwitchToDifficulty); } - protected void SwitchToDifficulty(BeatmapInfo beatmapInfo) => loader?.ScheduleDifficultySwitch(beatmapInfo); + protected void SwitchToDifficulty(BeatmapInfo nextBeatmap) => loader?.ScheduleDifficultySwitch(nextBeatmap, new EditorState + { + Time = clock.CurrentTimeAccurate + }); + + private void restoreState([NotNull] EditorState state) + { + if (state.Time != null) + clock.Seek(state.Time.Value); + } private void cancelExit() => loader?.CancelPendingDifficultySwitch(); diff --git a/osu.Game/Screens/Edit/EditorLoader.cs b/osu.Game/Screens/Edit/EditorLoader.cs index 6bbfa92c3b..b6e57b0491 100644 --- a/osu.Game/Screens/Edit/EditorLoader.cs +++ b/osu.Game/Screens/Edit/EditorLoader.cs @@ -20,6 +20,13 @@ namespace osu.Game.Screens.Edit /// public class EditorLoader : ScreenWithBeatmapBackground { + /// + /// The stored state from the last editor opened. + /// This will be read by the next editor instance to be opened to restore any relevant previous state. + /// + [CanBeNull] + public EditorState State; + public override float BackgroundParallaxAmount => 0.1f; public override bool AllowBackButton => false; @@ -61,7 +68,7 @@ namespace osu.Game.Screens.Edit } } - public void ScheduleDifficultySwitch(BeatmapInfo beatmapInfo) + public void ScheduleDifficultySwitch(BeatmapInfo nextBeatmap, EditorState editorState) { scheduledDifficultySwitch?.Cancel(); ValidForResume = true; @@ -70,7 +77,8 @@ namespace osu.Game.Screens.Edit scheduledDifficultySwitch = Schedule(() => { - Beatmap.Value = beatmapManager.GetWorkingBeatmap(beatmapInfo); + Beatmap.Value = beatmapManager.GetWorkingBeatmap(nextBeatmap); + State = editorState; // This screen is a weird exception to the rule that nothing after song select changes the global beatmap. // Because of this, we need to update the background stack's beatmap to match. diff --git a/osu.Game/Screens/Edit/EditorState.cs b/osu.Game/Screens/Edit/EditorState.cs new file mode 100644 index 0000000000..3aa92f35bc --- /dev/null +++ b/osu.Game/Screens/Edit/EditorState.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +namespace osu.Game.Screens.Edit +{ + /// + /// Structure used to transport data between instances on difficulty change. + /// It's intended to be received by from one editor instance and passed down to the next one. + /// + public class EditorState + { + /// + /// The current clock time when a difficulty switch was requested. + /// + public double? Time { get; set; } + } +} From 79d0f4835e0fbc6fd6fd28be2901c48c3ae7a1a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 11 Sep 2021 17:26:31 +0200 Subject: [PATCH 1864/2442] Add failing tests for preserving clipboard content --- .../Editing/TestSceneDifficultySwitching.cs | 34 +++++++++++++++++++ .../Screens/Edit/Compose/ComposeScreen.cs | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs index 0954bf1b49..1b548dc1ba 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs @@ -71,6 +71,40 @@ namespace osu.Game.Tests.Visual.Editing AddAssert("stack empty", () => Stack.CurrentScreen == null); } + [Test] + public void TestClipboardPreservedAfterSwitch([Values] bool sameRuleset) + { + BeatmapInfo targetDifficulty = null; + + AddStep("select first object", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.First())); + AddStep("copy object", () => Editor.Copy()); + + AddStep("set target difficulty", () => + { + targetDifficulty = sameRuleset + ? importedBeatmapSet.Beatmaps.Last(beatmap => !beatmap.Equals(Beatmap.Value.BeatmapInfo) && beatmap.RulesetID == Beatmap.Value.BeatmapInfo.RulesetID) + : importedBeatmapSet.Beatmaps.Last(beatmap => !beatmap.Equals(Beatmap.Value.BeatmapInfo) && beatmap.RulesetID != Beatmap.Value.BeatmapInfo.RulesetID); + }); + switchToDifficulty(() => targetDifficulty); + confirmEditingBeatmap(() => targetDifficulty); + + AddAssert("no objects selected", () => !EditorBeatmap.SelectedHitObjects.Any()); + AddStep("paste object", () => Editor.Paste()); + + if (sameRuleset) + AddAssert("object was pasted", () => EditorBeatmap.SelectedHitObjects.Any()); + else + AddAssert("object was not pasted", () => !EditorBeatmap.SelectedHitObjects.Any()); + + AddStep("exit editor", () => Stack.Exit()); + + AddUntilStep("prompt for save dialog shown", () => DialogOverlay.CurrentDialog is PromptForSaveDialog); + AddStep("discard changes", () => ((PromptForSaveDialog)DialogOverlay.CurrentDialog).PerformOkAction()); + + // ensure editor loader didn't resume. + AddAssert("stack empty", () => Stack.CurrentScreen == null); + } + [Test] public void TestPreventSwitchDueToUnsavedChanges() { diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs index 62b3d33069..e324f1edeb 100644 --- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs +++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs @@ -80,7 +80,7 @@ namespace osu.Game.Screens.Edit.Compose public bool OnPressed(PlatformAction action) { if (action == PlatformAction.Copy) - host.GetClipboard().SetText(formatSelectionAsString()); + host.GetClipboard()?.SetText(formatSelectionAsString()); return false; } From 35ee889e5b1b567757632b5bb9dd0b350b5820a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 11 Sep 2021 17:55:11 +0200 Subject: [PATCH 1865/2442] Restore clipboard content after difficulty switch --- osu.Game/Screens/Edit/Editor.cs | 5 ++++- osu.Game/Screens/Edit/EditorState.cs | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 502ac7a70f..eba75f4408 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -744,13 +744,16 @@ namespace osu.Game.Screens.Edit protected void SwitchToDifficulty(BeatmapInfo nextBeatmap) => loader?.ScheduleDifficultySwitch(nextBeatmap, new EditorState { - Time = clock.CurrentTimeAccurate + Time = clock.CurrentTimeAccurate, + ClipboardContent = editorBeatmap.BeatmapInfo.RulesetID == nextBeatmap.RulesetID ? clipboard.Value : null }); private void restoreState([NotNull] EditorState state) { if (state.Time != null) clock.Seek(state.Time.Value); + + clipboard.Value = state.ClipboardContent ?? clipboard.Value; } private void cancelExit() => loader?.CancelPendingDifficultySwitch(); diff --git a/osu.Game/Screens/Edit/EditorState.cs b/osu.Game/Screens/Edit/EditorState.cs index 3aa92f35bc..09cc1184d2 100644 --- a/osu.Game/Screens/Edit/EditorState.cs +++ b/osu.Game/Screens/Edit/EditorState.cs @@ -15,5 +15,10 @@ namespace osu.Game.Screens.Edit /// The current clock time when a difficulty switch was requested. /// public double? Time { get; set; } + + /// + /// The current editor clipboard content at the time when a difficulty switch was requested. + /// + public string? ClipboardContent { get; set; } } } From cbb9ff1c492b51f450f8841277597ff38de61667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 12 Sep 2021 13:44:25 +0200 Subject: [PATCH 1866/2442] Only run prompt-for-save test logic when relevant --- .../Visual/Editing/TestSceneDifficultySwitching.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs index 1b548dc1ba..c81a1abfbc 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs @@ -98,8 +98,11 @@ namespace osu.Game.Tests.Visual.Editing AddStep("exit editor", () => Stack.Exit()); - AddUntilStep("prompt for save dialog shown", () => DialogOverlay.CurrentDialog is PromptForSaveDialog); - AddStep("discard changes", () => ((PromptForSaveDialog)DialogOverlay.CurrentDialog).PerformOkAction()); + if (sameRuleset) + { + AddUntilStep("prompt for save dialog shown", () => DialogOverlay.CurrentDialog is PromptForSaveDialog); + AddStep("discard changes", () => ((PromptForSaveDialog)DialogOverlay.CurrentDialog).PerformOkAction()); + } // ensure editor loader didn't resume. AddAssert("stack empty", () => Stack.CurrentScreen == null); From 1a60ce164e31c5cf3706e25d4b7e42aa70f359da Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Tue, 24 Aug 2021 00:15:16 +0200 Subject: [PATCH 1867/2442] Add `ParticleJet` --- .../Visual/Gameplay/TestSceneParticleJet.cs | 61 +++++++ osu.Game/Graphics/Particles/ParticleJet.cs | 49 +++++ osu.Game/Graphics/Particles/ParticleSpewer.cs | 172 ++++++++++++++++++ 3 files changed, 282 insertions(+) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs create mode 100644 osu.Game/Graphics/Particles/ParticleJet.cs create mode 100644 osu.Game/Graphics/Particles/ParticleSpewer.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs new file mode 100644 index 0000000000..6438ba0b22 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs @@ -0,0 +1,61 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Testing; +using osu.Game.Graphics.Particles; +using osu.Game.Skinning; + +namespace osu.Game.Tests.Visual.Gameplay +{ + [TestFixture] + public class TestSceneParticleJet : OsuTestScene + { + private ParticleJet jet; + + [Resolved] + private SkinManager skinManager { get; set; } + + public TestSceneParticleJet() + { + AddStep("create", () => + { + Child = jet = createJet(); + }); + + AddToggleStep("toggle spawning", value => jet.Active = value); + } + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("create jet", () => Child = jet = createJet()); + } + + [Test] + public void TestPresence() + { + AddStep("start jet", () => jet.Active = true); + AddAssert("is present", () => jet.IsPresent); + + AddWaitStep("wait for some particles", 3); + AddStep("stop jet", () => jet.Active = false); + + AddWaitStep("wait for clean screen", 5); + AddAssert("is not present", () => !jet.IsPresent); + } + + private ParticleJet createJet() + { + return new ParticleJet(skinManager.DefaultLegacySkin.GetTexture("star2"), 180) + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativePositionAxes = Axes.Y, + Y = -0.1f, + }; + } + } +} diff --git a/osu.Game/Graphics/Particles/ParticleJet.cs b/osu.Game/Graphics/Particles/ParticleJet.cs new file mode 100644 index 0000000000..039dd36ddc --- /dev/null +++ b/osu.Game/Graphics/Particles/ParticleJet.cs @@ -0,0 +1,49 @@ +// 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 osu.Framework.Graphics.Textures; +using osu.Framework.Utils; +using osuTK; + +namespace osu.Game.Graphics.Particles +{ + public class ParticleJet : ParticleSpewer + { + private const int particles_per_second = 80; + private const double particle_lifetime = 500; + private const float angular_velocity = 3f; + private const int angle_spread = 10; + private const float velocity_min = 1.3f; + private const float velocity_max = 1.5f; + + private readonly int angle; + + protected override float ParticleGravity => 0.25f; + + public ParticleJet(Texture texture, int angle) + : base(texture, particles_per_second, particle_lifetime) + { + this.angle = angle; + } + + protected override FallingParticle SpawnParticle() + { + var directionRads = MathUtils.DegreesToRadians( + RNG.NextSingle(angle - angle_spread / 2, angle + angle_spread / 2) + ); + var direction = new Vector2(MathF.Sin(directionRads), MathF.Cos(directionRads)); + + return new FallingParticle + { + StartTime = (float)Time.Current, + Position = OriginPosition, + Duration = RNG.NextSingle((float)particle_lifetime * 0.8f, (float)particle_lifetime), + Velocity = direction * new Vector2(RNG.NextSingle(velocity_min, velocity_max)), + AngularVelocity = RNG.NextSingle(-angular_velocity, angular_velocity), + StartScale = 1f, + EndScale = 2f, + }; + } + } +} diff --git a/osu.Game/Graphics/Particles/ParticleSpewer.cs b/osu.Game/Graphics/Particles/ParticleSpewer.cs new file mode 100644 index 0000000000..7196727ca1 --- /dev/null +++ b/osu.Game/Graphics/Particles/ParticleSpewer.cs @@ -0,0 +1,172 @@ +// 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 osu.Framework.Graphics; +using osu.Framework.Graphics.OpenGL.Vertices; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osuTK; + +namespace osu.Game.Graphics.Particles +{ + public abstract class ParticleSpewer : Sprite + { + private readonly FallingParticle[] particles; + private int currentIndex; + private double lastParticleAdded; + + private readonly double cooldown; + private readonly double maxLifetime; + + /// + /// Determines whether particles are being spawned. + /// + public bool Active { get; set; } + + public bool HasActiveParticles => Active || (lastParticleAdded + maxLifetime) > Time.Current; + public override bool IsPresent => base.IsPresent && HasActiveParticles; + + protected virtual float ParticleGravity => 0.5f; + + protected ParticleSpewer(Texture texture, int perSecond, double maxLifetime) + { + Texture = texture; + Blending = BlendingParameters.Additive; + + particles = new FallingParticle[perSecond * (int)Math.Ceiling(maxLifetime / 1000)]; + + cooldown = 1000f / perSecond; + this.maxLifetime = maxLifetime; + } + + protected override void Update() + { + base.Update(); + + if (Active && Time.Current > lastParticleAdded + cooldown) + { + addParticle(SpawnParticle()); + } + + Invalidate(Invalidation.DrawNode); + } + + /// + /// Called each time a new particle should be spawned. + /// + protected abstract FallingParticle SpawnParticle(); + + private void addParticle(FallingParticle fallingParticle) + { + particles[currentIndex] = fallingParticle; + + currentIndex = (currentIndex + 1) % particles.Length; + lastParticleAdded = Time.Current; + } + + protected override DrawNode CreateDrawNode() => new ParticleSpewerDrawNode(this); + + private class ParticleSpewerDrawNode : SpriteDrawNode + { + private readonly FallingParticle[] particles; + + protected new ParticleSpewer Source => (ParticleSpewer)base.Source; + + private float currentTime; + private float gravity; + + public ParticleSpewerDrawNode(Sprite source) + : base(source) + { + particles = new FallingParticle[Source.particles.Length]; + } + + public override void ApplyState() + { + base.ApplyState(); + + Source.particles.CopyTo(particles, 0); + + currentTime = (float)Source.Time.Current; + gravity = Source.ParticleGravity; + } + + protected override void Blit(Action vertexAction) + { + foreach (var p in particles) + { + // ignore particles that weren't initialized. + if (p.StartTime <= 0) continue; + + var timeSinceStart = currentTime - p.StartTime; + + var alpha = p.AlphaAtTime(timeSinceStart); + if (alpha <= 0) continue; + + var scale = p.ScaleAtTime(timeSinceStart); + var pos = p.PositionAtTime(timeSinceStart, gravity); + var angle = p.AngleAtTime(timeSinceStart); + + var rect = new RectangleF( + pos.X - Texture.DisplayWidth * scale / 2, + pos.Y - Texture.DisplayHeight * scale / 2, + Texture.DisplayWidth * scale, + Texture.DisplayHeight * scale); + + var quad = new Quad( + transformPosition(rect.TopLeft, rect.Centre, angle), + transformPosition(rect.TopRight, rect.Centre, angle), + transformPosition(rect.BottomLeft, rect.Centre, angle), + transformPosition(rect.BottomRight, rect.Centre, angle) + ); + + DrawQuad(Texture, quad, DrawColourInfo.Colour.MultiplyAlpha(alpha), null, vertexAction, + new Vector2(InflationAmount.X / DrawRectangle.Width, InflationAmount.Y / DrawRectangle.Height), + null, TextureCoords); + } + } + + private Vector2 transformPosition(Vector2 pos, Vector2 centre, float angle) + { + // rotate point around centre. + float cos = MathF.Cos(angle); + float sin = MathF.Sin(angle); + + float x = centre.X + (pos.X - centre.X) * cos + (pos.Y - centre.Y) * sin; + float y = centre.Y + (pos.Y - centre.Y) * cos - (pos.X - centre.X) * sin; + + // convert to screen space. + return Vector2Extensions.Transform(new Vector2(x, y), DrawInfo.Matrix); + } + } + + protected struct FallingParticle + { + public float StartTime; + public Vector2 Position; + public Vector2 Velocity; + public float Duration; + public float AngularVelocity; + public float StartScale; + public float EndScale; + + public float AlphaAtTime(float timeSinceStart) => 1 - progressAtTime(timeSinceStart); + + public float ScaleAtTime(float timeSinceStart) => StartScale + (EndScale - StartScale) * progressAtTime(timeSinceStart); + + public float AngleAtTime(float timeSinceStart) => AngularVelocity / 1000 * timeSinceStart; + + public Vector2 PositionAtTime(float timeSinceStart, float gravity) + { + var progress = progressAtTime(timeSinceStart); + var grav = new Vector2(0, -gravity) * progress; + + return Position + (Velocity - grav) * timeSinceStart; + } + + private float progressAtTime(float timeSinceStart) => Math.Clamp(timeSinceStart / Duration, 0, 1); + } + } +} From 714cf33aac1b94ce27d6411b4f0efdc3cab92c8b Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 29 Aug 2021 21:41:47 +0200 Subject: [PATCH 1868/2442] Change `ParticleSpewer` to use screen space --- osu.Game/Graphics/Particles/ParticleJet.cs | 19 ++++--- osu.Game/Graphics/Particles/ParticleSpewer.cs | 51 +++++++++++++------ 2 files changed, 45 insertions(+), 25 deletions(-) diff --git a/osu.Game/Graphics/Particles/ParticleJet.cs b/osu.Game/Graphics/Particles/ParticleJet.cs index 039dd36ddc..eb7a49abc3 100644 --- a/osu.Game/Graphics/Particles/ParticleJet.cs +++ b/osu.Game/Graphics/Particles/ParticleJet.cs @@ -29,21 +29,20 @@ namespace osu.Game.Graphics.Particles protected override FallingParticle SpawnParticle() { + var p = base.SpawnParticle(); + var directionRads = MathUtils.DegreesToRadians( RNG.NextSingle(angle - angle_spread / 2, angle + angle_spread / 2) ); var direction = new Vector2(MathF.Sin(directionRads), MathF.Cos(directionRads)); - return new FallingParticle - { - StartTime = (float)Time.Current, - Position = OriginPosition, - Duration = RNG.NextSingle((float)particle_lifetime * 0.8f, (float)particle_lifetime), - Velocity = direction * new Vector2(RNG.NextSingle(velocity_min, velocity_max)), - AngularVelocity = RNG.NextSingle(-angular_velocity, angular_velocity), - StartScale = 1f, - EndScale = 2f, - }; + p.Duration = RNG.NextSingle((float)particle_lifetime * 0.8f, (float)particle_lifetime); + p.Velocity = direction * new Vector2(RNG.NextSingle(velocity_min, velocity_max)); + p.AngularVelocity = RNG.NextSingle(-angular_velocity, angular_velocity); + p.StartScale = 1f; + p.EndScale = 2f; + + return p; } } } diff --git a/osu.Game/Graphics/Particles/ParticleSpewer.cs b/osu.Game/Graphics/Particles/ParticleSpewer.cs index 7196727ca1..f2c358bd96 100644 --- a/osu.Game/Graphics/Particles/ParticleSpewer.cs +++ b/osu.Game/Graphics/Particles/ParticleSpewer.cs @@ -45,6 +45,10 @@ namespace osu.Game.Graphics.Particles { base.Update(); + // reset cooldown if the clock was rewound. + // this can happen when seeking in replays. + if (lastParticleAdded > Time.Current) lastParticleAdded = 0; + if (Active && Time.Current > lastParticleAdded + cooldown) { addParticle(SpawnParticle()); @@ -56,7 +60,14 @@ namespace osu.Game.Graphics.Particles /// /// Called each time a new particle should be spawned. /// - protected abstract FallingParticle SpawnParticle(); + protected virtual FallingParticle SpawnParticle() + { + return new FallingParticle + { + StartTime = (float)Time.Current, + StartPosition = ToScreenSpace(OriginPosition), + }; + } private void addParticle(FallingParticle fallingParticle) { @@ -68,6 +79,8 @@ namespace osu.Game.Graphics.Particles protected override DrawNode CreateDrawNode() => new ParticleSpewerDrawNode(this); + # region DrawNode + private class ParticleSpewerDrawNode : SpriteDrawNode { private readonly FallingParticle[] particles; @@ -102,6 +115,10 @@ namespace osu.Game.Graphics.Particles var timeSinceStart = currentTime - p.StartTime; + // ignore particles from the future. + // these can appear when seeking in replays. + if (timeSinceStart < 0) continue; + var alpha = p.AlphaAtTime(timeSinceStart); if (alpha <= 0) continue; @@ -109,17 +126,21 @@ namespace osu.Game.Graphics.Particles var pos = p.PositionAtTime(timeSinceStart, gravity); var angle = p.AngleAtTime(timeSinceStart); + var matrixScale = DrawInfo.Matrix.ExtractScale(); + var width = Texture.DisplayWidth * scale * matrixScale.X; + var height = Texture.DisplayHeight * scale * matrixScale.Y; + var rect = new RectangleF( - pos.X - Texture.DisplayWidth * scale / 2, - pos.Y - Texture.DisplayHeight * scale / 2, - Texture.DisplayWidth * scale, - Texture.DisplayHeight * scale); + pos.X - width / 2, + pos.Y - height / 2, + width, + height); var quad = new Quad( - transformPosition(rect.TopLeft, rect.Centre, angle), - transformPosition(rect.TopRight, rect.Centre, angle), - transformPosition(rect.BottomLeft, rect.Centre, angle), - transformPosition(rect.BottomRight, rect.Centre, angle) + rotatePosition(rect.TopLeft, rect.Centre, angle), + rotatePosition(rect.TopRight, rect.Centre, angle), + rotatePosition(rect.BottomLeft, rect.Centre, angle), + rotatePosition(rect.BottomRight, rect.Centre, angle) ); DrawQuad(Texture, quad, DrawColourInfo.Colour.MultiplyAlpha(alpha), null, vertexAction, @@ -128,24 +149,24 @@ namespace osu.Game.Graphics.Particles } } - private Vector2 transformPosition(Vector2 pos, Vector2 centre, float angle) + private Vector2 rotatePosition(Vector2 pos, Vector2 centre, float angle) { - // rotate point around centre. float cos = MathF.Cos(angle); float sin = MathF.Sin(angle); float x = centre.X + (pos.X - centre.X) * cos + (pos.Y - centre.Y) * sin; float y = centre.Y + (pos.Y - centre.Y) * cos - (pos.X - centre.X) * sin; - // convert to screen space. - return Vector2Extensions.Transform(new Vector2(x, y), DrawInfo.Matrix); + return new Vector2(x, y); } } + #endregion + protected struct FallingParticle { public float StartTime; - public Vector2 Position; + public Vector2 StartPosition; public Vector2 Velocity; public float Duration; public float AngularVelocity; @@ -163,7 +184,7 @@ namespace osu.Game.Graphics.Particles var progress = progressAtTime(timeSinceStart); var grav = new Vector2(0, -gravity) * progress; - return Position + (Velocity - grav) * timeSinceStart; + return StartPosition + (Velocity - grav) * timeSinceStart; } private float progressAtTime(float timeSinceStart) => Math.Clamp(timeSinceStart / Duration, 0, 1); From 4c753420d3859df95a3eed7751df08b0f83a6747 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sat, 4 Sep 2021 16:47:12 +0200 Subject: [PATCH 1869/2442] Fix `ParticleSpewer` gravity calculation --- osu.Game/Graphics/Particles/ParticleJet.cs | 6 +++--- osu.Game/Graphics/Particles/ParticleSpewer.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/Particles/ParticleJet.cs b/osu.Game/Graphics/Particles/ParticleJet.cs index eb7a49abc3..6bdde44a2c 100644 --- a/osu.Game/Graphics/Particles/ParticleJet.cs +++ b/osu.Game/Graphics/Particles/ParticleJet.cs @@ -14,12 +14,12 @@ namespace osu.Game.Graphics.Particles private const double particle_lifetime = 500; private const float angular_velocity = 3f; private const int angle_spread = 10; - private const float velocity_min = 1.3f; - private const float velocity_max = 1.5f; + private const float velocity_min = 1300f; + private const float velocity_max = 1500f; private readonly int angle; - protected override float ParticleGravity => 0.25f; + protected override float ParticleGravity => 750f; public ParticleJet(Texture texture, int angle) : base(texture, particles_per_second, particle_lifetime) diff --git a/osu.Game/Graphics/Particles/ParticleSpewer.cs b/osu.Game/Graphics/Particles/ParticleSpewer.cs index f2c358bd96..52e089fcca 100644 --- a/osu.Game/Graphics/Particles/ParticleSpewer.cs +++ b/osu.Game/Graphics/Particles/ParticleSpewer.cs @@ -28,7 +28,7 @@ namespace osu.Game.Graphics.Particles public bool HasActiveParticles => Active || (lastParticleAdded + maxLifetime) > Time.Current; public override bool IsPresent => base.IsPresent && HasActiveParticles; - protected virtual float ParticleGravity => 0.5f; + protected virtual float ParticleGravity => 0; protected ParticleSpewer(Texture texture, int perSecond, double maxLifetime) { @@ -182,9 +182,9 @@ namespace osu.Game.Graphics.Particles public Vector2 PositionAtTime(float timeSinceStart, float gravity) { var progress = progressAtTime(timeSinceStart); - var grav = new Vector2(0, -gravity) * progress; + var currentGravity = new Vector2(0, gravity * Duration / 1000 * progress); - return StartPosition + (Velocity - grav) * timeSinceStart; + return StartPosition + (Velocity + currentGravity) * timeSinceStart / 1000; } private float progressAtTime(float timeSinceStart) => Math.Clamp(timeSinceStart / Duration, 0, 1); From 328c9a5dd038d9882ebd11d8f0ba068b16826252 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sat, 4 Sep 2021 20:50:30 +0200 Subject: [PATCH 1870/2442] Change `ParticleSpewer.Active` to a Bindable --- osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs | 6 +++--- osu.Game/Graphics/Particles/ParticleSpewer.cs | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs index 6438ba0b22..e570abcf88 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.Gameplay Child = jet = createJet(); }); - AddToggleStep("toggle spawning", value => jet.Active = value); + AddToggleStep("toggle spawning", value => jet.Active.Value = value); } [SetUpSteps] @@ -37,11 +37,11 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestPresence() { - AddStep("start jet", () => jet.Active = true); + AddStep("start jet", () => jet.Active.Value = true); AddAssert("is present", () => jet.IsPresent); AddWaitStep("wait for some particles", 3); - AddStep("stop jet", () => jet.Active = false); + AddStep("stop jet", () => jet.Active.Value = false); AddWaitStep("wait for clean screen", 5); AddAssert("is not present", () => !jet.IsPresent); diff --git a/osu.Game/Graphics/Particles/ParticleSpewer.cs b/osu.Game/Graphics/Particles/ParticleSpewer.cs index 52e089fcca..bc25206311 100644 --- a/osu.Game/Graphics/Particles/ParticleSpewer.cs +++ b/osu.Game/Graphics/Particles/ParticleSpewer.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Vertices; using osu.Framework.Graphics.Primitives; @@ -23,9 +24,9 @@ namespace osu.Game.Graphics.Particles /// /// Determines whether particles are being spawned. /// - public bool Active { get; set; } + public readonly BindableBool Active = new BindableBool(); - public bool HasActiveParticles => Active || (lastParticleAdded + maxLifetime) > Time.Current; + public bool HasActiveParticles => Active.Value || (lastParticleAdded + maxLifetime) > Time.Current; public override bool IsPresent => base.IsPresent && HasActiveParticles; protected virtual float ParticleGravity => 0; @@ -49,7 +50,7 @@ namespace osu.Game.Graphics.Particles // this can happen when seeking in replays. if (lastParticleAdded > Time.Current) lastParticleAdded = 0; - if (Active && Time.Current > lastParticleAdded + cooldown) + if (Active.Value && Time.Current > lastParticleAdded + cooldown) { addParticle(SpawnParticle()); } From ee4006f3d73389eddfcc382de288bb041d4f1989 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sat, 4 Sep 2021 21:49:05 +0200 Subject: [PATCH 1871/2442] Add legacy cursor star particles --- .../Skinning/Legacy/LegacyCursor.cs | 5 + .../Legacy/LegacyCursorStarParticles.cs | 185 ++++++++++++++++++ osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 1 + 3 files changed, 191 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs index b2ffc171be..cd7954a8d6 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs @@ -31,6 +31,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy InternalChildren = new[] { + new LegacyCursorStarParticles + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, ExpandTarget = new NonPlayfieldSprite { Texture = skin.GetTexture("cursor"), diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs new file mode 100644 index 0000000000..f10d9a0fa9 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs @@ -0,0 +1,185 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Textures; +using osu.Framework.Input.Bindings; +using osu.Framework.Utils; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Particles; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Screens.Play; +using osu.Game.Skinning; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Skinning.Legacy +{ + public class LegacyCursorStarParticles : BeatSyncedContainer, IKeyBindingHandler + { + private StarParticleSpewer breakSpewer; + private StarParticleSpewer kiaiSpewer; + + [Resolved(canBeNull: true)] + private Player player { get; set; } + + [Resolved(canBeNull: true)] + private OsuPlayfield osuPlayfield { get; set; } + + [BackgroundDependencyLoader] + private void load(ISkinSource skin, OsuColour colour) + { + var texture = skin.GetTexture("star2"); + + InternalChildren = new[] + { + breakSpewer = new StarParticleSpewer(texture, 20) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = colour.PinkLighter, + Direction = SpewDirection.None, + Active = + { + Value = true, + } + }, + kiaiSpewer = new StarParticleSpewer(texture, 60) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = colour.PinkLighter, + Direction = SpewDirection.None, + Active = + { + Value = false, + } + }, + }; + + if (player != null) + { + breakSpewer.Active.BindTarget = player.IsBreakTime; + } + } + + protected override void Update() + { + if (osuPlayfield == null) return; + + // find active kiai slider or spinner. + var kiaiHitObject = osuPlayfield.HitObjectContainer.AliveObjects.FirstOrDefault(h => + h.HitObject.Kiai && + ( + (h is DrawableSlider slider && slider.Tracking.Value) || + (h is DrawableSpinner spinner && spinner.RotationTracker.Tracking) + ) + ); + + kiaiSpewer.Active.Value = kiaiHitObject != null; + } + + public bool OnPressed(OsuAction action) + { + handleInput(action, true); + return false; + } + + public void OnReleased(OsuAction action) + { + handleInput(action, false); + } + + private bool leftPressed; + private bool rightPressed; + + private void handleInput(OsuAction action, bool pressed) + { + switch (action) + { + case OsuAction.LeftButton: + leftPressed = pressed; + break; + + case OsuAction.RightButton: + rightPressed = pressed; + break; + } + + if (leftPressed && rightPressed) + breakSpewer.Direction = SpewDirection.Both; + else if (leftPressed) + breakSpewer.Direction = SpewDirection.Left; + else if (rightPressed) + breakSpewer.Direction = SpewDirection.Right; + else + breakSpewer.Direction = SpewDirection.None; + } + + private class StarParticleSpewer : ParticleSpewer + { + private const int particle_lifetime_min = 300; + private const int particle_lifetime_max = 1000; + + public SpewDirection Direction { get; set; } + + protected override float ParticleGravity => 460; + + public StarParticleSpewer(Texture texture, int perSecond) + : base(texture, perSecond, particle_lifetime_max) + { + } + + protected override FallingParticle SpawnParticle() + { + var p = base.SpawnParticle(); + + p.Duration = RNG.NextSingle(particle_lifetime_min, particle_lifetime_max); + p.AngularVelocity = RNG.NextSingle(-3f, 3f); + p.StartScale = RNG.NextSingle(0.5f, 1f); + p.EndScale = RNG.NextSingle(2f); + + switch (Direction) + { + case SpewDirection.None: + p.Velocity = Vector2.Zero; + break; + + case SpewDirection.Left: + p.Velocity = new Vector2( + RNG.NextSingle(-460f, 0) * 2, + RNG.NextSingle(-40f, 40f) * 2 + ); + break; + + case SpewDirection.Right: + p.Velocity = new Vector2( + RNG.NextSingle(0, 460f) * 2, + RNG.NextSingle(-40f, 40f) * 2 + ); + break; + + case SpewDirection.Both: + p.Velocity = new Vector2( + RNG.NextSingle(-460f, 460f) * 2, + RNG.NextSingle(-160f, 160f) * 2 + ); + break; + } + + return p; + } + } + + private enum SpewDirection + { + None, + Left, + Right, + Both, + } + } +} diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index c1c2ea2299..2233a547b9 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -24,6 +24,7 @@ using osuTK; namespace osu.Game.Rulesets.Osu.UI { + [Cached] public class OsuPlayfield : Playfield { private readonly PlayfieldBorder playfieldBorder; From 5b1b36436f0ec4ccf95f9dbdf45c28f5a87c3fbc Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 5 Sep 2021 01:01:49 +0200 Subject: [PATCH 1872/2442] Add cursor velocity to star particles --- .../Legacy/LegacyCursorStarParticles.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs index f10d9a0fa9..52d4eedf42 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs @@ -131,6 +131,44 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy public StarParticleSpewer(Texture texture, int perSecond) : base(texture, perSecond, particle_lifetime_max) { + Active.BindValueChanged(_ => resetVelocityCalculation()); + } + + private Vector2 screenPosition => ToScreenSpace(OriginPosition); + + private Vector2 screenVelocity; + + private const double velocity_calculation_delay = 15; + private double lastVelocityCalculation; + private Vector2 positionDifference; + private Vector2? lastPosition; + + protected override void Update() + { + base.Update(); + + if (lastPosition != null) + { + positionDifference += (screenPosition - lastPosition.Value); + lastVelocityCalculation += Clock.ElapsedFrameTime; + } + + lastPosition = screenPosition; + + if (lastVelocityCalculation > velocity_calculation_delay) + { + screenVelocity = positionDifference / (float)lastVelocityCalculation; + + positionDifference = Vector2.Zero; + lastVelocityCalculation = 0; + } + } + + private void resetVelocityCalculation() + { + positionDifference = Vector2.Zero; + lastVelocityCalculation = 0; + lastPosition = null; } protected override FallingParticle SpawnParticle() @@ -170,6 +208,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy break; } + p.Velocity += screenVelocity * 50; + return p; } } From db662f8c5c9ae2b7b35a0da4317e1e9ee94ab9dd Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 5 Sep 2021 16:08:22 +0200 Subject: [PATCH 1873/2442] Add `ParticleParent` option to `ParticleSpewer` --- .../Legacy/LegacyCursorStarParticles.cs | 31 ++++++++++++------- osu.Game/Graphics/Particles/ParticleJet.cs | 1 + osu.Game/Graphics/Particles/ParticleSpewer.cs | 17 +++++++--- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs index 52d4eedf42..58fe4f9b7c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Direction = SpewDirection.None, Active = { - Value = true, + Value = false, } }, kiaiSpewer = new StarParticleSpewer(texture, 60) @@ -64,6 +64,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { breakSpewer.Active.BindTarget = player.IsBreakTime; } + + if (osuPlayfield != null) + { + breakSpewer.ParticleParent = osuPlayfield; + kiaiSpewer.ParticleParent = osuPlayfield; + } } protected override void Update() @@ -126,7 +132,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy public SpewDirection Direction { get; set; } - protected override float ParticleGravity => 460; + protected override float ParticleGravity => 240; public StarParticleSpewer(Texture texture, int perSecond) : base(texture, perSecond, particle_lifetime_max) @@ -134,7 +140,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Active.BindValueChanged(_ => resetVelocityCalculation()); } - private Vector2 screenPosition => ToScreenSpace(OriginPosition); + private Vector2 positionInParent => ToSpaceOfOtherDrawable(OriginPosition, ParticleParent); private Vector2 screenVelocity; @@ -149,11 +155,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy if (lastPosition != null) { - positionDifference += (screenPosition - lastPosition.Value); + positionDifference += (positionInParent - lastPosition.Value); lastVelocityCalculation += Clock.ElapsedFrameTime; } - lastPosition = screenPosition; + lastPosition = positionInParent; if (lastVelocityCalculation > velocity_calculation_delay) { @@ -175,6 +181,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { var p = base.SpawnParticle(); + p.StartPosition = positionInParent; p.Duration = RNG.NextSingle(particle_lifetime_min, particle_lifetime_max); p.AngularVelocity = RNG.NextSingle(-3f, 3f); p.StartScale = RNG.NextSingle(0.5f, 1f); @@ -188,27 +195,27 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy case SpewDirection.Left: p.Velocity = new Vector2( - RNG.NextSingle(-460f, 0) * 2, - RNG.NextSingle(-40f, 40f) * 2 + RNG.NextSingle(-460f, 0), + RNG.NextSingle(-40f, 40f) ); break; case SpewDirection.Right: p.Velocity = new Vector2( - RNG.NextSingle(0, 460f) * 2, - RNG.NextSingle(-40f, 40f) * 2 + RNG.NextSingle(0, 460f), + RNG.NextSingle(-40f, 40f) ); break; case SpewDirection.Both: p.Velocity = new Vector2( - RNG.NextSingle(-460f, 460f) * 2, - RNG.NextSingle(-160f, 160f) * 2 + RNG.NextSingle(-460f, 460f), + RNG.NextSingle(-160f, 160f) ); break; } - p.Velocity += screenVelocity * 50; + p.Velocity += screenVelocity * 40; return p; } diff --git a/osu.Game/Graphics/Particles/ParticleJet.cs b/osu.Game/Graphics/Particles/ParticleJet.cs index 6bdde44a2c..763f8d0a9e 100644 --- a/osu.Game/Graphics/Particles/ParticleJet.cs +++ b/osu.Game/Graphics/Particles/ParticleJet.cs @@ -36,6 +36,7 @@ namespace osu.Game.Graphics.Particles ); var direction = new Vector2(MathF.Sin(directionRads), MathF.Cos(directionRads)); + p.StartPosition = OriginPosition; p.Duration = RNG.NextSingle((float)particle_lifetime * 0.8f, (float)particle_lifetime); p.Velocity = direction * new Vector2(RNG.NextSingle(velocity_min, velocity_max)); p.AngularVelocity = RNG.NextSingle(-angular_velocity, angular_velocity); diff --git a/osu.Game/Graphics/Particles/ParticleSpewer.cs b/osu.Game/Graphics/Particles/ParticleSpewer.cs index bc25206311..2251d9590d 100644 --- a/osu.Game/Graphics/Particles/ParticleSpewer.cs +++ b/osu.Game/Graphics/Particles/ParticleSpewer.cs @@ -26,6 +26,12 @@ namespace osu.Game.Graphics.Particles /// public readonly BindableBool Active = new BindableBool(); + /// + /// whose DrawInfo will be used to draw each particle. + /// Defaults to the itself. + /// + public IDrawable ParticleParent; + public bool HasActiveParticles => Active.Value || (lastParticleAdded + maxLifetime) > Time.Current; public override bool IsPresent => base.IsPresent && HasActiveParticles; @@ -35,6 +41,7 @@ namespace osu.Game.Graphics.Particles { Texture = texture; Blending = BlendingParameters.Additive; + ParticleParent = this; particles = new FallingParticle[perSecond * (int)Math.Ceiling(maxLifetime / 1000)]; @@ -66,7 +73,6 @@ namespace osu.Game.Graphics.Particles return new FallingParticle { StartTime = (float)Time.Current, - StartPosition = ToScreenSpace(OriginPosition), }; } @@ -90,6 +96,7 @@ namespace osu.Game.Graphics.Particles private float currentTime; private float gravity; + private Matrix3 particleDrawMatrix; public ParticleSpewerDrawNode(Sprite source) : base(source) @@ -105,6 +112,7 @@ namespace osu.Game.Graphics.Particles currentTime = (float)Source.Time.Current; gravity = Source.ParticleGravity; + particleDrawMatrix = Source.ParticleParent.DrawInfo.Matrix; } protected override void Blit(Action vertexAction) @@ -127,9 +135,8 @@ namespace osu.Game.Graphics.Particles var pos = p.PositionAtTime(timeSinceStart, gravity); var angle = p.AngleAtTime(timeSinceStart); - var matrixScale = DrawInfo.Matrix.ExtractScale(); - var width = Texture.DisplayWidth * scale * matrixScale.X; - var height = Texture.DisplayHeight * scale * matrixScale.Y; + var width = Texture.DisplayWidth * scale; + var height = Texture.DisplayHeight * scale; var rect = new RectangleF( pos.X - width / 2, @@ -158,7 +165,7 @@ namespace osu.Game.Graphics.Particles float x = centre.X + (pos.X - centre.X) * cos + (pos.Y - centre.Y) * sin; float y = centre.Y + (pos.Y - centre.Y) * cos - (pos.X - centre.X) * sin; - return new Vector2(x, y); + return Vector2Extensions.Transform(new Vector2(x, y), particleDrawMatrix); } } From 6d68da8ff00229bfcbccd68f7b6d73c80c4da70d Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 5 Sep 2021 16:25:24 +0200 Subject: [PATCH 1874/2442] Remove `StartScale` from `ParticleSpewer` particles --- .../Skinning/Legacy/LegacyCursorStarParticles.cs | 1 - osu.Game/Graphics/Particles/ParticleJet.cs | 1 - osu.Game/Graphics/Particles/ParticleSpewer.cs | 3 +-- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs index 58fe4f9b7c..d6c4d9f92a 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs @@ -184,7 +184,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy p.StartPosition = positionInParent; p.Duration = RNG.NextSingle(particle_lifetime_min, particle_lifetime_max); p.AngularVelocity = RNG.NextSingle(-3f, 3f); - p.StartScale = RNG.NextSingle(0.5f, 1f); p.EndScale = RNG.NextSingle(2f); switch (Direction) diff --git a/osu.Game/Graphics/Particles/ParticleJet.cs b/osu.Game/Graphics/Particles/ParticleJet.cs index 763f8d0a9e..2d70a7f697 100644 --- a/osu.Game/Graphics/Particles/ParticleJet.cs +++ b/osu.Game/Graphics/Particles/ParticleJet.cs @@ -40,7 +40,6 @@ namespace osu.Game.Graphics.Particles p.Duration = RNG.NextSingle((float)particle_lifetime * 0.8f, (float)particle_lifetime); p.Velocity = direction * new Vector2(RNG.NextSingle(velocity_min, velocity_max)); p.AngularVelocity = RNG.NextSingle(-angular_velocity, angular_velocity); - p.StartScale = 1f; p.EndScale = 2f; return p; diff --git a/osu.Game/Graphics/Particles/ParticleSpewer.cs b/osu.Game/Graphics/Particles/ParticleSpewer.cs index 2251d9590d..f9fb30abdc 100644 --- a/osu.Game/Graphics/Particles/ParticleSpewer.cs +++ b/osu.Game/Graphics/Particles/ParticleSpewer.cs @@ -178,12 +178,11 @@ namespace osu.Game.Graphics.Particles public Vector2 Velocity; public float Duration; public float AngularVelocity; - public float StartScale; public float EndScale; public float AlphaAtTime(float timeSinceStart) => 1 - progressAtTime(timeSinceStart); - public float ScaleAtTime(float timeSinceStart) => StartScale + (EndScale - StartScale) * progressAtTime(timeSinceStart); + public float ScaleAtTime(float timeSinceStart) => 1 + (EndScale - 1) * progressAtTime(timeSinceStart); public float AngleAtTime(float timeSinceStart) => AngularVelocity / 1000 * timeSinceStart; From c2f7b01ca400b8286365877c3f363f247897c857 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 5 Sep 2021 17:08:00 +0200 Subject: [PATCH 1875/2442] Change particle `AngularVelocity` into `StartAngle` and `EndAngle` --- .../Skinning/Legacy/LegacyCursorStarParticles.cs | 3 ++- osu.Game/Graphics/Particles/ParticleJet.cs | 4 ++-- osu.Game/Graphics/Particles/ParticleSpewer.cs | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs index d6c4d9f92a..9c901a7de5 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs @@ -183,7 +183,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy p.StartPosition = positionInParent; p.Duration = RNG.NextSingle(particle_lifetime_min, particle_lifetime_max); - p.AngularVelocity = RNG.NextSingle(-3f, 3f); + p.StartAngle = (float)(RNG.NextDouble() * 4 - 2); + p.EndAngle = RNG.NextSingle(-2f, 2f); p.EndScale = RNG.NextSingle(2f); switch (Direction) diff --git a/osu.Game/Graphics/Particles/ParticleJet.cs b/osu.Game/Graphics/Particles/ParticleJet.cs index 2d70a7f697..a76aa6b75f 100644 --- a/osu.Game/Graphics/Particles/ParticleJet.cs +++ b/osu.Game/Graphics/Particles/ParticleJet.cs @@ -12,7 +12,6 @@ namespace osu.Game.Graphics.Particles { private const int particles_per_second = 80; private const double particle_lifetime = 500; - private const float angular_velocity = 3f; private const int angle_spread = 10; private const float velocity_min = 1300f; private const float velocity_max = 1500f; @@ -39,7 +38,8 @@ namespace osu.Game.Graphics.Particles p.StartPosition = OriginPosition; p.Duration = RNG.NextSingle((float)particle_lifetime * 0.8f, (float)particle_lifetime); p.Velocity = direction * new Vector2(RNG.NextSingle(velocity_min, velocity_max)); - p.AngularVelocity = RNG.NextSingle(-angular_velocity, angular_velocity); + p.StartAngle = RNG.NextSingle(-2f, 2f); + p.EndAngle = RNG.NextSingle(-2f, 2f); p.EndScale = 2f; return p; diff --git a/osu.Game/Graphics/Particles/ParticleSpewer.cs b/osu.Game/Graphics/Particles/ParticleSpewer.cs index f9fb30abdc..3d2225d382 100644 --- a/osu.Game/Graphics/Particles/ParticleSpewer.cs +++ b/osu.Game/Graphics/Particles/ParticleSpewer.cs @@ -177,14 +177,15 @@ namespace osu.Game.Graphics.Particles public Vector2 StartPosition; public Vector2 Velocity; public float Duration; - public float AngularVelocity; + public float StartAngle; + public float EndAngle; public float EndScale; public float AlphaAtTime(float timeSinceStart) => 1 - progressAtTime(timeSinceStart); public float ScaleAtTime(float timeSinceStart) => 1 + (EndScale - 1) * progressAtTime(timeSinceStart); - public float AngleAtTime(float timeSinceStart) => AngularVelocity / 1000 * timeSinceStart; + public float AngleAtTime(float timeSinceStart) => StartAngle + (EndAngle - StartAngle) * progressAtTime(timeSinceStart); public Vector2 PositionAtTime(float timeSinceStart, float gravity) { From 99eff4f41f5e5d126fc165fed86309e98ed05cfd Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Thu, 9 Sep 2021 23:18:19 +0200 Subject: [PATCH 1876/2442] Move cursor particles under `OsuCursorContainer` --- .../Skinning/Legacy/LegacyCursor.cs | 5 -- .../Legacy/LegacyCursorStarParticles.cs | 71 ++++++++++--------- .../UI/Cursor/OsuCursorContainer.cs | 11 ++- osu.Game/Graphics/Particles/ParticleSpewer.cs | 27 ++----- 4 files changed, 53 insertions(+), 61 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs index cd7954a8d6..b2ffc171be 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs @@ -31,11 +31,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy InternalChildren = new[] { - new LegacyCursorStarParticles - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, ExpandTarget = new NonPlayfieldSprite { Texture = skin.GetTexture("cursor"), diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs index 9c901a7de5..89ffa4ca15 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs @@ -4,11 +4,13 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Textures; +using osu.Framework.Input; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Framework.Utils; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Particles; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.UI; @@ -18,7 +20,7 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Skinning.Legacy { - public class LegacyCursorStarParticles : BeatSyncedContainer, IKeyBindingHandler + public class LegacyCursorStarParticles : CompositeDrawable, IKeyBindingHandler { private StarParticleSpewer breakSpewer; private StarParticleSpewer kiaiSpewer; @@ -64,12 +66,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { breakSpewer.Active.BindTarget = player.IsBreakTime; } - - if (osuPlayfield != null) - { - breakSpewer.ParticleParent = osuPlayfield; - kiaiSpewer.ParticleParent = osuPlayfield; - } } protected override void Update() @@ -116,7 +112,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy } if (leftPressed && rightPressed) - breakSpewer.Direction = SpewDirection.Both; + breakSpewer.Direction = SpewDirection.Omni; else if (leftPressed) breakSpewer.Direction = SpewDirection.Left; else if (rightPressed) @@ -125,13 +121,14 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy breakSpewer.Direction = SpewDirection.None; } - private class StarParticleSpewer : ParticleSpewer + private class StarParticleSpewer : ParticleSpewer, IRequireHighFrequencyMousePosition { private const int particle_lifetime_min = 300; private const int particle_lifetime_max = 1000; public SpewDirection Direction { get; set; } + protected override bool CanSpawnParticles => base.CanSpawnParticles && cursorScreenPosition.HasValue; protected override float ParticleGravity => 240; public StarParticleSpewer(Texture texture, int perSecond) @@ -140,48 +137,52 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Active.BindValueChanged(_ => resetVelocityCalculation()); } - private Vector2 positionInParent => ToSpaceOfOtherDrawable(OriginPosition, ParticleParent); + private Vector2? cursorScreenPosition; + private Vector2 cursorVelocity; - private Vector2 screenVelocity; + private const double max_velocity_frame_length = 15; + private double velocityFrameLength; + private Vector2 totalPosDifference; - private const double velocity_calculation_delay = 15; - private double lastVelocityCalculation; - private Vector2 positionDifference; - private Vector2? lastPosition; - - protected override void Update() + protected override bool OnMouseMove(MouseMoveEvent e) { - base.Update(); - - if (lastPosition != null) + if (cursorScreenPosition == null) { - positionDifference += (positionInParent - lastPosition.Value); - lastVelocityCalculation += Clock.ElapsedFrameTime; + cursorScreenPosition = e.ScreenSpaceMousePosition; + return base.OnMouseMove(e); } - lastPosition = positionInParent; + // calculate cursor velocity. + totalPosDifference += e.ScreenSpaceMousePosition - cursorScreenPosition.Value; + cursorScreenPosition = e.ScreenSpaceMousePosition; - if (lastVelocityCalculation > velocity_calculation_delay) + velocityFrameLength += Clock.ElapsedFrameTime; + + if (velocityFrameLength > max_velocity_frame_length) { - screenVelocity = positionDifference / (float)lastVelocityCalculation; + cursorVelocity = totalPosDifference / (float)velocityFrameLength; - positionDifference = Vector2.Zero; - lastVelocityCalculation = 0; + totalPosDifference = Vector2.Zero; + velocityFrameLength = 0; } + + return base.OnMouseMove(e); } + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; + private void resetVelocityCalculation() { - positionDifference = Vector2.Zero; - lastVelocityCalculation = 0; - lastPosition = null; + cursorScreenPosition = null; + totalPosDifference = Vector2.Zero; + velocityFrameLength = 0; } protected override FallingParticle SpawnParticle() { var p = base.SpawnParticle(); - p.StartPosition = positionInParent; + p.StartPosition = ToLocalSpace(cursorScreenPosition ?? Vector2.Zero); p.Duration = RNG.NextSingle(particle_lifetime_min, particle_lifetime_max); p.StartAngle = (float)(RNG.NextDouble() * 4 - 2); p.EndAngle = RNG.NextSingle(-2f, 2f); @@ -207,7 +208,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy ); break; - case SpewDirection.Both: + case SpewDirection.Omni: p.Velocity = new Vector2( RNG.NextSingle(-460f, 460f), RNG.NextSingle(-160f, 160f) @@ -215,7 +216,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy break; } - p.Velocity += screenVelocity * 40; + p.Velocity += cursorVelocity * 40; return p; } @@ -226,7 +227,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy None, Left, Right, - Both, + Omni, } } } diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs index 5812e8cf75..fbb7bfd7b1 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs @@ -11,6 +11,7 @@ using osu.Framework.Input.Bindings; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets.Osu.Configuration; +using osu.Game.Rulesets.Osu.Skinning.Legacy; using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; using osu.Game.Skinning; @@ -42,7 +43,15 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor InternalChild = fadeContainer = new Container { RelativeSizeAxes = Axes.Both, - Child = cursorTrail = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.CursorTrail), _ => new DefaultCursorTrail(), confineMode: ConfineMode.NoScaling) + Children = new[] + { + cursorTrail = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.CursorTrail), _ => new DefaultCursorTrail(), confineMode: ConfineMode.NoScaling), + new LegacyCursorStarParticles() + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + } }; } diff --git a/osu.Game/Graphics/Particles/ParticleSpewer.cs b/osu.Game/Graphics/Particles/ParticleSpewer.cs index 3d2225d382..f0c9dff239 100644 --- a/osu.Game/Graphics/Particles/ParticleSpewer.cs +++ b/osu.Game/Graphics/Particles/ParticleSpewer.cs @@ -26,22 +26,16 @@ namespace osu.Game.Graphics.Particles /// public readonly BindableBool Active = new BindableBool(); - /// - /// whose DrawInfo will be used to draw each particle. - /// Defaults to the itself. - /// - public IDrawable ParticleParent; - public bool HasActiveParticles => Active.Value || (lastParticleAdded + maxLifetime) > Time.Current; public override bool IsPresent => base.IsPresent && HasActiveParticles; + protected virtual bool CanSpawnParticles => true; protected virtual float ParticleGravity => 0; protected ParticleSpewer(Texture texture, int perSecond, double maxLifetime) { Texture = texture; Blending = BlendingParameters.Additive; - ParticleParent = this; particles = new FallingParticle[perSecond * (int)Math.Ceiling(maxLifetime / 1000)]; @@ -57,9 +51,12 @@ namespace osu.Game.Graphics.Particles // this can happen when seeking in replays. if (lastParticleAdded > Time.Current) lastParticleAdded = 0; - if (Active.Value && Time.Current > lastParticleAdded + cooldown) + if (Active.Value && CanSpawnParticles && Time.Current > lastParticleAdded + cooldown) { - addParticle(SpawnParticle()); + particles[currentIndex] = SpawnParticle(); + + currentIndex = (currentIndex + 1) % particles.Length; + lastParticleAdded = Time.Current; } Invalidate(Invalidation.DrawNode); @@ -76,14 +73,6 @@ namespace osu.Game.Graphics.Particles }; } - private void addParticle(FallingParticle fallingParticle) - { - particles[currentIndex] = fallingParticle; - - currentIndex = (currentIndex + 1) % particles.Length; - lastParticleAdded = Time.Current; - } - protected override DrawNode CreateDrawNode() => new ParticleSpewerDrawNode(this); # region DrawNode @@ -96,7 +85,6 @@ namespace osu.Game.Graphics.Particles private float currentTime; private float gravity; - private Matrix3 particleDrawMatrix; public ParticleSpewerDrawNode(Sprite source) : base(source) @@ -112,7 +100,6 @@ namespace osu.Game.Graphics.Particles currentTime = (float)Source.Time.Current; gravity = Source.ParticleGravity; - particleDrawMatrix = Source.ParticleParent.DrawInfo.Matrix; } protected override void Blit(Action vertexAction) @@ -165,7 +152,7 @@ namespace osu.Game.Graphics.Particles float x = centre.X + (pos.X - centre.X) * cos + (pos.Y - centre.Y) * sin; float y = centre.Y + (pos.Y - centre.Y) * cos - (pos.X - centre.X) * sin; - return Vector2Extensions.Transform(new Vector2(x, y), particleDrawMatrix); + return Vector2Extensions.Transform(new Vector2(x, y), DrawInfo.Matrix); } } From cfcb46034c51bc4a95186f4e304e8b84294706fa Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Fri, 10 Sep 2021 00:02:37 +0200 Subject: [PATCH 1877/2442] Remove `ParticleJet` --- .../Visual/Gameplay/TestSceneParticleJet.cs | 61 ------------ .../Gameplay/TestSceneParticleSpewer.cs | 94 +++++++++++++++++++ osu.Game/Graphics/Particles/ParticleJet.cs | 48 ---------- 3 files changed, 94 insertions(+), 109 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs delete mode 100644 osu.Game/Graphics/Particles/ParticleJet.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs deleted file mode 100644 index e570abcf88..0000000000 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Testing; -using osu.Game.Graphics.Particles; -using osu.Game.Skinning; - -namespace osu.Game.Tests.Visual.Gameplay -{ - [TestFixture] - public class TestSceneParticleJet : OsuTestScene - { - private ParticleJet jet; - - [Resolved] - private SkinManager skinManager { get; set; } - - public TestSceneParticleJet() - { - AddStep("create", () => - { - Child = jet = createJet(); - }); - - AddToggleStep("toggle spawning", value => jet.Active.Value = value); - } - - [SetUpSteps] - public void SetUpSteps() - { - AddStep("create jet", () => Child = jet = createJet()); - } - - [Test] - public void TestPresence() - { - AddStep("start jet", () => jet.Active.Value = true); - AddAssert("is present", () => jet.IsPresent); - - AddWaitStep("wait for some particles", 3); - AddStep("stop jet", () => jet.Active.Value = false); - - AddWaitStep("wait for clean screen", 5); - AddAssert("is not present", () => !jet.IsPresent); - } - - private ParticleJet createJet() - { - return new ParticleJet(skinManager.DefaultLegacySkin.GetTexture("star2"), 180) - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - RelativePositionAxes = Axes.Y, - Y = -0.1f, - }; - } - } -} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs new file mode 100644 index 0000000000..3a59374c98 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs @@ -0,0 +1,94 @@ +// 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 NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Textures; +using osu.Framework.Testing; +using osu.Framework.Utils; +using osu.Game.Graphics.Particles; +using osu.Game.Skinning; +using osuTK; + +namespace osu.Game.Tests.Visual.Gameplay +{ + [TestFixture] + public class TestSceneParticleSpewer : OsuTestScene + { + private TestParticleSpewer spewer; + + [Resolved] + private SkinManager skinManager { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + Child = spewer = createSpewer(); + + AddToggleStep("toggle spawning", value => spewer.Active.Value = value); + AddSliderStep("particle gravity", 0f, 250f, 0f, value => spewer.Gravity = value); + AddSliderStep("particle velocity", 0f, 500f, 250f, value => spewer.MaxVelocity = value); + } + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("create jet", () => Child = spewer = createSpewer()); + } + + [Test] + public void TestPresence() + { + AddStep("start jet", () => spewer.Active.Value = true); + AddAssert("is present", () => spewer.IsPresent); + + AddWaitStep("wait for some particles", 3); + AddStep("stop jet", () => spewer.Active.Value = false); + + AddWaitStep("wait for clean screen", 8); + AddAssert("is not present", () => !spewer.IsPresent); + } + + private TestParticleSpewer createSpewer() + { + return new TestParticleSpewer(skinManager.DefaultLegacySkin.GetTexture("star2")) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + } + + private class TestParticleSpewer : ParticleSpewer + { + private const int lifetime = 1500; + private const int rate = 250; + + public float Gravity = 0; + public float MaxVelocity = 250; + + protected override float ParticleGravity => Gravity; + + public TestParticleSpewer(Texture texture) + : base(texture, rate, lifetime) + { + } + + protected override FallingParticle SpawnParticle() + { + var p = base.SpawnParticle(); + p.Velocity = new Vector2( + RNG.NextSingle(-MaxVelocity, MaxVelocity), + RNG.NextSingle(-MaxVelocity, MaxVelocity) + ); + p.Duration = RNG.NextSingle(lifetime); + p.StartAngle = RNG.NextSingle(MathF.PI * 2); + p.EndAngle = RNG.NextSingle(MathF.PI * 2); + p.EndScale = RNG.NextSingle(0.5f, 1.5f); + + return p; + } + } + } +} diff --git a/osu.Game/Graphics/Particles/ParticleJet.cs b/osu.Game/Graphics/Particles/ParticleJet.cs deleted file mode 100644 index a76aa6b75f..0000000000 --- a/osu.Game/Graphics/Particles/ParticleJet.cs +++ /dev/null @@ -1,48 +0,0 @@ -// 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 osu.Framework.Graphics.Textures; -using osu.Framework.Utils; -using osuTK; - -namespace osu.Game.Graphics.Particles -{ - public class ParticleJet : ParticleSpewer - { - private const int particles_per_second = 80; - private const double particle_lifetime = 500; - private const int angle_spread = 10; - private const float velocity_min = 1300f; - private const float velocity_max = 1500f; - - private readonly int angle; - - protected override float ParticleGravity => 750f; - - public ParticleJet(Texture texture, int angle) - : base(texture, particles_per_second, particle_lifetime) - { - this.angle = angle; - } - - protected override FallingParticle SpawnParticle() - { - var p = base.SpawnParticle(); - - var directionRads = MathUtils.DegreesToRadians( - RNG.NextSingle(angle - angle_spread / 2, angle + angle_spread / 2) - ); - var direction = new Vector2(MathF.Sin(directionRads), MathF.Cos(directionRads)); - - p.StartPosition = OriginPosition; - p.Duration = RNG.NextSingle((float)particle_lifetime * 0.8f, (float)particle_lifetime); - p.Velocity = direction * new Vector2(RNG.NextSingle(velocity_min, velocity_max)); - p.StartAngle = RNG.NextSingle(-2f, 2f); - p.EndAngle = RNG.NextSingle(-2f, 2f); - p.EndScale = 2f; - - return p; - } - } -} From 8862d3fa1ea67d42c54dda8710206f036aed6436 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Fri, 10 Sep 2021 00:29:05 +0200 Subject: [PATCH 1878/2442] Add `OsuSkinComponents.CursorParticles` --- osu.Game.Rulesets.Osu/OsuSkinComponents.cs | 1 + .../Skinning/Legacy/OsuLegacySkinTransformer.cs | 3 +++ osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs | 7 +------ 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs index 46e501758b..484d1c6753 100644 --- a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs +++ b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs @@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Osu FollowPoint, Cursor, CursorTrail, + CursorParticles, SliderScorePoint, ReverseArrow, HitCircleText, diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index 41b0a88f11..0887e4d1d3 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -89,6 +89,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy return null; + case OsuSkinComponents.CursorParticles: + return new LegacyCursorStarParticles(); + case OsuSkinComponents.HitCircleText: if (!this.HasFont(LegacyFont.HitCircle)) return null; diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs index fbb7bfd7b1..463b1f4eaf 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs @@ -11,7 +11,6 @@ using osu.Framework.Input.Bindings; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets.Osu.Configuration; -using osu.Game.Rulesets.Osu.Skinning.Legacy; using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; using osu.Game.Skinning; @@ -46,11 +45,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor Children = new[] { cursorTrail = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.CursorTrail), _ => new DefaultCursorTrail(), confineMode: ConfineMode.NoScaling), - new LegacyCursorStarParticles() - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, + new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.CursorParticles), confineMode: ConfineMode.NoScaling), } }; } From 911282234e9a0944caec1af51c6c544e28927edf Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Fri, 10 Sep 2021 00:30:58 +0200 Subject: [PATCH 1879/2442] Rename legacy cursor particle classes --- ...orStarParticles.cs => LegacyCursorParticles.cs} | 14 +++++++------- .../Skinning/Legacy/OsuLegacySkinTransformer.cs | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) rename osu.Game.Rulesets.Osu/Skinning/Legacy/{LegacyCursorStarParticles.cs => LegacyCursorParticles.cs} (93%) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs similarity index 93% rename from osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs rename to osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index 89ffa4ca15..876af29e88 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -20,10 +20,10 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Skinning.Legacy { - public class LegacyCursorStarParticles : CompositeDrawable, IKeyBindingHandler + public class LegacyCursorParticles : CompositeDrawable, IKeyBindingHandler { - private StarParticleSpewer breakSpewer; - private StarParticleSpewer kiaiSpewer; + private LegacyCursorParticleSpewer breakSpewer; + private LegacyCursorParticleSpewer kiaiSpewer; [Resolved(canBeNull: true)] private Player player { get; set; } @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy InternalChildren = new[] { - breakSpewer = new StarParticleSpewer(texture, 20) + breakSpewer = new LegacyCursorParticleSpewer(texture, 20) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Value = false, } }, - kiaiSpewer = new StarParticleSpewer(texture, 60) + kiaiSpewer = new LegacyCursorParticleSpewer(texture, 60) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -121,7 +121,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy breakSpewer.Direction = SpewDirection.None; } - private class StarParticleSpewer : ParticleSpewer, IRequireHighFrequencyMousePosition + private class LegacyCursorParticleSpewer : ParticleSpewer, IRequireHighFrequencyMousePosition { private const int particle_lifetime_min = 300; private const int particle_lifetime_max = 1000; @@ -131,7 +131,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy protected override bool CanSpawnParticles => base.CanSpawnParticles && cursorScreenPosition.HasValue; protected override float ParticleGravity => 240; - public StarParticleSpewer(Texture texture, int perSecond) + public LegacyCursorParticleSpewer(Texture texture, int perSecond) : base(texture, perSecond, particle_lifetime_max) { Active.BindValueChanged(_ => resetVelocityCalculation()); diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index 0887e4d1d3..20c432b298 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy return null; case OsuSkinComponents.CursorParticles: - return new LegacyCursorStarParticles(); + return new LegacyCursorParticles(); case OsuSkinComponents.HitCircleText: if (!this.HasFont(LegacyFont.HitCircle)) From a688e69859262529d668ad6db5758d58d29aa8f6 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sat, 11 Sep 2021 01:55:16 +0200 Subject: [PATCH 1880/2442] Scale down cursor particles --- .../Skinning/Legacy/LegacyCursorParticles.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index 876af29e88..f8b5ab97df 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -35,6 +35,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private void load(ISkinSource skin, OsuColour colour) { var texture = skin.GetTexture("star2"); + if (texture == null) + return; + + texture.ScaleAdjust = 3.2f; InternalChildren = new[] { From 82d16ab394e747a333b47a9ac725d8152b94ce07 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 12 Sep 2021 23:45:50 +0200 Subject: [PATCH 1881/2442] Fix `LegacyCursorParticles` texture null reference --- .../Skinning/Legacy/OsuLegacySkinTransformer.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index 20c432b298..02a67ea44f 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -90,7 +90,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy return null; case OsuSkinComponents.CursorParticles: - return new LegacyCursorParticles(); + if (GetTexture("star2") != null) + return new LegacyCursorParticles(); + + return null; case OsuSkinComponents.HitCircleText: if (!this.HasFont(LegacyFont.HitCircle)) From 16f98357e621260b5a7e9107055e391e2c453a62 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 12 Sep 2021 23:53:41 +0200 Subject: [PATCH 1882/2442] Add cursor particles tests --- .../TestSceneCursorParticles.cs | 174 ++++++++++++++++++ .../Skinning/Legacy/LegacyCursorParticles.cs | 8 +- 2 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/TestSceneCursorParticles.cs diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneCursorParticles.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorParticles.cs new file mode 100644 index 0000000000..11b1f5b2af --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorParticles.cs @@ -0,0 +1,174 @@ +// 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.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.Skinning.Legacy; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Skinning; +using osuTK; +using osuTK.Input; + +namespace osu.Game.Rulesets.Osu.Tests +{ + public class TestSceneCursorParticles : TestSceneOsuPlayer + { + protected override bool Autoplay => autoplay; + protected override bool HasCustomSteps => true; + + private bool autoplay; + private IBeatmap currentBeatmap; + + [Resolved] + private SkinManager skinManager { get; set; } + + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => currentBeatmap ?? base.CreateBeatmap(ruleset); + + [Test] + public void TestLegacyBreakParticles() + { + LegacyCursorParticles cursorParticles = null; + + createLegacyTest(false, () => new Beatmap + { + Breaks = + { + new BreakPeriod(8500, 10000), + }, + HitObjects = + { + new HitCircle + { + StartTime = 8000, + Position = OsuPlayfield.BASE_SIZE / 2, + }, + new HitCircle + { + StartTime = 11000, + Position = OsuPlayfield.BASE_SIZE / 2, + }, + } + }); + + AddUntilStep("fetch cursor particles", () => + { + cursorParticles = this.ChildrenOfType().SingleOrDefault(); + return cursorParticles != null; + }); + + AddStep("move mouse to centre", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.Centre)); + + AddAssert("particles are being spawned", () => cursorParticles.Active); + + AddStep("press left mouse button", () => InputManager.PressButton(MouseButton.Left)); + AddWaitStep("wait a bit", 5); + AddStep("press right mouse button", () => InputManager.PressButton(MouseButton.Right)); + AddWaitStep("wait a bit", 5); + AddStep("release left mouse button", () => InputManager.ReleaseButton(MouseButton.Left)); + AddWaitStep("wait a bit", 5); + AddStep("release right mouse button", () => InputManager.ReleaseButton(MouseButton.Right)); + + AddUntilStep("wait for beatmap start", () => !Player.IsBreakTime.Value); + AddAssert("particle spawning stopped", () => !cursorParticles.Active); + + AddUntilStep("wait for break", () => Player.IsBreakTime.Value); + AddAssert("particles are being spawned", () => cursorParticles.Active); + + AddUntilStep("wait for break end", () => !Player.IsBreakTime.Value); + AddAssert("particle spawning stopped", () => !cursorParticles.Active); + } + + [Test] + public void TestLegacyKiaiParticles() + { + LegacyCursorParticles cursorParticles = null; + DrawableSpinner spinner = null; + DrawableSlider slider = null; + + createLegacyTest(true, () => + { + var controlPointInfo = new ControlPointInfo(); + controlPointInfo.Add(0, new EffectControlPoint { KiaiMode = true }); + + return new Beatmap + { + ControlPointInfo = controlPointInfo, + HitObjects = + { + new Spinner + { + StartTime = 0, + Duration = 1000, + Position = OsuPlayfield.BASE_SIZE / 2, + }, + new Slider + { + StartTime = 2500, + RepeatCount = 0, + Position = OsuPlayfield.BASE_SIZE / 2, + Path = new SliderPath(new[] + { + new PathControlPoint(Vector2.Zero), + new PathControlPoint(new Vector2(100, 0)), + }) + }, + new HitCircle + { + StartTime = 4500, + Position = OsuPlayfield.BASE_SIZE / 2, + }, + }, + }; + } + ); + + AddUntilStep("fetch cursor particles", () => + { + cursorParticles = this.ChildrenOfType().SingleOrDefault(); + return cursorParticles != null; + }); + + AddUntilStep("wait for spinner tracking", () => + { + spinner = this.ChildrenOfType().SingleOrDefault(); + return spinner?.RotationTracker.Tracking == true; + }); + AddAssert("particles are being spawned", () => cursorParticles.Active); + + AddUntilStep("spinner tracking stopped", () => !spinner.RotationTracker.Tracking); + AddAssert("particle spawning stopped", () => !cursorParticles.Active); + + AddUntilStep("wait for slider tracking", () => + { + slider = this.ChildrenOfType().SingleOrDefault(); + return slider?.Tracking.Value == true; + }); + AddAssert("particles are being spawned", () => cursorParticles.Active); + + AddUntilStep("slider tracking stopped", () => !slider.Tracking.Value); + AddAssert("particle spawning stopped", () => !cursorParticles.Active); + } + + private void createLegacyTest(bool autoplay, Func beatmap) => CreateTest(() => + { + AddStep("set beatmap", () => + { + this.autoplay = autoplay; + currentBeatmap = beatmap(); + }); + AddStep("setup default legacy skin", () => + { + skinManager.CurrentSkinInfo.Value = skinManager.DefaultLegacySkin.SkinInfo; + }); + }); + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index f8b5ab97df..2080b1a3ce 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { public class LegacyCursorParticles : CompositeDrawable, IKeyBindingHandler { + public bool Active => breakSpewer?.Active.Value == true || kiaiSpewer?.Active.Value == true; + private LegacyCursorParticleSpewer breakSpewer; private LegacyCursorParticleSpewer kiaiSpewer; @@ -32,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private OsuPlayfield osuPlayfield { get; set; } [BackgroundDependencyLoader] - private void load(ISkinSource skin, OsuColour colour) + private void load(ISkinSource skin, OsuColour colours) { var texture = skin.GetTexture("star2"); if (texture == null) @@ -46,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Colour = colour.PinkLighter, + Colour = colours.PinkLighter, Direction = SpewDirection.None, Active = { @@ -57,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Colour = colour.PinkLighter, + Colour = colours.PinkLighter, Direction = SpewDirection.None, Active = { From 7327603fc834de3cbb32b29e5a511d1df8061107 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Mon, 13 Sep 2021 00:05:49 +0200 Subject: [PATCH 1883/2442] Fix outdated test step description --- osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs index 3a59374c98..e58bbb737e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs @@ -35,17 +35,17 @@ namespace osu.Game.Tests.Visual.Gameplay [SetUpSteps] public void SetUpSteps() { - AddStep("create jet", () => Child = spewer = createSpewer()); + AddStep("create spewer", () => Child = spewer = createSpewer()); } [Test] public void TestPresence() { - AddStep("start jet", () => spewer.Active.Value = true); + AddStep("start spewer", () => spewer.Active.Value = true); AddAssert("is present", () => spewer.IsPresent); AddWaitStep("wait for some particles", 3); - AddStep("stop jet", () => spewer.Active.Value = false); + AddStep("stop spewer", () => spewer.Active.Value = false); AddWaitStep("wait for clean screen", 8); AddAssert("is not present", () => !spewer.IsPresent); From 224244801f35ed2b031c9be907cd891181b5da97 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Mon, 13 Sep 2021 00:35:13 +0200 Subject: [PATCH 1884/2442] Remove Particles namespace --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs | 1 - osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs | 2 +- osu.Game/Graphics/{Particles => }/ParticleSpewer.cs | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) rename osu.Game/Graphics/{Particles => }/ParticleSpewer.cs (99%) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index 2080b1a3ce..95c2a6b930 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -11,7 +11,6 @@ using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Utils; using osu.Game.Graphics; -using osu.Game.Graphics.Particles; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.UI; using osu.Game.Screens.Play; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs index e58bbb737e..fe14869e9a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Framework.Testing; using osu.Framework.Utils; -using osu.Game.Graphics.Particles; +using osu.Game.Graphics; using osu.Game.Skinning; using osuTK; diff --git a/osu.Game/Graphics/Particles/ParticleSpewer.cs b/osu.Game/Graphics/ParticleSpewer.cs similarity index 99% rename from osu.Game/Graphics/Particles/ParticleSpewer.cs rename to osu.Game/Graphics/ParticleSpewer.cs index f0c9dff239..544b2bd2ed 100644 --- a/osu.Game/Graphics/Particles/ParticleSpewer.cs +++ b/osu.Game/Graphics/ParticleSpewer.cs @@ -10,7 +10,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osuTK; -namespace osu.Game.Graphics.Particles +namespace osu.Game.Graphics { public abstract class ParticleSpewer : Sprite { From b009772206a4ffeebb2b7d36dad890ec655c2ff6 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Mon, 13 Sep 2021 00:45:36 +0200 Subject: [PATCH 1885/2442] Fix code inspect failure --- osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs index fe14869e9a..e6b5763f2c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs @@ -65,7 +65,7 @@ namespace osu.Game.Tests.Visual.Gameplay private const int lifetime = 1500; private const int rate = 250; - public float Gravity = 0; + public float Gravity; public float MaxVelocity = 250; protected override float ParticleGravity => Gravity; From 9fd616c578107ab375fa102ab313a6d5f40a34cf Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Tue, 14 Sep 2021 00:16:42 +0200 Subject: [PATCH 1886/2442] Tiny refactor --- .../Skinning/Legacy/LegacyCursorParticles.cs | 4 ++-- osu.Game/Graphics/ParticleSpewer.cs | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index 95c2a6b930..8dc486285b 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -149,6 +149,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private double velocityFrameLength; private Vector2 totalPosDifference; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; + protected override bool OnMouseMove(MouseMoveEvent e) { if (cursorScreenPosition == null) @@ -174,8 +176,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy return base.OnMouseMove(e); } - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; - private void resetVelocityCalculation() { cursorScreenPosition = null; diff --git a/osu.Game/Graphics/ParticleSpewer.cs b/osu.Game/Graphics/ParticleSpewer.cs index 544b2bd2ed..8b82dfb7c6 100644 --- a/osu.Game/Graphics/ParticleSpewer.cs +++ b/osu.Game/Graphics/ParticleSpewer.cs @@ -132,10 +132,10 @@ namespace osu.Game.Graphics height); var quad = new Quad( - rotatePosition(rect.TopLeft, rect.Centre, angle), - rotatePosition(rect.TopRight, rect.Centre, angle), - rotatePosition(rect.BottomLeft, rect.Centre, angle), - rotatePosition(rect.BottomRight, rect.Centre, angle) + transformPosition(rect.TopLeft, rect.Centre, angle), + transformPosition(rect.TopRight, rect.Centre, angle), + transformPosition(rect.BottomLeft, rect.Centre, angle), + transformPosition(rect.BottomRight, rect.Centre, angle) ); DrawQuad(Texture, quad, DrawColourInfo.Colour.MultiplyAlpha(alpha), null, vertexAction, @@ -144,7 +144,7 @@ namespace osu.Game.Graphics } } - private Vector2 rotatePosition(Vector2 pos, Vector2 centre, float angle) + private Vector2 transformPosition(Vector2 pos, Vector2 centre, float angle) { float cos = MathF.Cos(angle); float sin = MathF.Sin(angle); From c4886be7e1132ed189dbc400db3930cb3c218b11 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Tue, 14 Sep 2021 00:36:01 +0200 Subject: [PATCH 1887/2442] Add `StarBreakAdditive` config support --- .../Skinning/Legacy/LegacyCursorParticles.cs | 7 +++++-- osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index 8dc486285b..a2ee9afff1 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -16,6 +16,7 @@ using osu.Game.Rulesets.Osu.UI; using osu.Game.Screens.Play; using osu.Game.Skinning; using osuTK; +using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Skinning.Legacy { @@ -36,6 +37,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private void load(ISkinSource skin, OsuColour colours) { var texture = skin.GetTexture("star2"); + var starBreakAdditive = skin.GetConfig(OsuSkinColour.StarBreakAdditive)?.Value ?? new Color4(255, 182, 193, 255); + if (texture == null) return; @@ -47,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Colour = colours.PinkLighter, + Colour = starBreakAdditive, Direction = SpewDirection.None, Active = { @@ -58,7 +61,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Colour = colours.PinkLighter, + Colour = starBreakAdditive, Direction = SpewDirection.None, Active = { diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs b/osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs index f7ba8b9fc4..24f9217a5f 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs @@ -9,5 +9,6 @@ namespace osu.Game.Rulesets.Osu.Skinning SliderBorder, SliderBall, SpinnerBackground, + StarBreakAdditive, } } From d13ff12a3e4cb0218f1aa1560b9439354aea0353 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Tue, 14 Sep 2021 00:36:52 +0200 Subject: [PATCH 1888/2442] Remove hardcoded particle scale --- .../Skinning/Legacy/LegacyCursorParticles.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index a2ee9afff1..ccccd1810c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -39,11 +39,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy var texture = skin.GetTexture("star2"); var starBreakAdditive = skin.GetConfig(OsuSkinColour.StarBreakAdditive)?.Value ?? new Color4(255, 182, 193, 255); - if (texture == null) - return; - - texture.ScaleAdjust = 3.2f; - InternalChildren = new[] { breakSpewer = new LegacyCursorParticleSpewer(texture, 20) From 178a3d1132801dbe0d0e6725def89622cc48932d Mon Sep 17 00:00:00 2001 From: MBmasher Date: Tue, 14 Sep 2021 10:23:11 +1000 Subject: [PATCH 1889/2442] Remove redundant check --- .../Difficulty/Skills/Flashlight.cs | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index f6760235b4..abd900a80d 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -34,33 +34,29 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double scalingFactor = 52.0 / osuHitObject.Radius; double smallDistNerf = 1.0; + double cumulativeStrainTime = 0.0; double result = 0.0; - if (Previous.Count > 0) + for (int i = 0; i < Previous.Count; i++) { - double cumulativeStrainTime = 0.0; + var osuPrevious = (OsuDifficultyHitObject)Previous[i]; + var osuPreviousHitObject = (OsuHitObject)(osuPrevious.BaseObject); - for (int i = 0; i < Previous.Count; i++) + if (!(osuPrevious.BaseObject is Spinner)) { - var osuPrevious = (OsuDifficultyHitObject)Previous[i]; - var osuPreviousHitObject = (OsuHitObject)(osuPrevious.BaseObject); + double jumpDistance = (osuHitObject.StackedPosition - osuPreviousHitObject.EndPosition).Length; - if (!(osuPrevious.BaseObject is Spinner)) - { - double jumpDistance = (osuHitObject.StackedPosition - osuPreviousHitObject.EndPosition).Length; + cumulativeStrainTime += osuPrevious.StrainTime; - cumulativeStrainTime += osuPrevious.StrainTime; + // We want to nerf objects that can be easily seen within the Flashlight circle radius. + if (i == 0) + smallDistNerf = Math.Min(1.0, jumpDistance / 75.0); - // We want to nerf objects that can be easily seen within the Flashlight circle radius. - if (i == 0) - smallDistNerf = Math.Min(1.0, jumpDistance / 75.0); + // We also want to nerf stacks so that only the first object of the stack is accounted for. + double stackNerf = Math.Min(1.0, (osuPrevious.JumpDistance / scalingFactor) / 25.0); - // We also want to nerf stacks so that only the first object of the stack is accounted for. - double stackNerf = Math.Min(1.0, (osuPrevious.JumpDistance / scalingFactor) / 25.0); - - result += Math.Pow(0.8, i) * stackNerf * scalingFactor * jumpDistance / cumulativeStrainTime; - } + result += Math.Pow(0.8, i) * stackNerf * scalingFactor * jumpDistance / cumulativeStrainTime; } } From c4fbae136a8c4e53785227a30cd9d92323d06376 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Tue, 14 Sep 2021 10:34:21 +1000 Subject: [PATCH 1890/2442] Rename FlashlightStrain to FlashlightRating --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs | 2 +- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 2 +- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index 1e870dac68..ac77a93239 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty { public double AimStrain { get; set; } public double SpeedStrain { get; set; } - public double FlashlightStrain { get; set; } + public double FlashlightRating { get; set; } public double ApproachRate { get; set; } public double OverallDifficulty { get; set; } public int HitCircleCount { get; set; } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index b0dd4dc9b0..28b2174071 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty Mods = mods, AimStrain = aimRating, SpeedStrain = speedRating, - FlashlightStrain = flashlightRating, + FlashlightRating = flashlightRating, ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5, OverallDifficulty = (80 - hitWindowGreat) / 6, MaxCombo = maxCombo, diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index f9a3423eab..bf4d92652c 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -193,7 +193,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (!mods.Any(h => h is OsuModFlashlight)) return 0.0; - double rawFlashlight = Attributes.FlashlightStrain; + double rawFlashlight = Attributes.FlashlightRating; if (mods.Any(m => m is OsuModTouchDevice)) rawFlashlight = Math.Pow(rawFlashlight, 0.8); From 3df4cbca2cfd09d81226b530f96fba6a5588e2a9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Sep 2021 13:45:10 +0900 Subject: [PATCH 1891/2442] Reduce precision of difficulty calculator tests --- osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs index 76f229a799..fdb3e1d465 100644 --- a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs +++ b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs @@ -23,7 +23,10 @@ namespace osu.Game.Tests.Beatmaps protected abstract string ResourceAssembly { get; } protected void Test(double expected, string name, params Mod[] mods) - => Assert.AreEqual(expected, CreateDifficultyCalculator(getBeatmap(name)).Calculate(mods).StarRating); + { + // Platform-dependent math functions (Pow, Cbrt, Exp, etc) may result in minute differences. + Assert.That(CreateDifficultyCalculator(getBeatmap(name)).Calculate(mods).StarRating, Is.EqualTo(expected).Within(0.00001)); + } private WorkingBeatmap getBeatmap(string name) { From eaac2bad3d31dce24d3e6a6fad03a4ff8b849e85 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Sep 2021 13:49:02 +0900 Subject: [PATCH 1892/2442] Fix incorrect child margin specifications --- osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index bec0ea419c..2fc039ad79 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -193,13 +193,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge [BackgroundDependencyLoader] private void load() { - shakeContainer = new ShakeContainer + Child = shakeContainer = new ShakeContainer { Margin = new MarginPadding(10), AutoSizeAxes = Axes.Both, Child = new FillFlowContainer { - Margin = new MarginPadding(10), Spacing = new Vector2(5), AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, @@ -218,7 +217,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge } } }; - Child = shakeContainer; joinButton.Action = () => lounge?.Join(room, passwordTextbox.Text, null, joinFailed); } From 6851e0000de64ca7bd41339eedcda4f6d3c7c99f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Sep 2021 13:51:18 +0900 Subject: [PATCH 1893/2442] Add test coverage --- .../TestSceneMultiplayerLoungeSubScreen.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs index 99f6ab1ae1..01bf742177 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs @@ -3,6 +3,7 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Framework.Screens; using osu.Framework.Testing; @@ -64,7 +65,23 @@ namespace osu.Game.Tests.Visual.Multiplayer } [Test] - public void TestJoinRoomWithPassword() + public void TestJoinRoomWithIncorrectPassword() + { + DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null; + + AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true)); + AddStep("select room", () => InputManager.Key(Key.Down)); + AddStep("attempt join room", () => InputManager.Key(Key.Enter)); + AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null); + AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "wrong"); + AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().TriggerClick()); + + AddAssert("room not joined", () => loungeScreen.IsCurrentScreen()); + AddUntilStep("password prompt still visible", () => passwordEntryPopover.State.Value == Visibility.Visible); + } + + [Test] + public void TestJoinRoomWithCorrectPassword() { DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null; From e3c56f9ebde94c87276af777164f61581142732a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Sep 2021 14:10:55 +0900 Subject: [PATCH 1894/2442] Show error message in popover --- .../OnlinePlay/Components/RoomManager.cs | 8 ++-- .../OnlinePlay/Lounge/DrawableLoungeRoom.cs | 37 ++++++++++++++----- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs index 4b3617c74a..a64d89b699 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs @@ -87,10 +87,10 @@ namespace osu.Game.Screens.OnlinePlay.Components currentJoinRoomRequest.Failure += exception => { - // provide error output if the operation wasn't canceled and the error doesn't stem from an invalid password - if (!(exception is OperationCanceledException) && !((APIException)exception).Message.Equals("Invalid room password", StringComparison.Ordinal)) - Logger.Log($"Failed to join room: {exception}", level: LogLevel.Important); - onError?.Invoke(exception.ToString()); + if (exception is OperationCanceledException) + return; + + onError?.Invoke(exception.Message); }; api.Queue(currentJoinRoomRequest); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index 2fc039ad79..0dec9b72bf 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -14,7 +14,9 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Input.Bindings; @@ -189,9 +191,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private OsuPasswordTextBox passwordTextbox; private TriangleButton joinButton; private ShakeContainer shakeContainer; + private OsuSpriteText errorText; [BackgroundDependencyLoader] - private void load() + private void load(OsuColour colours) { Child = shakeContainer = new ShakeContainer { @@ -201,19 +204,32 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { Spacing = new Vector2(5), AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, + Direction = FillDirection.Vertical, Children = new Drawable[] { - passwordTextbox = new OsuPasswordTextBox + new FillFlowContainer { - Width = 200, - PlaceholderText = "password", + Direction = FillDirection.Horizontal, + Spacing = new Vector2(5), + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + passwordTextbox = new OsuPasswordTextBox + { + Width = 200, + PlaceholderText = "password", + }, + joinButton = new TriangleButton + { + Width = 80, + Text = "Join Room", + } + } }, - joinButton = new TriangleButton + errorText = new OsuSpriteText { - Width = 80, - Text = "Join Room", - } + Colour = colours.Red, + }, } } }; @@ -225,6 +241,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { passwordTextbox.Text = string.Empty; + errorText.Text = error; + errorText.FadeOutFromOne(1000, Easing.In); + shakeContainer.Shake(); } From 7bd749d0eb8bdda4997a66531691cc4f052b8481 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Sep 2021 14:19:55 +0900 Subject: [PATCH 1895/2442] Remove weird shaking --- .../OnlinePlay/Lounge/DrawableLoungeRoom.cs | 56 +++++++++---------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index 0dec9b72bf..a67389e4c0 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -15,7 +15,6 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; @@ -190,47 +189,44 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private OsuPasswordTextBox passwordTextbox; private TriangleButton joinButton; - private ShakeContainer shakeContainer; private OsuSpriteText errorText; [BackgroundDependencyLoader] private void load(OsuColour colours) { - Child = shakeContainer = new ShakeContainer + Padding = new MarginPadding(10); + + Child = new FillFlowContainer { Margin = new MarginPadding(10), + Spacing = new Vector2(5), AutoSizeAxes = Axes.Both, - Child = new FillFlowContainer + Direction = FillDirection.Vertical, + Children = new Drawable[] { - Spacing = new Vector2(5), - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new Drawable[] + new FillFlowContainer { - new FillFlowContainer + Direction = FillDirection.Horizontal, + Spacing = new Vector2(5), + AutoSizeAxes = Axes.Both, + Children = new Drawable[] { - Direction = FillDirection.Horizontal, - Spacing = new Vector2(5), - AutoSizeAxes = Axes.Both, - Children = new Drawable[] + passwordTextbox = new OsuPasswordTextBox { - passwordTextbox = new OsuPasswordTextBox - { - Width = 200, - PlaceholderText = "password", - }, - joinButton = new TriangleButton - { - Width = 80, - Text = "Join Room", - } + Width = 200, + PlaceholderText = "password", + }, + joinButton = new TriangleButton + { + Width = 80, + Text = "Join Room", } - }, - errorText = new OsuSpriteText - { - Colour = colours.Red, - }, - } + } + }, + errorText = new OsuSpriteText + { + Colour = colours.Red, + }, } }; @@ -243,8 +239,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge errorText.Text = error; errorText.FadeOutFromOne(1000, Easing.In); - - shakeContainer.Shake(); } protected override void LoadComplete() From e17b800470d18095e079699795fe475fbd0c18e6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Sep 2021 14:44:23 +0900 Subject: [PATCH 1896/2442] Move shake logic into extension method --- osu.Game/Extensions/DrawableExtensions.cs | 27 +++++++++++++++ .../Graphics/Containers/ShakeContainer.cs | 34 ++----------------- 2 files changed, 29 insertions(+), 32 deletions(-) diff --git a/osu.Game/Extensions/DrawableExtensions.cs b/osu.Game/Extensions/DrawableExtensions.cs index 52d8230fb6..b05a2994ea 100644 --- a/osu.Game/Extensions/DrawableExtensions.cs +++ b/osu.Game/Extensions/DrawableExtensions.cs @@ -38,6 +38,33 @@ namespace osu.Game.Extensions return repeatDelegate; } + /// + /// Shake the contents of this container. + /// + /// The target to shake. + /// The length of a single shake. + /// Pixels of displacement per shake. + /// The maximum length the shake should last. + public static void Shake(this Drawable target, double shakeDuration = 80, float shakeMagnitude = 8, double? maximumLength = null) + { + // if we don't have enough time, don't bother shaking. + if (maximumLength < shakeDuration * 2) + return; + + var sequence = target.MoveToX(shakeMagnitude, shakeDuration / 2, Easing.OutSine).Then() + .MoveToX(-shakeMagnitude, shakeDuration, Easing.InOutSine).Then(); + + // if we don't have enough time for the second shake, skip it. + if (!maximumLength.HasValue || maximumLength >= shakeDuration * 4) + { + sequence = sequence + .MoveToX(shakeMagnitude, shakeDuration, Easing.InOutSine).Then() + .MoveToX(-shakeMagnitude, shakeDuration, Easing.InOutSine).Then(); + } + + sequence.MoveToX(0, shakeDuration / 2, Easing.InSine); + } + /// /// Accepts a delta vector in screen-space coordinates and converts it to one which can be applied to this drawable's position. /// diff --git a/osu.Game/Graphics/Containers/ShakeContainer.cs b/osu.Game/Graphics/Containers/ShakeContainer.cs index dca9df1e98..8a0ce287db 100644 --- a/osu.Game/Graphics/Containers/ShakeContainer.cs +++ b/osu.Game/Graphics/Containers/ShakeContainer.cs @@ -1,8 +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 osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Extensions; namespace osu.Game.Graphics.Containers { @@ -16,40 +16,10 @@ namespace osu.Game.Graphics.Containers /// public float ShakeDuration = 80; - /// - /// Total number of shakes. May be shortened if possible. - /// - public float TotalShakes = 4; - - /// - /// Pixels of displacement per shake. - /// - public float ShakeMagnitude = 8; - /// /// Shake the contents of this container. /// /// The maximum length the shake should last. - public void Shake(double? maximumLength = null) - { - const float shake_amount = 8; - - // if we don't have enough time, don't bother shaking. - if (maximumLength < ShakeDuration * 2) - return; - - var sequence = this.MoveToX(shake_amount, ShakeDuration / 2, Easing.OutSine).Then() - .MoveToX(-shake_amount, ShakeDuration, Easing.InOutSine).Then(); - - // if we don't have enough time for the second shake, skip it. - if (!maximumLength.HasValue || maximumLength >= ShakeDuration * 4) - { - sequence = sequence - .MoveToX(shake_amount, ShakeDuration, Easing.InOutSine).Then() - .MoveToX(-shake_amount, ShakeDuration, Easing.InOutSine).Then(); - } - - sequence.MoveToX(0, ShakeDuration / 2, Easing.InSine); - } + public void Shake(double? maximumLength = null) => this.Shake(ShakeDuration, maximumLength: maximumLength); } } From 8865e3cab8abdde1164f1d4cadea2a8a1dac1cb9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Sep 2021 14:44:32 +0900 Subject: [PATCH 1897/2442] Add back shake and tweak transform of text --- osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index a67389e4c0..11c3cfe06a 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -238,7 +239,13 @@ namespace osu.Game.Screens.OnlinePlay.Lounge passwordTextbox.Text = string.Empty; errorText.Text = error; - errorText.FadeOutFromOne(1000, Easing.In); + errorText + .FadeIn() + .FlashColour(Color4.White, 200) + .Delay(1000) + .FadeOutFromOne(1000, Easing.In); + + Body.Shake(); } protected override void LoadComplete() From 5058f285048d9ac7354f02d8514203321584c44f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Sep 2021 14:52:50 +0900 Subject: [PATCH 1898/2442] Remove breaking padding --- osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index 11c3cfe06a..310df0b91d 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -195,8 +195,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge [BackgroundDependencyLoader] private void load(OsuColour colours) { - Padding = new MarginPadding(10); - Child = new FillFlowContainer { Margin = new MarginPadding(10), From 67750e6e1a1e0b99091c0f893fd129972df6d4a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Sep 2021 15:08:43 +0900 Subject: [PATCH 1899/2442] Fix subsequent navigation tests failing due to escape key not being released --- osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 2c416ee758..aeb800f58a 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -418,6 +418,7 @@ namespace osu.Game.Tests.Visual.Navigation AddStep("Hold escape", () => InputManager.PressKey(Key.Escape)); AddUntilStep("Wait for intro", () => Game.ScreenStack.CurrentScreen is IntroTriangles); + AddStep("Release escape", () => InputManager.ReleaseKey(Key.Escape)); AddUntilStep("Wait for game exit", () => Game.ScreenStack.CurrentScreen == null); AddStep("test dispose doesn't crash", () => Game.Dispose()); } From 6cffbee59256e0bca56d424f5b0f77605d3f195a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Sep 2021 17:22:58 +0900 Subject: [PATCH 1900/2442] Fix random/target mods not working in spectator --- osu.Game/Online/Spectator/SpectatorClient.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index 2546374b21..494739797a 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -153,9 +153,9 @@ namespace osu.Game.Online.Spectator IsPlaying = true; // transfer state at point of beginning play - currentState.BeatmapID = beatmap.BeatmapInfo.OnlineBeatmapID; - currentState.RulesetID = currentRuleset.Value.ID; - currentState.Mods = currentMods.Value.Select(m => new APIMod(m)); + currentState.BeatmapID = score.ScoreInfo.Beatmap.OnlineBeatmapID; + currentState.RulesetID = score.ScoreInfo.RulesetID; + currentState.Mods = score.ScoreInfo.Mods.Select(m => new APIMod(m)).ToArray(); currentBeatmap = beatmap.PlayableBeatmap; currentScore = score; From b807c161b4907633304322a9525f5569d9bc18d8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Sep 2021 17:23:45 +0900 Subject: [PATCH 1901/2442] Remove now-unused DI params --- osu.Game/Online/Spectator/SpectatorClient.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index 494739797a..8c617784b9 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -15,8 +15,6 @@ using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Replays.Legacy; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays.Types; using osu.Game.Scoring; @@ -46,15 +44,8 @@ namespace osu.Game.Online.Spectator private readonly BindableDictionary playingUserStates = new BindableDictionary(); private IBeatmap? currentBeatmap; - private Score? currentScore; - [Resolved] - private IBindable currentRuleset { get; set; } = null!; - - [Resolved] - private IBindable> currentMods { get; set; } = null!; - private readonly SpectatorState currentState = new SpectatorState(); /// From 63aa3ddcba2ba2d1681c67e1d510ce245a079ef8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Sep 2021 17:45:23 +0900 Subject: [PATCH 1902/2442] Add animation support for mania notes --- .../Legacy/LegacyHoldNoteHeadPiece.cs | 8 +++--- .../Legacy/LegacyHoldNoteTailPiece.cs | 10 +++---- .../Skinning/Legacy/LegacyNotePiece.cs | 26 +++++++++++++------ osu.Game/Skinning/LegacySkinExtensions.cs | 3 +++ 4 files changed, 30 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHoldNoteHeadPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHoldNoteHeadPiece.cs index 21e5bdd5d6..1e75533442 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHoldNoteHeadPiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHoldNoteHeadPiece.cs @@ -1,18 +1,18 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics; using osu.Game.Skinning; namespace osu.Game.Rulesets.Mania.Skinning.Legacy { public class LegacyHoldNoteHeadPiece : LegacyNotePiece { - protected override Texture GetTexture(ISkinSource skin) + protected override Drawable GetAnimation(ISkinSource skin) { // TODO: Should fallback to the head from default legacy skin instead of note. - return GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteHeadImage) - ?? GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.NoteImage); + return GetAnimationFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteHeadImage) + ?? GetAnimationFromLookup(skin, LegacyManiaSkinConfigurationLookups.NoteImage); } } } diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHoldNoteTailPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHoldNoteTailPiece.cs index 232b47ae27..e6d4291d79 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHoldNoteTailPiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyHoldNoteTailPiece.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; -using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Skinning; @@ -18,12 +18,12 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy : new ValueChangedEvent(ScrollingDirection.Up, ScrollingDirection.Up)); } - protected override Texture GetTexture(ISkinSource skin) + protected override Drawable GetAnimation(ISkinSource skin) { // TODO: Should fallback to the head from default legacy skin instead of note. - return GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteTailImage) - ?? GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteHeadImage) - ?? GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.NoteImage); + return GetAnimationFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteTailImage) + ?? GetAnimationFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteHeadImage) + ?? GetAnimationFromLookup(skin, LegacyManiaSkinConfigurationLookups.NoteImage); } } } diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyNotePiece.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyNotePiece.cs index 31279796ce..e8ff1c23f3 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyNotePiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyNotePiece.cs @@ -1,9 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Sprites; @@ -19,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy private readonly IBindable direction = new Bindable(); private Container directionContainer; - private Sprite noteSprite; + private Drawable noteAnimation; private float? minimumColumnWidth; @@ -39,7 +41,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy Origin = Anchor.BottomCentre, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Child = noteSprite = new Sprite { Texture = GetTexture(skin) } + Child = noteAnimation = GetAnimation(skin) }; direction.BindTo(scrollingInfo.Direction); @@ -50,12 +52,18 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy { base.Update(); - if (noteSprite.Texture != null) + Texture texture = null; + + if (noteAnimation is Sprite sprite) + texture = sprite.Texture; + else if (noteAnimation is TextureAnimation textureAnimation) + texture = textureAnimation.CurrentFrame; + + if (texture != null) { // The height is scaled to the minimum column width, if provided. float minimumWidth = minimumColumnWidth ?? DrawWidth; - - noteSprite.Scale = Vector2.Divide(new Vector2(DrawWidth, minimumWidth), noteSprite.Texture.DisplayWidth); + noteAnimation.Scale = Vector2.Divide(new Vector2(DrawWidth, minimumWidth), texture.DisplayWidth); } } @@ -73,9 +81,11 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy } } - protected virtual Texture GetTexture(ISkinSource skin) => GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.NoteImage); + [CanBeNull] + protected virtual Drawable GetAnimation(ISkinSource skin) => GetAnimationFromLookup(skin, LegacyManiaSkinConfigurationLookups.NoteImage); - protected Texture GetTextureFromLookup(ISkin skin, LegacyManiaSkinConfigurationLookups lookup) + [CanBeNull] + protected Drawable GetAnimationFromLookup(ISkin skin, LegacyManiaSkinConfigurationLookups lookup) { string suffix = string.Empty; @@ -93,7 +103,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy string noteImage = GetColumnSkinConfig(skin, lookup)?.Value ?? $"mania-note{FallbackColumnIndex}{suffix}"; - return skin.GetTexture(noteImage, WrapMode.ClampToEdge, WrapMode.ClampToEdge); + return skin.GetAnimation(noteImage, WrapMode.ClampToEdge, WrapMode.ClampToEdge, true, true); } } } diff --git a/osu.Game/Skinning/LegacySkinExtensions.cs b/osu.Game/Skinning/LegacySkinExtensions.cs index ec25268be4..fd1f905868 100644 --- a/osu.Game/Skinning/LegacySkinExtensions.cs +++ b/osu.Game/Skinning/LegacySkinExtensions.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -17,10 +18,12 @@ namespace osu.Game.Skinning { public static class LegacySkinExtensions { + [CanBeNull] public static Drawable GetAnimation(this ISkin source, string componentName, bool animatable, bool looping, bool applyConfigFrameRate = false, string animationSeparator = "-", bool startAtCurrentTime = true, double? frameLength = null) => source.GetAnimation(componentName, default, default, animatable, looping, applyConfigFrameRate, animationSeparator, startAtCurrentTime, frameLength); + [CanBeNull] public static Drawable GetAnimation(this ISkin source, string componentName, WrapMode wrapModeS, WrapMode wrapModeT, bool animatable, bool looping, bool applyConfigFrameRate = false, string animationSeparator = "-", bool startAtCurrentTime = true, double? frameLength = null) From c009e1473ddf319d2229a03bcab7e80e55356ad3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Sep 2021 17:47:12 +0900 Subject: [PATCH 1903/2442] Add extra safety check --- osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyNotePiece.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyNotePiece.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyNotePiece.cs index e8ff1c23f3..bbc7520bac 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyNotePiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyNotePiece.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy if (noteAnimation is Sprite sprite) texture = sprite.Texture; - else if (noteAnimation is TextureAnimation textureAnimation) + else if (noteAnimation is TextureAnimation textureAnimation && textureAnimation.FrameCount > 0) texture = textureAnimation.CurrentFrame; if (texture != null) From a7759153385c7d7d9bfa78e0b5b060e994d2d60f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Sep 2021 18:20:28 +0900 Subject: [PATCH 1904/2442] Fix incorrect beatmap count and SR range in multi lounge --- .../Screens/OnlinePlay/Components/ListingPollingComponent.cs | 5 +++++ .../OnlinePlay/Components/SelectionPollingComponent.cs | 3 +++ 2 files changed, 8 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs index daac6a66cd..5a74df6ab1 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs @@ -57,7 +57,12 @@ namespace osu.Game.Screens.OnlinePlay.Components } foreach (var incoming in result) + { + // Copy the room to itself to populate some members (such as status and playlist expiry). + incoming.CopyFrom(incoming); + RoomManager.AddOrUpdateRoom(incoming); + } initialRoomsReceived.Value = true; tcs.SetResult(true); diff --git a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs index 0769d0747b..0a37d47e34 100644 --- a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs +++ b/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs @@ -39,6 +39,9 @@ namespace osu.Game.Screens.OnlinePlay.Components pollReq.Success += result => { + // Copy the room to itself to populate some members (such as status and playlist expiry). + result.CopyFrom(result); + RoomManager.AddOrUpdateRoom(result); tcs.SetResult(true); }; From 8796e45f63447ec2de4c89f4d03af0b384ec264a Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Tue, 14 Sep 2021 15:22:03 +0100 Subject: [PATCH 1905/2442] prevent 2B objects from dividing by zero --- osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs | 2 +- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index 63daea1ade..7467feb009 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills return Math.Max( result + (jumpDistanceExp + travelDistanceExp + Math.Sqrt(travelDistanceExp * jumpDistanceExp)) / Math.Max(osuCurrent.DeltaTime, timing_threshold), - (Math.Sqrt(travelDistanceExp * jumpDistanceExp) + jumpDistanceExp + travelDistanceExp) / osuCurrent.DeltaTime + (Math.Sqrt(travelDistanceExp * jumpDistanceExp) + jumpDistanceExp + travelDistanceExp) / Math.Max(osuCurrent.DeltaTime, 1) ); } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 2640d4ac41..39acbf4027 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } } - return (1 + (speedBonus - 1) * 0.75) * angleBonus * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / deltaTime; + return (1 + (speedBonus - 1) * 0.75) * angleBonus * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / Math.Max(deltaTime, 1); } } } From 2a894e7a3f560ae785e3395b55d36e8695a83741 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Sep 2021 23:26:02 +0900 Subject: [PATCH 1906/2442] Make `EditorLoader` state `private` --- osu.Game/Screens/Edit/EditorLoader.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorLoader.cs b/osu.Game/Screens/Edit/EditorLoader.cs index b6e57b0491..5ad720f7f0 100644 --- a/osu.Game/Screens/Edit/EditorLoader.cs +++ b/osu.Game/Screens/Edit/EditorLoader.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.Edit /// This will be read by the next editor instance to be opened to restore any relevant previous state. /// [CanBeNull] - public EditorState State; + private EditorState state; public override float BackgroundParallaxAmount => 0.1f; @@ -78,7 +78,7 @@ namespace osu.Game.Screens.Edit scheduledDifficultySwitch = Schedule(() => { Beatmap.Value = beatmapManager.GetWorkingBeatmap(nextBeatmap); - State = editorState; + state = editorState; // This screen is a weird exception to the rule that nothing after song select changes the global beatmap. // Because of this, we need to update the background stack's beatmap to match. From f8bdca542d2a6d993b3b0d4a6ddc7ae87ddaf9a4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Sep 2021 23:36:17 +0900 Subject: [PATCH 1907/2442] Make restoring state a `public` call on `Editor` --- osu.Game/Screens/Edit/Editor.cs | 22 +++++++++++----------- osu.Game/Screens/Edit/EditorLoader.cs | 8 +++++++- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index eba75f4408..5bb47e1c11 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -317,6 +317,16 @@ namespace osu.Game.Screens.Edit /// public void UpdateClockSource() => clock.ChangeSource(Beatmap.Value.Track); + /// + /// Restore the editor to a provided state. + /// + /// The state to restore. + public void RestoreState([NotNull] EditorState state) => Schedule(() => + { + clock.Seek(state.Time); + clipboard.Value = state.ClipboardContent; + }); + protected void Save() { // no longer new after first user-triggered save. @@ -476,8 +486,6 @@ namespace osu.Game.Screens.Edit }); resetTrack(true); - if (loader?.State != null) - restoreState(loader.State); } public override bool OnExiting(IScreen next) @@ -745,17 +753,9 @@ namespace osu.Game.Screens.Edit protected void SwitchToDifficulty(BeatmapInfo nextBeatmap) => loader?.ScheduleDifficultySwitch(nextBeatmap, new EditorState { Time = clock.CurrentTimeAccurate, - ClipboardContent = editorBeatmap.BeatmapInfo.RulesetID == nextBeatmap.RulesetID ? clipboard.Value : null + ClipboardContent = editorBeatmap.BeatmapInfo.RulesetID == nextBeatmap.RulesetID ? clipboard.Value : string.Empty }); - private void restoreState([NotNull] EditorState state) - { - if (state.Time != null) - clock.Seek(state.Time.Value); - - clipboard.Value = state.ClipboardContent ?? clipboard.Value; - } - private void cancelExit() => loader?.CancelPendingDifficultySwitch(); public double SnapTime(double time, double? referenceTime) => editorBeatmap.SnapTime(time, referenceTime); diff --git a/osu.Game/Screens/Edit/EditorLoader.cs b/osu.Game/Screens/Edit/EditorLoader.cs index 5ad720f7f0..2a01a5b6b2 100644 --- a/osu.Game/Screens/Edit/EditorLoader.cs +++ b/osu.Game/Screens/Edit/EditorLoader.cs @@ -91,7 +91,13 @@ namespace osu.Game.Screens.Edit private void pushEditor() { - this.Push(CreateEditor()); + var editor = CreateEditor(); + + this.Push(editor); + + if (state != null) + editor.RestoreState(state); + ValidForResume = false; } From 57f8ccca167d48e79c3c5e89b8d9db6d4a7febe9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Sep 2021 23:36:26 +0900 Subject: [PATCH 1908/2442] Remove nullability from `EditorState` properties Also update the xmldoc to not be specific to difficulty switching --- osu.Game/Screens/Edit/EditorState.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorState.cs b/osu.Game/Screens/Edit/EditorState.cs index 09cc1184d2..4690074e3d 100644 --- a/osu.Game/Screens/Edit/EditorState.cs +++ b/osu.Game/Screens/Edit/EditorState.cs @@ -6,19 +6,18 @@ namespace osu.Game.Screens.Edit { /// - /// Structure used to transport data between instances on difficulty change. - /// It's intended to be received by from one editor instance and passed down to the next one. + /// Structure used to convey the general state of an instance. /// public class EditorState { /// - /// The current clock time when a difficulty switch was requested. + /// The current audio time. /// - public double? Time { get; set; } + public double Time { get; set; } /// - /// The current editor clipboard content at the time when a difficulty switch was requested. + /// The editor clipboard content. /// - public string? ClipboardContent { get; set; } + public string ClipboardContent { get; set; } = string.Empty; } } From b9193aae6dcec54c1e5ff1a51f6863e240de7c74 Mon Sep 17 00:00:00 2001 From: AbstractQbit <38468635+AbstractQbit@users.noreply.github.com> Date: Tue, 14 Sep 2021 17:37:57 +0300 Subject: [PATCH 1909/2442] Make IOsuScreen.AllowTrackAdjustments nullable Allows for inheriting value from the previous screen if undefined --- osu.Game/OsuGame.cs | 29 +++++++++++++++++-- .../Maintenance/DirectorySelectScreen.cs | 2 +- osu.Game/Screens/Edit/Editor.cs | 2 +- osu.Game/Screens/IOsuScreen.cs | 3 +- osu.Game/Screens/Import/FileImportScreen.cs | 2 +- osu.Game/Screens/Menu/MainMenu.cs | 2 +- .../Spectate/MultiSpectatorScreen.cs | 2 +- .../Screens/OnlinePlay/OnlinePlayScreen.cs | 2 +- .../Screens/OnlinePlay/OnlinePlaySubScreen.cs | 2 ++ osu.Game/Screens/OsuScreen.cs | 2 +- osu.Game/Screens/Play/Player.cs | 2 +- .../Play/ScreenWithBeatmapBackground.cs | 2 ++ osu.Game/Screens/StartupScreen.cs | 2 +- 13 files changed, 42 insertions(+), 12 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 2107b3a0e9..c6a07bbbba 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1075,8 +1075,6 @@ namespace osu.Game OverlayActivationMode.BindTo(newOsuScreen.OverlayActivationMode); API.Activity.BindTo(newOsuScreen.Activity); - MusicController.AllowTrackAdjustments = newOsuScreen.AllowTrackAdjustments; - if (newOsuScreen.HideOverlaysOnEnter) CloseAllOverlays(); else @@ -1093,6 +1091,24 @@ namespace osu.Game { ScreenChanged(lastScreen, newScreen); Logger.Log($"Screen changed → {newScreen}"); + + // set AllowTrackAdjustments if new screen defines it, inherit otherwise + if (newScreen is IOsuScreen newOsuScreen && newOsuScreen.AllowTrackAdjustments.HasValue) + { + allowTrackAdjustmentsStack.Push(newOsuScreen.AllowTrackAdjustments.Value); + Logger.Log($"Screen's AllowTrackAdjustments explicit → {allowTrackAdjustmentsStack.First()}"); + } + else if (allowTrackAdjustmentsStack.Any()) + { + allowTrackAdjustmentsStack.Push(allowTrackAdjustmentsStack.First()); + Logger.Log($"Screen's AllowTrackAdjustments inherit → {allowTrackAdjustmentsStack.First()}"); + } + else + { + allowTrackAdjustmentsStack.Push(false); + } + + MusicController.AllowTrackAdjustments = allowTrackAdjustmentsStack.First(); } private void screenExited(IScreen lastScreen, IScreen newScreen) @@ -1102,8 +1118,17 @@ namespace osu.Game if (newScreen == null) Exit(); + + if (allowTrackAdjustmentsStack.Count > 1) + { + allowTrackAdjustmentsStack.Pop(); + MusicController.AllowTrackAdjustments = allowTrackAdjustmentsStack.First(); + Logger.Log($"Screen's AllowTrackAdjustments return ← {allowTrackAdjustmentsStack.First()}"); + } } + private Stack allowTrackAdjustmentsStack = new Stack(); + IBindable ILocalUserPlayInfo.IsPlaying => LocalUserPlaying; } } diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs index 1d67968ab1..6d0e79e2c7 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance private OsuDirectorySelector directorySelector; - public override bool AllowTrackAdjustments => false; + public override bool? AllowTrackAdjustments => false; /// /// Text to display in the header to inform the user of what they are selecting. diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 28ae7e620e..13e4a11915 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -56,7 +56,7 @@ namespace osu.Game.Screens.Edit public override bool DisallowExternalBeatmapRulesetChanges => true; - public override bool AllowTrackAdjustments => false; + public override bool? AllowTrackAdjustments => false; protected bool HasUnsavedChanges => lastSavedHash != changeHandler.CurrentStateHash; diff --git a/osu.Game/Screens/IOsuScreen.cs b/osu.Game/Screens/IOsuScreen.cs index 17384c161c..a1e4d9ed01 100644 --- a/osu.Game/Screens/IOsuScreen.cs +++ b/osu.Game/Screens/IOsuScreen.cs @@ -60,8 +60,9 @@ namespace osu.Game.Screens /// /// Whether mod track adjustments are allowed to be applied. + /// Null means to inherit from the parent screen. /// - bool AllowTrackAdjustments { get; } + bool? AllowTrackAdjustments { get; } /// /// Invoked when the back button has been pressed to close any overlays before exiting this . diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index 606174193d..69fcf31876 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Import { public override bool HideOverlaysOnEnter => true; - public override bool AllowTrackAdjustments => false; + public override bool? AllowTrackAdjustments => false; private OsuFileSelector fileSelector; private Container contentContainer; diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 8b2ec43e3e..00885a91c5 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -36,7 +36,7 @@ namespace osu.Game.Screens.Menu public override bool AllowExternalScreenChange => true; - public override bool AllowTrackAdjustments => false; + public override bool? AllowTrackAdjustments => false; private Screen songSelect; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs index bf7c738882..c45e3a79da 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs @@ -26,7 +26,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate public override bool DisallowExternalBeatmapRulesetChanges => true; // We are managing our own adjustments. For now, this happens inside the Player instances themselves. - public override bool AllowTrackAdjustments => false; + public override bool? AllowTrackAdjustments => false; /// /// Whether all spectating players have finished loading. diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 62bfd2cfed..e3945c9cac 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -24,7 +24,7 @@ namespace osu.Game.Screens.OnlinePlay [Cached] protected readonly OverlayColourProvider ColourProvider = new OverlayColourProvider(OverlayColourScheme.Plum); - public override bool AllowTrackAdjustments => false; + public override bool? AllowTrackAdjustments => false; public override bool CursorVisible => (screenStack?.CurrentScreen as IOnlinePlaySubScreen)?.CursorVisible ?? true; diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs index 3411c4afb1..8c4f0c1394 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs @@ -11,6 +11,8 @@ namespace osu.Game.Screens.OnlinePlay { public override bool DisallowExternalBeatmapRulesetChanges => false; + public override bool? AllowTrackAdjustments => true; + public virtual string ShortTitle => Title; [Resolved(CanBeNull = true)] diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 9aec2a5c19..78908b5d8a 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -81,7 +81,7 @@ namespace osu.Game.Screens public virtual float BackgroundParallaxAmount => 1; - public virtual bool AllowTrackAdjustments => true; + public virtual bool? AllowTrackAdjustments => null; public Bindable Beatmap { get; private set; } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index e8a2790c94..9927467bd6 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -56,7 +56,7 @@ namespace osu.Game.Screens.Play protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.UserTriggered; // We are managing our own adjustments (see OnEntering/OnExiting). - public override bool AllowTrackAdjustments => false; + public override bool? AllowTrackAdjustments => false; private readonly IBindable gameActive = new Bindable(true); diff --git a/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs b/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs index 88dab88d42..9bec320e22 100644 --- a/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs +++ b/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs @@ -10,6 +10,8 @@ namespace osu.Game.Screens.Play { protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value); + public override bool? AllowTrackAdjustments => true; + public void ApplyToBackground(Action action) => base.ApplyToBackground(b => action.Invoke((BackgroundScreenBeatmap)b)); } } diff --git a/osu.Game/Screens/StartupScreen.cs b/osu.Game/Screens/StartupScreen.cs index 15f75d7cff..7b73d36fdf 100644 --- a/osu.Game/Screens/StartupScreen.cs +++ b/osu.Game/Screens/StartupScreen.cs @@ -16,7 +16,7 @@ namespace osu.Game.Screens public override bool CursorVisible => false; - public override bool AllowTrackAdjustments => false; + public override bool? AllowTrackAdjustments => false; protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled; } From 01d2f4f17a98bfb372410838eb8159c260b6fa8a Mon Sep 17 00:00:00 2001 From: AbstractQbit <38468635+AbstractQbit@users.noreply.github.com> Date: Tue, 14 Sep 2021 18:04:43 +0300 Subject: [PATCH 1910/2442] Make `allowTrackAdjustmentsStack` readonly --- osu.Game/OsuGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index c6a07bbbba..5f1b2ac87d 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1127,7 +1127,7 @@ namespace osu.Game } } - private Stack allowTrackAdjustmentsStack = new Stack(); + private readonly Stack allowTrackAdjustmentsStack = new Stack(); IBindable ILocalUserPlayInfo.IsPlaying => LocalUserPlaying; } From bd18c581c11a6437a9a055ae4b267425d8463d6f Mon Sep 17 00:00:00 2001 From: AbstractQbit <38468635+AbstractQbit@users.noreply.github.com> Date: Tue, 14 Sep 2021 21:14:24 +0300 Subject: [PATCH 1911/2442] Replace `allowTrackAdjustmentsStack` with a Dictionary --- osu.Game/OsuGame.cs | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 5f1b2ac87d..499b561718 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1094,21 +1094,13 @@ namespace osu.Game // set AllowTrackAdjustments if new screen defines it, inherit otherwise if (newScreen is IOsuScreen newOsuScreen && newOsuScreen.AllowTrackAdjustments.HasValue) - { - allowTrackAdjustmentsStack.Push(newOsuScreen.AllowTrackAdjustments.Value); - Logger.Log($"Screen's AllowTrackAdjustments explicit → {allowTrackAdjustmentsStack.First()}"); - } - else if (allowTrackAdjustmentsStack.Any()) - { - allowTrackAdjustmentsStack.Push(allowTrackAdjustmentsStack.First()); - Logger.Log($"Screen's AllowTrackAdjustments inherit → {allowTrackAdjustmentsStack.First()}"); - } + allowTrackAdjustmentsDict[newScreen] = newOsuScreen.AllowTrackAdjustments.Value; + else if (allowTrackAdjustmentsDict.ContainsKey(lastScreen)) + allowTrackAdjustmentsDict[newScreen] = allowTrackAdjustmentsDict[lastScreen]; else - { - allowTrackAdjustmentsStack.Push(false); - } + allowTrackAdjustmentsDict[newScreen] = true; - MusicController.AllowTrackAdjustments = allowTrackAdjustmentsStack.First(); + MusicController.AllowTrackAdjustments = allowTrackAdjustmentsDict[newScreen]; } private void screenExited(IScreen lastScreen, IScreen newScreen) @@ -1116,18 +1108,15 @@ namespace osu.Game ScreenChanged(lastScreen, newScreen); Logger.Log($"Screen changed ← {newScreen}"); + allowTrackAdjustmentsDict.Remove(lastScreen); + if (newScreen == null) Exit(); - - if (allowTrackAdjustmentsStack.Count > 1) - { - allowTrackAdjustmentsStack.Pop(); - MusicController.AllowTrackAdjustments = allowTrackAdjustmentsStack.First(); - Logger.Log($"Screen's AllowTrackAdjustments return ← {allowTrackAdjustmentsStack.First()}"); - } + else + MusicController.AllowTrackAdjustments = allowTrackAdjustmentsDict[newScreen]; } - private readonly Stack allowTrackAdjustmentsStack = new Stack(); + private readonly Dictionary allowTrackAdjustmentsDict = new Dictionary(); IBindable ILocalUserPlayInfo.IsPlaying => LocalUserPlaying; } From baf99619343ff49ed240c8cc09e3db9453dfda2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 14 Sep 2021 22:50:45 +0200 Subject: [PATCH 1912/2442] Amend xmldoc of shake extension method --- osu.Game/Extensions/DrawableExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Extensions/DrawableExtensions.cs b/osu.Game/Extensions/DrawableExtensions.cs index b05a2994ea..03cc345947 100644 --- a/osu.Game/Extensions/DrawableExtensions.cs +++ b/osu.Game/Extensions/DrawableExtensions.cs @@ -39,7 +39,7 @@ namespace osu.Game.Extensions } /// - /// Shake the contents of this container. + /// Shakes this drawable. /// /// The target to shake. /// The length of a single shake. From 4b3ab42ffd8cb2b9c60490315dce8470637dffcd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 15 Sep 2021 13:18:46 +0900 Subject: [PATCH 1913/2442] Ensure beatmap is populated --- osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs | 8 +++++++- .../Visual/Gameplay/TestSceneReplayRecording.cs | 8 +++++++- .../Visual/Gameplay/TestSceneSpectatorPlayback.cs | 4 +++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs index b38f7a998d..aded934b88 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs @@ -17,10 +17,12 @@ using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Replays; using osu.Game.Rulesets; +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.Beatmaps; using osu.Game.Tests.Visual.UserInterface; using osuTK; using osuTK.Graphics; @@ -54,7 +56,11 @@ namespace osu.Game.Tests.Visual.Gameplay { recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique) { - Recorder = recorder = new TestReplayRecorder(new Score { Replay = replay }) + Recorder = recorder = new TestReplayRecorder(new Score + { + Replay = replay, + ScoreInfo = { Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo } + }) { ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos), }, diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs index 6e338b7202..37fd1a77d5 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs @@ -13,10 +13,12 @@ using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Replays; using osu.Game.Rulesets; +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.Beatmaps; using osu.Game.Tests.Visual.UserInterface; using osuTK; using osuTK.Graphics; @@ -45,7 +47,11 @@ namespace osu.Game.Tests.Visual.Gameplay { recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique) { - Recorder = new TestReplayRecorder(new Score { Replay = replay }) + Recorder = new TestReplayRecorder(new Score + { + Replay = replay, + ScoreInfo = { Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo } + }) { ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos) }, diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs index bb577886cc..039daf39e2 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs @@ -25,11 +25,13 @@ using osu.Game.Online.Spectator; using osu.Game.Replays; using osu.Game.Replays.Legacy; using osu.Game.Rulesets; +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.Beatmaps; using osu.Game.Tests.Visual.UserInterface; using osuTK; using osuTK.Graphics; @@ -354,7 +356,7 @@ namespace osu.Game.Tests.Visual.Gameplay internal class TestReplayRecorder : ReplayRecorder { public TestReplayRecorder() - : base(new Score()) + : base(new Score { ScoreInfo = { Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo } }) { } From 34bde293abebbe144c6e7ab425aa874af8289da2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 15 Sep 2021 13:26:39 +0900 Subject: [PATCH 1914/2442] Fix tests --- osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyNotePiece.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyNotePiece.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyNotePiece.cs index bbc7520bac..321a87f8b1 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyNotePiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyNotePiece.cs @@ -21,6 +21,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy private readonly IBindable direction = new Bindable(); private Container directionContainer; + + [CanBeNull] private Drawable noteAnimation; private float? minimumColumnWidth; @@ -41,7 +43,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy Origin = Anchor.BottomCentre, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Child = noteAnimation = GetAnimation(skin) + Child = noteAnimation = GetAnimation(skin) ?? Empty() }; direction.BindTo(scrollingInfo.Direction); From a2dcef7c0aa1acd3557007e3c410443862318512 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Sep 2021 13:37:30 +0900 Subject: [PATCH 1915/2442] Use local (or barebones `BeatmapInfo`) where feasible --- osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs | 4 +--- osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs | 4 +--- osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs | 4 +--- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs index aded934b88..2ce0213ea2 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs @@ -17,12 +17,10 @@ using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Replays; using osu.Game.Rulesets; -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.Beatmaps; using osu.Game.Tests.Visual.UserInterface; using osuTK; using osuTK.Graphics; @@ -59,7 +57,7 @@ namespace osu.Game.Tests.Visual.Gameplay Recorder = recorder = new TestReplayRecorder(new Score { Replay = replay, - ScoreInfo = { Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo } + ScoreInfo = { Beatmap = gameplayBeatmap.BeatmapInfo } }) { ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos), diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs index 37fd1a77d5..85a2870bf9 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs @@ -13,12 +13,10 @@ using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Replays; using osu.Game.Rulesets; -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.Beatmaps; using osu.Game.Tests.Visual.UserInterface; using osuTK; using osuTK.Graphics; @@ -50,7 +48,7 @@ namespace osu.Game.Tests.Visual.Gameplay Recorder = new TestReplayRecorder(new Score { Replay = replay, - ScoreInfo = { Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo } + ScoreInfo = { Beatmap = gameplayBeatmap.BeatmapInfo } }) { ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs index 039daf39e2..d9d0dc6c58 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs @@ -25,13 +25,11 @@ using osu.Game.Online.Spectator; using osu.Game.Replays; using osu.Game.Replays.Legacy; using osu.Game.Rulesets; -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.Beatmaps; using osu.Game.Tests.Visual.UserInterface; using osuTK; using osuTK.Graphics; @@ -356,7 +354,7 @@ namespace osu.Game.Tests.Visual.Gameplay internal class TestReplayRecorder : ReplayRecorder { public TestReplayRecorder() - : base(new Score { ScoreInfo = { Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo } }) + : base(new Score { ScoreInfo = { Beatmap = new BeatmapInfo() } }) { } From 8217b90b1cb71f793a25e31dc644794da4b1d189 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 5 Sep 2021 00:41:34 +0300 Subject: [PATCH 1916/2442] Consider legacy glyph texture heights as the baselines for simplicity Mixing `LegacySpriteText` with legitment fonts should never be the case, so it's fine to consuder the height as the baseline, since there's really no other way around it. --- osu.Game/Skinning/LegacySpriteText.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySpriteText.cs b/osu.Game/Skinning/LegacySpriteText.cs index 7895fcccca..8fc6cbde7d 100644 --- a/osu.Game/Skinning/LegacySpriteText.cs +++ b/osu.Game/Skinning/LegacySpriteText.cs @@ -56,7 +56,7 @@ namespace osu.Game.Skinning if (texture == null) return null; - return new TexturedCharacterGlyph(new CharacterGlyph(character, 0, 0, texture.Width, null), texture, 1f / texture.ScaleAdjust); + return new TexturedCharacterGlyph(new CharacterGlyph(character, 0, 0, texture.Width, texture.Height, null), texture, 1f / texture.ScaleAdjust); } private static string getLookupName(char character) From f9af24df23e88659793eb629ab13be689dfb3d5f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 15 Sep 2021 15:22:27 +0900 Subject: [PATCH 1917/2442] Fix mania hitobject tests --- .../Skinning/ManiaHitObjectTestScene.cs | 4 +-- .../Skinning/ManiaSkinnableTestScene.cs | 25 ++----------------- 2 files changed, 4 insertions(+), 25 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaHitObjectTestScene.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaHitObjectTestScene.cs index b7d7af6b8c..68cf3b67df 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaHitObjectTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaHitObjectTestScene.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning { c.Add(CreateHitObject().With(h => { - h.HitObject.StartTime = START_TIME; + h.HitObject.StartTime = Time.Current + 5000; h.AccentColour.Value = Color4.Orange; })); }) @@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning { c.Add(CreateHitObject().With(h => { - h.HitObject.StartTime = START_TIME; + h.HitObject.StartTime = Time.Current + 5000; h.AccentColour.Value = Color4.Orange; })); }) diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaSkinnableTestScene.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaSkinnableTestScene.cs index 1d84a2dfcb..ddfd057cd8 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaSkinnableTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaSkinnableTestScene.cs @@ -19,8 +19,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning /// public abstract class ManiaSkinnableTestScene : SkinnableTestScene { - protected const double START_TIME = 1000000000; - [Cached(Type = typeof(IScrollingInfo))] private readonly TestScrollingInfo scrollingInfo = new TestScrollingInfo(); @@ -55,27 +53,8 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning public readonly Bindable Direction = new Bindable(); IBindable IScrollingInfo.Direction => Direction; - IBindable IScrollingInfo.TimeRange { get; } = new Bindable(1000); - IScrollAlgorithm IScrollingInfo.Algorithm { get; } = new ZeroScrollAlgorithm(); - } - - private class ZeroScrollAlgorithm : IScrollAlgorithm - { - public double GetDisplayStartTime(double originTime, float offset, double timeRange, float scrollLength) - => double.MinValue; - - public float GetLength(double startTime, double endTime, double timeRange, float scrollLength) - => scrollLength; - - public float PositionAt(double time, double currentTime, double timeRange, float scrollLength) - => (float)((time - START_TIME) / timeRange) * scrollLength; - - public double TimeAt(float position, double currentTime, double timeRange, float scrollLength) - => 0; - - public void Reset() - { - } + IBindable IScrollingInfo.TimeRange { get; } = new Bindable(5000); + IScrollAlgorithm IScrollingInfo.Algorithm { get; } = new ConstantScrollAlgorithm(); } } } From 187c557ea8105dc1db329cdf6828bc2a79fc41dc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 22 Aug 2021 02:21:45 +0900 Subject: [PATCH 1918/2442] Begin migrating settings implementation across to realm --- osu.Game/Configuration/RealmSetting.cs | 33 +++++++++++++++++++++++++ osu.Game/Configuration/SettingsStore.cs | 25 ++++++++++--------- osu.Game/OsuGameBase.cs | 4 +-- osu.Game/Rulesets/RulesetConfigCache.cs | 4 +-- 4 files changed, 50 insertions(+), 16 deletions(-) create mode 100644 osu.Game/Configuration/RealmSetting.cs diff --git a/osu.Game/Configuration/RealmSetting.cs b/osu.Game/Configuration/RealmSetting.cs new file mode 100644 index 0000000000..b773796067 --- /dev/null +++ b/osu.Game/Configuration/RealmSetting.cs @@ -0,0 +1,33 @@ +// 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 osu.Game.Database; +using Realms; + +namespace osu.Game.Configuration +{ + [MapTo(@"Setting")] + public class RealmSetting : RealmObject, IHasGuidPrimaryKey + { + [PrimaryKey] + public Guid ID { get; set; } = Guid.NewGuid(); + + public int? RulesetID { get; set; } + + public int? Variant { get; set; } + + public string Key { get; set; } + + [MapTo(nameof(Value))] + public string ValueString { get; set; } + + public object Value + { + get => ValueString; + set => ValueString = value.ToString(); + } + + public override string ToString() => $"{Key}=>{Value}"; + } +} diff --git a/osu.Game/Configuration/SettingsStore.cs b/osu.Game/Configuration/SettingsStore.cs index 86e84b0732..864fa3cf53 100644 --- a/osu.Game/Configuration/SettingsStore.cs +++ b/osu.Game/Configuration/SettingsStore.cs @@ -1,31 +1,34 @@ // 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 osu.Game.Database; +using Realms; namespace osu.Game.Configuration { - public class SettingsStore : DatabaseBackedStore + public class RealmSettingsStore { - public event Action SettingChanged; + private readonly RealmContextFactory realmFactory; - public SettingsStore(DatabaseContextFactory contextFactory) - : base(contextFactory) + public RealmSettingsStore(RealmContextFactory realmFactory) { + this.realmFactory = realmFactory; } /// - /// Retrieve s for a specified ruleset/variant content. + /// Retrieve s for a specified ruleset/variant content. /// /// The ruleset's internal ID. /// An optional variant. - public List Query(int? rulesetId = null, int? variant = null) => - ContextFactory.Get().DatabasedSetting.Where(b => b.RulesetID == rulesetId && b.Variant == variant).ToList(); + public List Query(int? rulesetId = null, int? variant = null) + { + using (var context = realmFactory.GetForRead()) + return context.Realm.All().Where(b => b.RulesetID == rulesetId && b.Variant == variant).ToList(); + } - public void Update(DatabasedSetting setting) + public void Update(RealmSetting setting) { using (ContextFactory.GetForWrite()) { @@ -33,11 +36,9 @@ namespace osu.Game.Configuration Refresh(ref setting); setting.Value = newValue; } - - SettingChanged?.Invoke(); } - public void Delete(DatabasedSetting setting) + public void Delete(RealmSetting setting) { using (var usage = ContextFactory.GetForWrite()) usage.Context.Remove(setting); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index f4db0f2603..12f53df6e8 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -140,7 +140,7 @@ namespace osu.Game private FileStore fileStore; - private SettingsStore settingsStore; + private RealmSettingsStore settingsStore; private RulesetConfigCache rulesetConfigCache; @@ -279,7 +279,7 @@ namespace osu.Game migrateDataToRealm(); - dependencies.Cache(settingsStore = new SettingsStore(contextFactory)); + dependencies.Cache(settingsStore = new RealmSettingsStore(realmFactory)); dependencies.Cache(rulesetConfigCache = new RulesetConfigCache(settingsStore)); var powerStatus = CreateBatteryInfo(); diff --git a/osu.Game/Rulesets/RulesetConfigCache.cs b/osu.Game/Rulesets/RulesetConfigCache.cs index d42428638c..885fa249df 100644 --- a/osu.Game/Rulesets/RulesetConfigCache.cs +++ b/osu.Game/Rulesets/RulesetConfigCache.cs @@ -16,9 +16,9 @@ namespace osu.Game.Rulesets public class RulesetConfigCache : Component { private readonly ConcurrentDictionary configCache = new ConcurrentDictionary(); - private readonly SettingsStore settingsStore; + private readonly RealmSettingsStore settingsStore; - public RulesetConfigCache(SettingsStore settingsStore) + public RulesetConfigCache(RealmSettingsStore settingsStore) { this.settingsStore = settingsStore; } From 14314476f07ab0271ac49fbfa8a557d838676b04 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 8 Sep 2021 20:57:19 +0900 Subject: [PATCH 1919/2442] Update realm to latest version --- 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 941656bb70..5a302c5349 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From a2f1752344061e1f9ae4f7a4679f872de90feeaf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Sep 2021 14:39:47 +0900 Subject: [PATCH 1920/2442] Make settings works with current caching structure Will likely pull out that `RulesetConfigCache` next, but this is an "everything works" state. --- .../ManiaRulesetConfigManager.cs | 6 +- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 4 +- .../Configuration/OsuRulesetConfigManager.cs | 6 +- osu.Game.Rulesets.Osu/OsuRuleset.cs | 40 ++++++------- .../Testing/TestSceneRulesetDependencies.cs | 4 +- .../Configuration/DatabasedConfigManager.cs | 59 ++++++------------- osu.Game/Configuration/SettingsStore.cs | 21 +------ osu.Game/OsuGameBase.cs | 5 +- .../Settings/RulesetSettingsSubsection.cs | 4 +- .../Configuration/RulesetConfigManager.cs | 5 +- osu.Game/Rulesets/Ruleset.cs | 34 +++++------ osu.Game/Rulesets/RulesetConfigCache.cs | 34 ++++++++--- 12 files changed, 101 insertions(+), 121 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs index ac8168dfc9..d9bd0ab609 100644 --- a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs +++ b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs @@ -3,7 +3,7 @@ using System; using osu.Framework.Configuration.Tracking; -using osu.Game.Configuration; +using osu.Game.Database; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Mania.UI; @@ -11,8 +11,8 @@ namespace osu.Game.Rulesets.Mania.Configuration { public class ManiaRulesetConfigManager : RulesetConfigManager { - public ManiaRulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null) - : base(settings, ruleset, variant) + public ManiaRulesetConfigManager(RealmContextFactory realmFactory, RulesetInfo ruleset, int? variant = null) + : base(realmFactory, ruleset, variant) { } diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 1f79dae280..7ad27b94eb 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -17,7 +17,7 @@ using osu.Game.Graphics; using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Replays.Types; using osu.Game.Beatmaps.Legacy; -using osu.Game.Configuration; +using osu.Game.Database; using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Difficulty; @@ -278,7 +278,7 @@ namespace osu.Game.Rulesets.Mania public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame(); - public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new ManiaRulesetConfigManager(settings, RulesetInfo); + public override IRulesetConfigManager CreateConfig(RealmContextFactory realmFactory) => new ManiaRulesetConfigManager(realmFactory, RulesetInfo); public override RulesetSettingsSubsection CreateSettings() => new ManiaSettingsSubsection(this); diff --git a/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs b/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs index 9589fd576f..23c25c6558 100644 --- a/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs +++ b/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs @@ -1,7 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Configuration; +using osu.Game.Database; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.UI; @@ -9,8 +9,8 @@ namespace osu.Game.Rulesets.Osu.Configuration { public class OsuRulesetConfigManager : RulesetConfigManager { - public OsuRulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null) - : base(settings, ruleset, variant) + public OsuRulesetConfigManager(RealmContextFactory realmFactory, RulesetInfo ruleset, int? variant = null) + : base(realmFactory, ruleset, variant) { } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index f4a93a571d..b7cb0c5313 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -1,41 +1,41 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Rulesets.Osu.UI; -using osu.Game.Rulesets.UI; +using System; using System.Collections.Generic; +using System.Linq; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; -using osu.Game.Overlays.Settings; using osu.Framework.Input.Bindings; -using osu.Game.Rulesets.Osu.Edit; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Osu.Replays; -using osu.Game.Rulesets.Replays.Types; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; -using osu.Game.Configuration; +using osu.Game.Database; +using osu.Game.Graphics; +using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Difficulty; -using osu.Game.Rulesets.Osu.Scoring; -using osu.Game.Rulesets.Scoring; -using osu.Game.Scoring; -using osu.Game.Skinning; -using System; -using System.Linq; -using osu.Framework.Extensions.EnumExtensions; +using osu.Game.Rulesets.Osu.Edit; using osu.Game.Rulesets.Osu.Edit.Setup; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Replays; +using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Osu.Skinning.Legacy; using osu.Game.Rulesets.Osu.Statistics; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.Replays.Types; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Scoring; using osu.Game.Screens.Edit.Setup; using osu.Game.Screens.Ranking.Statistics; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu { @@ -229,7 +229,7 @@ namespace osu.Game.Rulesets.Osu public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame(); - public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new OsuRulesetConfigManager(settings, RulesetInfo); + public override IRulesetConfigManager CreateConfig(RealmContextFactory realmFactory) => new OsuRulesetConfigManager(realmFactory, RulesetInfo); protected override IEnumerable GetValidHitResults() { diff --git a/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs b/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs index 8c6932e792..12960fd4d0 100644 --- a/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs +++ b/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs @@ -12,7 +12,7 @@ using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Framework.Testing; using osu.Game.Beatmaps; -using osu.Game.Configuration; +using osu.Game.Database; using osu.Game.Rulesets; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Difficulty; @@ -74,7 +74,7 @@ namespace osu.Game.Tests.Testing } public override IResourceStore CreateResourceStore() => new NamespacedResourceStore(TestResources.GetStore(), @"Resources"); - public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new TestRulesetConfigManager(); + public override IRulesetConfigManager CreateConfig(RealmContextFactory realmFactory) => new TestRulesetConfigManager(); public override IEnumerable GetModsFor(ModType type) => Array.Empty(); public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => null; diff --git a/osu.Game/Configuration/DatabasedConfigManager.cs b/osu.Game/Configuration/DatabasedConfigManager.cs index b3783b45a8..0514d11e24 100644 --- a/osu.Game/Configuration/DatabasedConfigManager.cs +++ b/osu.Game/Configuration/DatabasedConfigManager.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Bindables; using osu.Framework.Configuration; +using osu.Game.Database; using osu.Game.Rulesets; namespace osu.Game.Configuration @@ -13,19 +14,17 @@ namespace osu.Game.Configuration public abstract class DatabasedConfigManager : ConfigManager where TLookup : struct, Enum { - private readonly SettingsStore settings; + private readonly RealmContextFactory realmFactory; private readonly int? variant; - private List databasedSettings; + private List databasedSettings; private readonly RulesetInfo ruleset; - private bool legacySettingsExist; - - protected DatabasedConfigManager(SettingsStore settings, RulesetInfo ruleset = null, int? variant = null) + protected DatabasedConfigManager(RealmContextFactory realmFactory, RulesetInfo ruleset = null, int? variant = null) { - this.settings = settings; + this.realmFactory = realmFactory; this.ruleset = ruleset; this.variant = variant; @@ -36,39 +35,22 @@ namespace osu.Game.Configuration protected override void PerformLoad() { - databasedSettings = settings.Query(ruleset?.ID, variant); - legacySettingsExist = databasedSettings.Any(s => int.TryParse(s.Key, out _)); + var rulesetID = ruleset?.ID; + + // As long as RulesetConfigCache exists, there is no need to subscribe to realm events. + databasedSettings = realmFactory.Context.All().Where(b => b.RulesetID == rulesetID && b.Variant == variant).ToList(); } protected override bool PerformSave() { - lock (dirtySettings) - { - foreach (var setting in dirtySettings) - settings.Update(setting); - dirtySettings.Clear(); - } - + // do nothing, realm saves immediately return true; } - private readonly List dirtySettings = new List(); - protected override void AddBindable(TLookup lookup, Bindable bindable) { base.AddBindable(lookup, bindable); - if (legacySettingsExist) - { - var legacySetting = databasedSettings.Find(s => s.Key == ((int)(object)lookup).ToString()); - - if (legacySetting != null) - { - bindable.Parse(legacySetting.Value); - settings.Delete(legacySetting); - } - } - var setting = databasedSettings.Find(s => s.Key == lookup.ToString()); if (setting != null) @@ -77,12 +59,15 @@ namespace osu.Game.Configuration } else { - settings.Update(setting = new DatabasedSetting + realmFactory.Context.Write(() => { - Key = lookup.ToString(), - Value = bindable.Value, - RulesetID = ruleset?.ID, - Variant = variant, + realmFactory.Context.Add(setting = new RealmSetting + { + Key = lookup.ToString(), + Value = bindable.Value, + RulesetID = ruleset?.ID, + Variant = variant, + }); }); databasedSettings.Add(setting); @@ -90,13 +75,7 @@ namespace osu.Game.Configuration bindable.ValueChanged += b => { - setting.Value = b.NewValue; - - lock (dirtySettings) - { - if (!dirtySettings.Contains(setting)) - dirtySettings.Add(setting); - } + realmFactory.Context.Write(() => setting.Value = b.NewValue); }; } } diff --git a/osu.Game/Configuration/SettingsStore.cs b/osu.Game/Configuration/SettingsStore.cs index 864fa3cf53..49775927b1 100644 --- a/osu.Game/Configuration/SettingsStore.cs +++ b/osu.Game/Configuration/SettingsStore.cs @@ -4,15 +4,14 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Database; -using Realms; namespace osu.Game.Configuration { - public class RealmSettingsStore + public class SettingsStore { private readonly RealmContextFactory realmFactory; - public RealmSettingsStore(RealmContextFactory realmFactory) + public SettingsStore(RealmContextFactory realmFactory) { this.realmFactory = realmFactory; } @@ -27,21 +26,5 @@ namespace osu.Game.Configuration using (var context = realmFactory.GetForRead()) return context.Realm.All().Where(b => b.RulesetID == rulesetId && b.Variant == variant).ToList(); } - - public void Update(RealmSetting setting) - { - using (ContextFactory.GetForWrite()) - { - var newValue = setting.Value; - Refresh(ref setting); - setting.Value = newValue; - } - } - - public void Delete(RealmSetting setting) - { - using (var usage = ContextFactory.GetForWrite()) - usage.Context.Remove(setting); - } } } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 12f53df6e8..600465b823 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -140,8 +140,6 @@ namespace osu.Game private FileStore fileStore; - private RealmSettingsStore settingsStore; - private RulesetConfigCache rulesetConfigCache; private SpectatorClient spectatorClient; @@ -279,8 +277,7 @@ namespace osu.Game migrateDataToRealm(); - dependencies.Cache(settingsStore = new RealmSettingsStore(realmFactory)); - dependencies.Cache(rulesetConfigCache = new RulesetConfigCache(settingsStore)); + dependencies.Cache(rulesetConfigCache = new RulesetConfigCache(realmFactory, RulesetStore)); var powerStatus = CreateBatteryInfo(); if (powerStatus != null) diff --git a/osu.Game/Overlays/Settings/RulesetSettingsSubsection.cs b/osu.Game/Overlays/Settings/RulesetSettingsSubsection.cs index 93b07fbac7..9ff5b8935a 100644 --- a/osu.Game/Overlays/Settings/RulesetSettingsSubsection.cs +++ b/osu.Game/Overlays/Settings/RulesetSettingsSubsection.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Game.Configuration; +using osu.Game.Database; using osu.Game.Rulesets; using osu.Game.Rulesets.Configuration; @@ -10,7 +10,7 @@ namespace osu.Game.Overlays.Settings { /// /// A which provides subclasses with the - /// from the 's . + /// from the 's . /// public abstract class RulesetSettingsSubsection : SettingsSubsection { diff --git a/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs index 0ff3455f00..17dbd30103 100644 --- a/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs +++ b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs @@ -3,14 +3,15 @@ using System; using osu.Game.Configuration; +using osu.Game.Database; namespace osu.Game.Rulesets.Configuration { public abstract class RulesetConfigManager : DatabasedConfigManager, IRulesetConfigManager where TLookup : struct, Enum { - protected RulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null) - : base(settings, ruleset, variant) + protected RulesetConfigManager(RealmContextFactory realmFactory, RulesetInfo ruleset, int? variant = null) + : base(realmFactory, ruleset, variant) { } } diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index b0c3836774..cf4ea4f01d 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -5,32 +5,32 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; +using osu.Framework.Extensions; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; using osu.Framework.IO.Stores; +using osu.Framework.Testing; using osu.Game.Beatmaps; -using osu.Game.Overlays.Settings; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Replays.Types; -using osu.Game.Rulesets.UI; using osu.Game.Beatmaps.Legacy; -using osu.Game.Configuration; +using osu.Game.Database; +using osu.Game.Extensions; +using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Scoring; -using osu.Game.Scoring; -using osu.Game.Skinning; -using osu.Game.Users; -using JetBrains.Annotations; -using osu.Framework.Extensions; -using osu.Framework.Extensions.EnumExtensions; -using osu.Framework.Testing; -using osu.Game.Extensions; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Filter; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Replays.Types; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Scoring; using osu.Game.Screens.Edit.Setup; using osu.Game.Screens.Ranking.Statistics; +using osu.Game.Skinning; +using osu.Game.Users; namespace osu.Game.Rulesets { @@ -262,8 +262,8 @@ namespace osu.Game.Rulesets /// /// Creates the for this . /// - /// The to store the settings. - public virtual IRulesetConfigManager CreateConfig(SettingsStore settings) => null; + /// The to store the settings. + public virtual IRulesetConfigManager CreateConfig(RealmContextFactory realmFactory) => null; /// /// A unique short name to reference this ruleset in online requests. diff --git a/osu.Game/Rulesets/RulesetConfigCache.cs b/osu.Game/Rulesets/RulesetConfigCache.cs index 885fa249df..afdf3219df 100644 --- a/osu.Game/Rulesets/RulesetConfigCache.cs +++ b/osu.Game/Rulesets/RulesetConfigCache.cs @@ -2,9 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Concurrent; +using System.Collections.Generic; using osu.Framework.Graphics; -using osu.Game.Configuration; +using osu.Game.Database; using osu.Game.Rulesets.Configuration; namespace osu.Game.Rulesets @@ -15,12 +15,29 @@ namespace osu.Game.Rulesets /// public class RulesetConfigCache : Component { - private readonly ConcurrentDictionary configCache = new ConcurrentDictionary(); - private readonly RealmSettingsStore settingsStore; + private readonly RealmContextFactory realmFactory; + private readonly RulesetStore rulesets; - public RulesetConfigCache(RealmSettingsStore settingsStore) + private readonly Dictionary configCache = new Dictionary(); + + public RulesetConfigCache(RealmContextFactory realmFactory, RulesetStore rulesets) { - this.settingsStore = settingsStore; + this.realmFactory = realmFactory; + this.rulesets = rulesets; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + // let's keep things simple for now and just retrieve all the required configs at startup.. + foreach (var ruleset in rulesets.AvailableRulesets) + { + if (ruleset.ID == null) + continue; + + configCache[ruleset.ID.Value] = ruleset.CreateInstance().CreateConfig(realmFactory); + } } /// @@ -34,7 +51,10 @@ namespace osu.Game.Rulesets if (ruleset.RulesetInfo.ID == null) return null; - return configCache.GetOrAdd(ruleset.RulesetInfo.ID.Value, _ => ruleset.CreateConfig(settingsStore)); + if (!configCache.TryGetValue(ruleset.RulesetInfo.ID.Value, out var config)) + return null; + + return config; } protected override void Dispose(bool isDisposing) From ac377a2e3c39235fa18b4be85b90b744b174a2cc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Sep 2021 14:56:40 +0900 Subject: [PATCH 1921/2442] Remove unused `SettingsStore` --- .../Visual/Navigation/TestSceneOsuGame.cs | 1 - osu.Game/Configuration/SettingsStore.cs | 30 ------------------- 2 files changed, 31 deletions(-) delete mode 100644 osu.Game/Configuration/SettingsStore.cs diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs index b8232837b5..43459408d5 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs @@ -75,7 +75,6 @@ namespace osu.Game.Tests.Visual.Navigation typeof(FileStore), typeof(ScoreManager), typeof(BeatmapManager), - typeof(SettingsStore), typeof(RulesetConfigCache), typeof(OsuColour), typeof(IBindable), diff --git a/osu.Game/Configuration/SettingsStore.cs b/osu.Game/Configuration/SettingsStore.cs deleted file mode 100644 index 49775927b1..0000000000 --- a/osu.Game/Configuration/SettingsStore.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using System.Linq; -using osu.Game.Database; - -namespace osu.Game.Configuration -{ - public class SettingsStore - { - private readonly RealmContextFactory realmFactory; - - public SettingsStore(RealmContextFactory realmFactory) - { - this.realmFactory = realmFactory; - } - - /// - /// Retrieve s for a specified ruleset/variant content. - /// - /// The ruleset's internal ID. - /// An optional variant. - public List Query(int? rulesetId = null, int? variant = null) - { - using (var context = realmFactory.GetForRead()) - return context.Realm.All().Where(b => b.RulesetID == rulesetId && b.Variant == variant).ToList(); - } - } -} From 2bcb3fd304ad8ca70bc1b998239ee10f70b94b1f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Sep 2021 15:22:16 +0900 Subject: [PATCH 1922/2442] Add migration of existing settings --- osu.Game/OsuGameBase.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 600465b823..489d5b5e51 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -469,6 +469,25 @@ namespace osu.Game db.Context.RemoveRange(existingBindings); + var existingSettings = db.Context.DatabasedSetting; + + // only migrate data if the realm database is empty. + if (!usage.Realm.All().Any()) + { + foreach (var dkb in existingSettings) + { + usage.Realm.Add(new RealmSetting + { + ValueString = dkb.StringValue, + Key = dkb.Key, + RulesetID = dkb.RulesetID, + Variant = dkb.Variant + }); + } + } + + db.Context.RemoveRange(existingSettings); + usage.Commit(); } } From b87af3dd68e3de7b19483210e6d94437a6a6c651 Mon Sep 17 00:00:00 2001 From: AbstractQbit <38468635+AbstractQbit@users.noreply.github.com> Date: Wed, 15 Sep 2021 10:55:16 +0300 Subject: [PATCH 1923/2442] Move the inherited `AllowTrackAdjustments` into `OsuScreen` --- osu.Game/OsuGame.cs | 21 +++++---------------- osu.Game/Screens/IOsuScreen.cs | 2 +- osu.Game/Screens/OsuScreen.cs | 8 +++++++- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 499b561718..f2f925a778 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1075,6 +1075,11 @@ namespace osu.Game OverlayActivationMode.BindTo(newOsuScreen.OverlayActivationMode); API.Activity.BindTo(newOsuScreen.Activity); + if (newOsuScreen.AllowTrackAdjustments.HasValue) + MusicController.AllowTrackAdjustments = newOsuScreen.AllowTrackAdjustments.Value; + else + newOsuScreen.AllowTrackAdjustments = MusicController.AllowTrackAdjustments; + if (newOsuScreen.HideOverlaysOnEnter) CloseAllOverlays(); else @@ -1091,16 +1096,6 @@ namespace osu.Game { ScreenChanged(lastScreen, newScreen); Logger.Log($"Screen changed → {newScreen}"); - - // set AllowTrackAdjustments if new screen defines it, inherit otherwise - if (newScreen is IOsuScreen newOsuScreen && newOsuScreen.AllowTrackAdjustments.HasValue) - allowTrackAdjustmentsDict[newScreen] = newOsuScreen.AllowTrackAdjustments.Value; - else if (allowTrackAdjustmentsDict.ContainsKey(lastScreen)) - allowTrackAdjustmentsDict[newScreen] = allowTrackAdjustmentsDict[lastScreen]; - else - allowTrackAdjustmentsDict[newScreen] = true; - - MusicController.AllowTrackAdjustments = allowTrackAdjustmentsDict[newScreen]; } private void screenExited(IScreen lastScreen, IScreen newScreen) @@ -1108,16 +1103,10 @@ namespace osu.Game ScreenChanged(lastScreen, newScreen); Logger.Log($"Screen changed ← {newScreen}"); - allowTrackAdjustmentsDict.Remove(lastScreen); - if (newScreen == null) Exit(); - else - MusicController.AllowTrackAdjustments = allowTrackAdjustmentsDict[newScreen]; } - private readonly Dictionary allowTrackAdjustmentsDict = new Dictionary(); - IBindable ILocalUserPlayInfo.IsPlaying => LocalUserPlaying; } } diff --git a/osu.Game/Screens/IOsuScreen.cs b/osu.Game/Screens/IOsuScreen.cs index a1e4d9ed01..262bbfedc6 100644 --- a/osu.Game/Screens/IOsuScreen.cs +++ b/osu.Game/Screens/IOsuScreen.cs @@ -62,7 +62,7 @@ namespace osu.Game.Screens /// Whether mod track adjustments are allowed to be applied. /// Null means to inherit from the parent screen. /// - bool? AllowTrackAdjustments { get; } + bool? AllowTrackAdjustments { set; get; } /// /// Invoked when the back button has been pressed to close any overlays before exiting this . diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 78908b5d8a..0deaa3e80e 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -81,7 +81,13 @@ namespace osu.Game.Screens public virtual float BackgroundParallaxAmount => 1; - public virtual bool? AllowTrackAdjustments => null; + private bool? allowTrackAdjustments = null; + + public virtual bool? AllowTrackAdjustments + { + set => allowTrackAdjustments = value; + get => allowTrackAdjustments; + } public Bindable Beatmap { get; private set; } From 48cf98ef9325cfd40fca7ab91bb50cc102517c53 Mon Sep 17 00:00:00 2001 From: AbstractQbit <38468635+AbstractQbit@users.noreply.github.com> Date: Wed, 15 Sep 2021 11:00:49 +0300 Subject: [PATCH 1924/2442] Rephrase null meaning in `IOsuScreen.AllowTrackAdjustments` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Screens/IOsuScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/IOsuScreen.cs b/osu.Game/Screens/IOsuScreen.cs index 262bbfedc6..735853e462 100644 --- a/osu.Game/Screens/IOsuScreen.cs +++ b/osu.Game/Screens/IOsuScreen.cs @@ -60,7 +60,7 @@ namespace osu.Game.Screens /// /// Whether mod track adjustments are allowed to be applied. - /// Null means to inherit from the parent screen. + /// A value means that the parent screen's value of this setting will be used. /// bool? AllowTrackAdjustments { set; get; } From f54d554d3099ecf845573bbcb9bda1c4250edc3c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 15 Sep 2021 17:03:26 +0900 Subject: [PATCH 1925/2442] Extract removal to method --- osu.Game/Online/Rooms/Room.cs | 15 ++++++++++----- .../Components/ListingPollingComponent.cs | 4 +--- .../Components/SelectionPollingComponent.cs | 4 +--- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index d964060f10..5f71b4be4a 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -179,11 +179,7 @@ namespace osu.Game.Online.Rooms if (EndDate.Value != null && DateTimeOffset.Now >= EndDate.Value) Status.Value = new RoomStatusEnded(); - // Todo: This is not the best way/place to do this, but the intention is to display all playlist items when the room has ended, - // and display only the non-expired playlist items while the room is still active. In order to achieve this, all expired items are removed from the source Room. - // More refactoring is required before this can be done locally instead - DrawableRoomPlaylist is currently directly bound to the playlist to display items in the room. - if (!(Status.Value is RoomStatusEnded)) - other.Playlist.RemoveAll(i => i.Expired); + other.RemoveExpiredPlaylistItems(); if (!Playlist.SequenceEqual(other.Playlist)) { @@ -200,6 +196,15 @@ namespace osu.Game.Online.Rooms Position.Value = other.Position.Value; } + public void RemoveExpiredPlaylistItems() + { + // Todo: This is not the best way/place to do this, but the intention is to display all playlist items when the room has ended, + // and display only the non-expired playlist items while the room is still active. In order to achieve this, all expired items are removed from the source Room. + // More refactoring is required before this can be done locally instead - DrawableRoomPlaylist is currently directly bound to the playlist to display items in the room. + if (!(Status.Value is RoomStatusEnded)) + Playlist.RemoveAll(i => i.Expired); + } + public bool ShouldSerializeRoomID() => false; public bool ShouldSerializeHost() => false; public bool ShouldSerializeEndDate() => false; diff --git a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs index 5a74df6ab1..fcf7767958 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs @@ -58,9 +58,7 @@ namespace osu.Game.Screens.OnlinePlay.Components foreach (var incoming in result) { - // Copy the room to itself to populate some members (such as status and playlist expiry). - incoming.CopyFrom(incoming); - + incoming.RemoveExpiredPlaylistItems(); RoomManager.AddOrUpdateRoom(incoming); } diff --git a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs index 0a37d47e34..b9d2bdf23e 100644 --- a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs +++ b/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs @@ -39,9 +39,7 @@ namespace osu.Game.Screens.OnlinePlay.Components pollReq.Success += result => { - // Copy the room to itself to populate some members (such as status and playlist expiry). - result.CopyFrom(result); - + result.RemoveExpiredPlaylistItems(); RoomManager.AddOrUpdateRoom(result); tcs.SetResult(true); }; From 5bb741b4e8126327e9fd4ccd76c76142f8fab39a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Sep 2021 15:22:43 +0900 Subject: [PATCH 1926/2442] Remove migration of key bindings --- osu.Game/Database/OsuDbContext.cs | 9 +---- .../Input/Bindings/DatabasedKeyBinding.cs | 39 ------------------- osu.Game/OsuGameBase.cs | 20 +--------- 3 files changed, 2 insertions(+), 66 deletions(-) delete mode 100644 osu.Game/Input/Bindings/DatabasedKeyBinding.cs diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index 68d186c65d..1d8322aadd 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -12,7 +12,6 @@ using osu.Game.Configuration; using osu.Game.IO; using osu.Game.Rulesets; using osu.Game.Scoring; -using DatabasedKeyBinding = osu.Game.Input.Bindings.DatabasedKeyBinding; using LogLevel = Microsoft.Extensions.Logging.LogLevel; using osu.Game.Skinning; @@ -24,14 +23,13 @@ namespace osu.Game.Database public DbSet BeatmapDifficulty { get; set; } public DbSet BeatmapMetadata { get; set; } public DbSet BeatmapSetInfo { get; set; } - public DbSet DatabasedSetting { get; set; } public DbSet FileInfo { get; set; } public DbSet RulesetInfo { get; set; } public DbSet SkinInfo { get; set; } public DbSet ScoreInfo { get; set; } // migrated to realm - public DbSet DatabasedKeyBinding { get; set; } + public DbSet DatabasedSetting { get; set; } private readonly string connectionString; @@ -138,11 +136,6 @@ namespace osu.Game.Database modelBuilder.Entity().HasIndex(b => b.Hash).IsUnique(); modelBuilder.Entity().HasIndex(b => b.DeletePending); - modelBuilder.Entity().HasIndex(b => new { b.RulesetID, b.Variant }); - modelBuilder.Entity().HasIndex(b => b.IntAction); - modelBuilder.Entity().Ignore(b => b.KeyCombination); - modelBuilder.Entity().Ignore(b => b.Action); - modelBuilder.Entity().HasIndex(b => new { b.RulesetID, b.Variant }); modelBuilder.Entity().HasIndex(b => b.Hash).IsUnique(); diff --git a/osu.Game/Input/Bindings/DatabasedKeyBinding.cs b/osu.Game/Input/Bindings/DatabasedKeyBinding.cs deleted file mode 100644 index ad3493d0fc..0000000000 --- a/osu.Game/Input/Bindings/DatabasedKeyBinding.cs +++ /dev/null @@ -1,39 +0,0 @@ -// 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.DataAnnotations.Schema; -using osu.Framework.Input.Bindings; -using osu.Game.Database; - -namespace osu.Game.Input.Bindings -{ - [Table("KeyBinding")] - public class DatabasedKeyBinding : IKeyBinding, IHasPrimaryKey - { - public int ID { get; set; } - - public int? RulesetID { get; set; } - - public int? Variant { get; set; } - - [Column("Keys")] - public string KeysString { get; set; } - - [Column("Action")] - public int IntAction { get; set; } - - [NotMapped] - public KeyCombination KeyCombination - { - get => KeysString; - set => KeysString = value.ToString(); - } - - [NotMapped] - public object Action - { - get => IntAction; - set => IntAction = (int)value; - } - } -} diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 489d5b5e51..ee97b27265 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -450,25 +450,7 @@ namespace osu.Game using (var db = contextFactory.GetForWrite()) using (var usage = realmFactory.GetForWrite()) { - var existingBindings = db.Context.DatabasedKeyBinding; - - // only migrate data if the realm database is empty. - if (!usage.Realm.All().Any()) - { - foreach (var dkb in existingBindings) - { - usage.Realm.Add(new RealmKeyBinding - { - KeyCombinationString = dkb.KeyCombination.ToString(), - ActionInt = (int)dkb.Action, - RulesetID = dkb.RulesetID, - Variant = dkb.Variant - }); - } - } - - db.Context.RemoveRange(existingBindings); - + // migrate ruleset settings. can be removed 20220315. var existingSettings = db.Context.DatabasedSetting; // only migrate data if the realm database is empty. From c36a67d06e718d7e5d05730c7110cb141d2e8ecb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Sep 2021 15:01:48 +0900 Subject: [PATCH 1927/2442] Fix some tests failing due to using a locally constructed ruleset --- osu.Game/Rulesets/RulesetConfigCache.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/RulesetConfigCache.cs b/osu.Game/Rulesets/RulesetConfigCache.cs index afdf3219df..f2c3121320 100644 --- a/osu.Game/Rulesets/RulesetConfigCache.cs +++ b/osu.Game/Rulesets/RulesetConfigCache.cs @@ -52,7 +52,7 @@ namespace osu.Game.Rulesets return null; if (!configCache.TryGetValue(ruleset.RulesetInfo.ID.Value, out var config)) - return null; + return ruleset.CreateConfig(realmFactory); return config; } From 520e5507645fe640073c25c9fc9e62e768a8ea80 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Sep 2021 16:08:31 +0900 Subject: [PATCH 1928/2442] Bring back `SettingsStore` to avoid changing ruleset API for now Also fixes some remaining test failures due to locally constructed rulesets that are not being tracked by the game. --- .../ManiaRulesetConfigManager.cs | 6 +-- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 4 +- .../Configuration/OsuRulesetConfigManager.cs | 6 +-- osu.Game.Rulesets.Osu/OsuRuleset.cs | 44 +++++++++---------- .../Testing/TestSceneRulesetDependencies.cs | 4 +- .../Configuration/DatabasedConfigManager.cs | 32 +++++++------- osu.Game/Configuration/SettingsStore.cs | 20 +++++++++ .../Settings/RulesetSettingsSubsection.cs | 4 +- .../Configuration/RulesetConfigManager.cs | 5 +-- osu.Game/Rulesets/Ruleset.cs | 30 ++++++------- osu.Game/Rulesets/RulesetConfigCache.cs | 9 +++- 11 files changed, 95 insertions(+), 69 deletions(-) create mode 100644 osu.Game/Configuration/SettingsStore.cs diff --git a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs index d9bd0ab609..ac8168dfc9 100644 --- a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs +++ b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs @@ -3,7 +3,7 @@ using System; using osu.Framework.Configuration.Tracking; -using osu.Game.Database; +using osu.Game.Configuration; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Mania.UI; @@ -11,8 +11,8 @@ namespace osu.Game.Rulesets.Mania.Configuration { public class ManiaRulesetConfigManager : RulesetConfigManager { - public ManiaRulesetConfigManager(RealmContextFactory realmFactory, RulesetInfo ruleset, int? variant = null) - : base(realmFactory, ruleset, variant) + public ManiaRulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null) + : base(settings, ruleset, variant) { } diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 7ad27b94eb..1f79dae280 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -17,7 +17,7 @@ using osu.Game.Graphics; using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Replays.Types; using osu.Game.Beatmaps.Legacy; -using osu.Game.Database; +using osu.Game.Configuration; using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Difficulty; @@ -278,7 +278,7 @@ namespace osu.Game.Rulesets.Mania public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame(); - public override IRulesetConfigManager CreateConfig(RealmContextFactory realmFactory) => new ManiaRulesetConfigManager(realmFactory, RulesetInfo); + public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new ManiaRulesetConfigManager(settings, RulesetInfo); public override RulesetSettingsSubsection CreateSettings() => new ManiaSettingsSubsection(this); diff --git a/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs b/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs index 23c25c6558..9589fd576f 100644 --- a/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs +++ b/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs @@ -1,7 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Database; +using osu.Game.Configuration; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.UI; @@ -9,8 +9,8 @@ namespace osu.Game.Rulesets.Osu.Configuration { public class OsuRulesetConfigManager : RulesetConfigManager { - public OsuRulesetConfigManager(RealmContextFactory realmFactory, RulesetInfo ruleset, int? variant = null) - : base(realmFactory, ruleset, variant) + public OsuRulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null) + : base(settings, ruleset, variant) { } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index b7cb0c5313..f4a93a571d 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -1,41 +1,41 @@ // 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 osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.UI; using System.Collections.Generic; -using System.Linq; -using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.Bindings; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Legacy; -using osu.Game.Database; -using osu.Game.Graphics; using osu.Game.Overlays.Settings; +using osu.Framework.Input.Bindings; +using osu.Game.Rulesets.Osu.Edit; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Osu.Replays; +using osu.Game.Rulesets.Replays.Types; +using osu.Game.Beatmaps.Legacy; +using osu.Game.Configuration; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Difficulty; -using osu.Game.Rulesets.Osu.Edit; -using osu.Game.Rulesets.Osu.Edit.Setup; -using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.Osu.Scoring; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; +using osu.Game.Skinning; +using System; +using System.Linq; +using osu.Framework.Extensions.EnumExtensions; +using osu.Game.Rulesets.Osu.Edit.Setup; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Skinning.Legacy; using osu.Game.Rulesets.Osu.Statistics; -using osu.Game.Rulesets.Osu.UI; -using osu.Game.Rulesets.Replays.Types; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; -using osu.Game.Scoring; using osu.Game.Screens.Edit.Setup; using osu.Game.Screens.Ranking.Statistics; -using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu { @@ -229,7 +229,7 @@ namespace osu.Game.Rulesets.Osu public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame(); - public override IRulesetConfigManager CreateConfig(RealmContextFactory realmFactory) => new OsuRulesetConfigManager(realmFactory, RulesetInfo); + public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new OsuRulesetConfigManager(settings, RulesetInfo); protected override IEnumerable GetValidHitResults() { diff --git a/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs b/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs index 12960fd4d0..8c6932e792 100644 --- a/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs +++ b/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs @@ -12,7 +12,7 @@ using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Framework.Testing; using osu.Game.Beatmaps; -using osu.Game.Database; +using osu.Game.Configuration; using osu.Game.Rulesets; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Difficulty; @@ -74,7 +74,7 @@ namespace osu.Game.Tests.Testing } public override IResourceStore CreateResourceStore() => new NamespacedResourceStore(TestResources.GetStore(), @"Resources"); - public override IRulesetConfigManager CreateConfig(RealmContextFactory realmFactory) => new TestRulesetConfigManager(); + public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new TestRulesetConfigManager(); public override IEnumerable GetModsFor(ModType type) => Array.Empty(); public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => null; diff --git a/osu.Game/Configuration/DatabasedConfigManager.cs b/osu.Game/Configuration/DatabasedConfigManager.cs index 0514d11e24..d6988e31b5 100644 --- a/osu.Game/Configuration/DatabasedConfigManager.cs +++ b/osu.Game/Configuration/DatabasedConfigManager.cs @@ -18,13 +18,13 @@ namespace osu.Game.Configuration private readonly int? variant; - private List databasedSettings; + private List databasedSettings = new List(); private readonly RulesetInfo ruleset; - protected DatabasedConfigManager(RealmContextFactory realmFactory, RulesetInfo ruleset = null, int? variant = null) + protected DatabasedConfigManager(SettingsStore store = null, RulesetInfo ruleset = null, int? variant = null) { - this.realmFactory = realmFactory; + realmFactory = store?.Realm; this.ruleset = ruleset; this.variant = variant; @@ -37,8 +37,11 @@ namespace osu.Game.Configuration { var rulesetID = ruleset?.ID; - // As long as RulesetConfigCache exists, there is no need to subscribe to realm events. - databasedSettings = realmFactory.Context.All().Where(b => b.RulesetID == rulesetID && b.Variant == variant).ToList(); + if (realmFactory != null) + { + // As long as RulesetConfigCache exists, there is no need to subscribe to realm events. + databasedSettings = realmFactory.Context.All().Where(b => b.RulesetID == rulesetID && b.Variant == variant).ToList(); + } } protected override bool PerformSave() @@ -59,23 +62,22 @@ namespace osu.Game.Configuration } else { - realmFactory.Context.Write(() => + setting = new RealmSetting { - realmFactory.Context.Add(setting = new RealmSetting - { - Key = lookup.ToString(), - Value = bindable.Value, - RulesetID = ruleset?.ID, - Variant = variant, - }); - }); + Key = lookup.ToString(), + Value = bindable.Value, + RulesetID = ruleset?.ID, + Variant = variant, + }; + + realmFactory?.Context.Write(() => realmFactory.Context.Add(setting)); databasedSettings.Add(setting); } bindable.ValueChanged += b => { - realmFactory.Context.Write(() => setting.Value = b.NewValue); + realmFactory?.Context.Write(() => setting.Value = b.NewValue); }; } } diff --git a/osu.Game/Configuration/SettingsStore.cs b/osu.Game/Configuration/SettingsStore.cs new file mode 100644 index 0000000000..2bba20fb09 --- /dev/null +++ b/osu.Game/Configuration/SettingsStore.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Database; + +namespace osu.Game.Configuration +{ + public class SettingsStore + { + // this class mostly exists as a wrapper to avoid breaking the ruleset API (see usage in RulesetConfigManager). + // it may cease to exist going forward, depending on how the structure of the config data layer changes. + + public readonly RealmContextFactory Realm; + + public SettingsStore(RealmContextFactory realmFactory) + { + Realm = realmFactory; + } + } +} diff --git a/osu.Game/Overlays/Settings/RulesetSettingsSubsection.cs b/osu.Game/Overlays/Settings/RulesetSettingsSubsection.cs index 9ff5b8935a..93b07fbac7 100644 --- a/osu.Game/Overlays/Settings/RulesetSettingsSubsection.cs +++ b/osu.Game/Overlays/Settings/RulesetSettingsSubsection.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Game.Database; +using osu.Game.Configuration; using osu.Game.Rulesets; using osu.Game.Rulesets.Configuration; @@ -10,7 +10,7 @@ namespace osu.Game.Overlays.Settings { /// /// A which provides subclasses with the - /// from the 's . + /// from the 's . /// public abstract class RulesetSettingsSubsection : SettingsSubsection { diff --git a/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs index 17dbd30103..0ff3455f00 100644 --- a/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs +++ b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs @@ -3,15 +3,14 @@ using System; using osu.Game.Configuration; -using osu.Game.Database; namespace osu.Game.Rulesets.Configuration { public abstract class RulesetConfigManager : DatabasedConfigManager, IRulesetConfigManager where TLookup : struct, Enum { - protected RulesetConfigManager(RealmContextFactory realmFactory, RulesetInfo ruleset, int? variant = null) - : base(realmFactory, ruleset, variant) + protected RulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null) + : base(settings, ruleset, variant) { } } diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index cf4ea4f01d..b0c3836774 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -5,32 +5,32 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; -using JetBrains.Annotations; -using osu.Framework.Extensions; -using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; using osu.Framework.IO.Stores; -using osu.Framework.Testing; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Legacy; -using osu.Game.Database; -using osu.Game.Extensions; using osu.Game.Overlays.Settings; -using osu.Game.Rulesets.Configuration; -using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Filter; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Replays.Types; -using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; +using osu.Game.Beatmaps.Legacy; +using osu.Game.Configuration; +using osu.Game.Rulesets.Configuration; +using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; -using osu.Game.Screens.Edit.Setup; -using osu.Game.Screens.Ranking.Statistics; using osu.Game.Skinning; using osu.Game.Users; +using JetBrains.Annotations; +using osu.Framework.Extensions; +using osu.Framework.Extensions.EnumExtensions; +using osu.Framework.Testing; +using osu.Game.Extensions; +using osu.Game.Rulesets.Filter; +using osu.Game.Screens.Edit.Setup; +using osu.Game.Screens.Ranking.Statistics; namespace osu.Game.Rulesets { @@ -262,8 +262,8 @@ namespace osu.Game.Rulesets /// /// Creates the for this . /// - /// The to store the settings. - public virtual IRulesetConfigManager CreateConfig(RealmContextFactory realmFactory) => null; + /// The to store the settings. + public virtual IRulesetConfigManager CreateConfig(SettingsStore settings) => null; /// /// A unique short name to reference this ruleset in online requests. diff --git a/osu.Game/Rulesets/RulesetConfigCache.cs b/osu.Game/Rulesets/RulesetConfigCache.cs index f2c3121320..aeac052673 100644 --- a/osu.Game/Rulesets/RulesetConfigCache.cs +++ b/osu.Game/Rulesets/RulesetConfigCache.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using osu.Framework.Graphics; +using osu.Game.Configuration; using osu.Game.Database; using osu.Game.Rulesets.Configuration; @@ -30,13 +31,15 @@ namespace osu.Game.Rulesets { base.LoadComplete(); + var settingsStore = new SettingsStore(realmFactory); + // let's keep things simple for now and just retrieve all the required configs at startup.. foreach (var ruleset in rulesets.AvailableRulesets) { if (ruleset.ID == null) continue; - configCache[ruleset.ID.Value] = ruleset.CreateInstance().CreateConfig(realmFactory); + configCache[ruleset.ID.Value] = ruleset.CreateInstance().CreateConfig(settingsStore); } } @@ -52,7 +55,9 @@ namespace osu.Game.Rulesets return null; if (!configCache.TryGetValue(ruleset.RulesetInfo.ID.Value, out var config)) - return ruleset.CreateConfig(realmFactory); + // any ruleset request which wasn't initialised on startup should not be stored to realm. + // this should only be used by tests. + return ruleset.CreateConfig(null); return config; } From 80ecf81be34ce87b350612b3932913e2556cbce8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Sep 2021 16:55:42 +0900 Subject: [PATCH 1929/2442] Rename all databased setting classes to be specific to rulesets for now --- .../Configuration/DatabasedConfigManager.cs | 84 ------------------- ...RealmSetting.cs => RealmRulesetSetting.cs} | 4 +- osu.Game/OsuGameBase.cs | 4 +- .../Configuration/RulesetConfigManager.cs | 73 +++++++++++++++- 4 files changed, 74 insertions(+), 91 deletions(-) delete mode 100644 osu.Game/Configuration/DatabasedConfigManager.cs rename osu.Game/Configuration/{RealmSetting.cs => RealmRulesetSetting.cs} (87%) diff --git a/osu.Game/Configuration/DatabasedConfigManager.cs b/osu.Game/Configuration/DatabasedConfigManager.cs deleted file mode 100644 index d6988e31b5..0000000000 --- a/osu.Game/Configuration/DatabasedConfigManager.cs +++ /dev/null @@ -1,84 +0,0 @@ -// 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 osu.Framework.Bindables; -using osu.Framework.Configuration; -using osu.Game.Database; -using osu.Game.Rulesets; - -namespace osu.Game.Configuration -{ - public abstract class DatabasedConfigManager : ConfigManager - where TLookup : struct, Enum - { - private readonly RealmContextFactory realmFactory; - - private readonly int? variant; - - private List databasedSettings = new List(); - - private readonly RulesetInfo ruleset; - - protected DatabasedConfigManager(SettingsStore store = null, RulesetInfo ruleset = null, int? variant = null) - { - realmFactory = store?.Realm; - this.ruleset = ruleset; - this.variant = variant; - - Load(); - - InitialiseDefaults(); - } - - protected override void PerformLoad() - { - var rulesetID = ruleset?.ID; - - if (realmFactory != null) - { - // As long as RulesetConfigCache exists, there is no need to subscribe to realm events. - databasedSettings = realmFactory.Context.All().Where(b => b.RulesetID == rulesetID && b.Variant == variant).ToList(); - } - } - - protected override bool PerformSave() - { - // do nothing, realm saves immediately - return true; - } - - protected override void AddBindable(TLookup lookup, Bindable bindable) - { - base.AddBindable(lookup, bindable); - - var setting = databasedSettings.Find(s => s.Key == lookup.ToString()); - - if (setting != null) - { - bindable.Parse(setting.Value); - } - else - { - setting = new RealmSetting - { - Key = lookup.ToString(), - Value = bindable.Value, - RulesetID = ruleset?.ID, - Variant = variant, - }; - - realmFactory?.Context.Write(() => realmFactory.Context.Add(setting)); - - databasedSettings.Add(setting); - } - - bindable.ValueChanged += b => - { - realmFactory?.Context.Write(() => setting.Value = b.NewValue); - }; - } - } -} diff --git a/osu.Game/Configuration/RealmSetting.cs b/osu.Game/Configuration/RealmRulesetSetting.cs similarity index 87% rename from osu.Game/Configuration/RealmSetting.cs rename to osu.Game/Configuration/RealmRulesetSetting.cs index b773796067..7623d1f948 100644 --- a/osu.Game/Configuration/RealmSetting.cs +++ b/osu.Game/Configuration/RealmRulesetSetting.cs @@ -7,8 +7,8 @@ using Realms; namespace osu.Game.Configuration { - [MapTo(@"Setting")] - public class RealmSetting : RealmObject, IHasGuidPrimaryKey + [MapTo(@"RulesetSetting")] + public class RealmRulesetSetting : RealmObject, IHasGuidPrimaryKey { [PrimaryKey] public Guid ID { get; set; } = Guid.NewGuid(); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index ee97b27265..4e4061db9d 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -454,11 +454,11 @@ namespace osu.Game var existingSettings = db.Context.DatabasedSetting; // only migrate data if the realm database is empty. - if (!usage.Realm.All().Any()) + if (!usage.Realm.All().Any()) { foreach (var dkb in existingSettings) { - usage.Realm.Add(new RealmSetting + usage.Realm.Add(new RealmRulesetSetting { ValueString = dkb.StringValue, Key = dkb.Key, diff --git a/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs index 0ff3455f00..3f5472e52c 100644 --- a/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs +++ b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs @@ -2,16 +2,83 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Bindables; +using osu.Framework.Configuration; using osu.Game.Configuration; +using osu.Game.Database; namespace osu.Game.Rulesets.Configuration { - public abstract class RulesetConfigManager : DatabasedConfigManager, IRulesetConfigManager + public abstract class RulesetConfigManager : ConfigManager, IRulesetConfigManager where TLookup : struct, Enum { - protected RulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null) - : base(settings, ruleset, variant) + private readonly RealmContextFactory realmFactory; + + private readonly int? variant; + + private List databasedSettings = new List(); + + private readonly RulesetInfo ruleset; + + protected RulesetConfigManager(SettingsStore store, RulesetInfo ruleset, int? variant = null) { + realmFactory = store?.Realm; + this.ruleset = ruleset; + this.variant = variant; + + Load(); + + InitialiseDefaults(); + } + + protected override void PerformLoad() + { + var rulesetID = ruleset?.ID; + + if (realmFactory != null) + { + // As long as RulesetConfigCache exists, there is no need to subscribe to realm events. + databasedSettings = realmFactory.Context.All().Where(b => b.RulesetID == rulesetID && b.Variant == variant).ToList(); + } + } + + protected override bool PerformSave() + { + // do nothing, realm saves immediately + return true; + } + + protected override void AddBindable(TLookup lookup, Bindable bindable) + { + base.AddBindable(lookup, bindable); + + var setting = databasedSettings.Find(s => s.Key == lookup.ToString()); + + if (setting != null) + { + bindable.Parse(setting.Value); + } + else + { + setting = new RealmRulesetSetting + { + Key = lookup.ToString(), + Value = bindable.Value, + RulesetID = ruleset?.ID, + Variant = variant, + }; + + realmFactory?.Context.Write(() => realmFactory.Context.Add(setting)); + + databasedSettings.Add(setting); + } + + bindable.ValueChanged += b => + { + realmFactory?.Context.Write(() => setting.Value = b.NewValue); + }; } } } From dcfe9c67e3019f91e5b92f1e433dae48d4c79843 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Sep 2021 17:01:31 +0900 Subject: [PATCH 1930/2442] Make ruleset id non-nullable --- osu.Game/Configuration/RealmRulesetSetting.cs | 3 ++- osu.Game/OsuGameBase.cs | 4 +++- .../Configuration/RulesetConfigManager.cs | 15 +++++++++------ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/osu.Game/Configuration/RealmRulesetSetting.cs b/osu.Game/Configuration/RealmRulesetSetting.cs index 7623d1f948..c88a261ad2 100644 --- a/osu.Game/Configuration/RealmRulesetSetting.cs +++ b/osu.Game/Configuration/RealmRulesetSetting.cs @@ -13,7 +13,8 @@ namespace osu.Game.Configuration [PrimaryKey] public Guid ID { get; set; } = Guid.NewGuid(); - public int? RulesetID { get; set; } + [Indexed] + public int RulesetID { get; set; } public int? Variant { get; set; } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 4e4061db9d..36406ded08 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -458,11 +458,13 @@ namespace osu.Game { foreach (var dkb in existingSettings) { + if (dkb.RulesetID == null) continue; + usage.Realm.Add(new RealmRulesetSetting { ValueString = dkb.StringValue, Key = dkb.Key, - RulesetID = dkb.RulesetID, + RulesetID = dkb.RulesetID.Value, Variant = dkb.Variant }); } diff --git a/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs index 3f5472e52c..a97976392a 100644 --- a/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs +++ b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs @@ -20,12 +20,17 @@ namespace osu.Game.Rulesets.Configuration private List databasedSettings = new List(); - private readonly RulesetInfo ruleset; + private readonly int rulesetId; protected RulesetConfigManager(SettingsStore store, RulesetInfo ruleset, int? variant = null) { realmFactory = store?.Realm; - this.ruleset = ruleset; + + if (realmFactory != null && !ruleset.ID.HasValue) + throw new InvalidOperationException("Attempted to add databased settings for a non-databased ruleset"); + + rulesetId = ruleset.ID ?? -1; + this.variant = variant; Load(); @@ -35,12 +40,10 @@ namespace osu.Game.Rulesets.Configuration protected override void PerformLoad() { - var rulesetID = ruleset?.ID; - if (realmFactory != null) { // As long as RulesetConfigCache exists, there is no need to subscribe to realm events. - databasedSettings = realmFactory.Context.All().Where(b => b.RulesetID == rulesetID && b.Variant == variant).ToList(); + databasedSettings = realmFactory.Context.All().Where(b => b.RulesetID == rulesetId && b.Variant == variant).ToList(); } } @@ -66,7 +69,7 @@ namespace osu.Game.Rulesets.Configuration { Key = lookup.ToString(), Value = bindable.Value, - RulesetID = ruleset?.ID, + RulesetID = rulesetId, Variant = variant, }; From 15e3f95c87dfef94deccc3f0d349c1b8c32bfb3b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Sep 2021 17:02:39 +0900 Subject: [PATCH 1931/2442] Remove remnants of `DatabasedSetting` from `SkinInfo` This was never used --- osu.Game/Skinning/SkinInfo.cs | 3 --- osu.Game/Skinning/SkinStore.cs | 6 ------ 2 files changed, 9 deletions(-) diff --git a/osu.Game/Skinning/SkinInfo.cs b/osu.Game/Skinning/SkinInfo.cs index 851d71f914..2bf8668ec6 100644 --- a/osu.Game/Skinning/SkinInfo.cs +++ b/osu.Game/Skinning/SkinInfo.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using osu.Framework.Extensions.ObjectExtensions; -using osu.Game.Configuration; using osu.Game.Database; using osu.Game.Extensions; using osu.Game.IO; @@ -39,8 +38,6 @@ namespace osu.Game.Skinning public List Files { get; set; } = new List(); - public List Settings { get; set; } - public bool DeletePending { get; set; } public static SkinInfo Default { get; } = new SkinInfo diff --git a/osu.Game/Skinning/SkinStore.cs b/osu.Game/Skinning/SkinStore.cs index 153eeda130..31cadb0a24 100644 --- a/osu.Game/Skinning/SkinStore.cs +++ b/osu.Game/Skinning/SkinStore.cs @@ -1,8 +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.Linq; -using Microsoft.EntityFrameworkCore; using osu.Framework.Platform; using osu.Game.Database; @@ -14,9 +12,5 @@ namespace osu.Game.Skinning : base(contextFactory, storage) { } - - protected override IQueryable AddIncludesForDeletion(IQueryable query) => - base.AddIncludesForDeletion(query) - .Include(s => s.Settings); // don't include FileInfo. these are handled by the FileStore itself. } } From a150fb29960bde3dcd9150dc4143eaea4cbead5d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Sep 2021 17:09:02 +0900 Subject: [PATCH 1932/2442] Add nullability directive and make variant non-nullable --- osu.Game/Configuration/RealmRulesetSetting.cs | 9 ++++++--- osu.Game/OsuGameBase.cs | 2 +- osu.Game/Rulesets/Configuration/RulesetConfigManager.cs | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game/Configuration/RealmRulesetSetting.cs b/osu.Game/Configuration/RealmRulesetSetting.cs index c88a261ad2..d1e1bb9ea2 100644 --- a/osu.Game/Configuration/RealmRulesetSetting.cs +++ b/osu.Game/Configuration/RealmRulesetSetting.cs @@ -5,6 +5,8 @@ using System; using osu.Game.Database; using Realms; +#nullable enable + namespace osu.Game.Configuration { [MapTo(@"RulesetSetting")] @@ -16,12 +18,13 @@ namespace osu.Game.Configuration [Indexed] public int RulesetID { get; set; } - public int? Variant { get; set; } + [Indexed] + public int Variant { get; set; } - public string Key { get; set; } + public string Key { get; set; } = string.Empty; [MapTo(nameof(Value))] - public string ValueString { get; set; } + public string ValueString { get; set; } = string.Empty; public object Value { diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 36406ded08..46614ca0ad 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -465,7 +465,7 @@ namespace osu.Game ValueString = dkb.StringValue, Key = dkb.Key, RulesetID = dkb.RulesetID.Value, - Variant = dkb.Variant + Variant = dkb.Variant ?? 0, }); } } diff --git a/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs index a97976392a..f4b4e2978c 100644 --- a/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs +++ b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Configuration { private readonly RealmContextFactory realmFactory; - private readonly int? variant; + private readonly int variant; private List databasedSettings = new List(); @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Configuration rulesetId = ruleset.ID ?? -1; - this.variant = variant; + this.variant = variant ?? 0; Load(); From a1d325cb22fb066f82f8561d46c2757214b38573 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Sep 2021 17:12:00 +0900 Subject: [PATCH 1933/2442] Mark key and value non-nullable (at realm end) and simplify `Value` logic --- osu.Game/Configuration/RealmRulesetSetting.cs | 13 ++++--------- osu.Game/OsuGameBase.cs | 2 +- .../Rulesets/Configuration/RulesetConfigManager.cs | 4 ++-- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/osu.Game/Configuration/RealmRulesetSetting.cs b/osu.Game/Configuration/RealmRulesetSetting.cs index d1e1bb9ea2..07e56ad8dd 100644 --- a/osu.Game/Configuration/RealmRulesetSetting.cs +++ b/osu.Game/Configuration/RealmRulesetSetting.cs @@ -21,17 +21,12 @@ namespace osu.Game.Configuration [Indexed] public int Variant { get; set; } + [Required] public string Key { get; set; } = string.Empty; - [MapTo(nameof(Value))] - public string ValueString { get; set; } = string.Empty; + [Required] + public string Value { get; set; } = string.Empty; - public object Value - { - get => ValueString; - set => ValueString = value.ToString(); - } - - public override string ToString() => $"{Key}=>{Value}"; + public override string ToString() => $"{Key} => {Value}"; } } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 46614ca0ad..59a05aec4f 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -462,8 +462,8 @@ namespace osu.Game usage.Realm.Add(new RealmRulesetSetting { - ValueString = dkb.StringValue, Key = dkb.Key, + Value = dkb.StringValue, RulesetID = dkb.RulesetID.Value, Variant = dkb.Variant ?? 0, }); diff --git a/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs index f4b4e2978c..a0ec8e3e0e 100644 --- a/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs +++ b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Configuration setting = new RealmRulesetSetting { Key = lookup.ToString(), - Value = bindable.Value, + Value = bindable.Value.ToString(), RulesetID = rulesetId, Variant = variant, }; @@ -80,7 +80,7 @@ namespace osu.Game.Rulesets.Configuration bindable.ValueChanged += b => { - realmFactory?.Context.Write(() => setting.Value = b.NewValue); + realmFactory?.Context.Write(() => setting.Value = b.NewValue.ToString()); }; } } From cdb44d7239e3ad92a56e40bd04ce5a6ee7ad86c7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 15 Sep 2021 17:16:08 +0900 Subject: [PATCH 1934/2442] Fix match footer test scene not working in visual testing --- .../Visual/Multiplayer/TestSceneMultiplayerMatchFooter.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchFooter.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchFooter.cs index 4e08ffef17..44a8d7b439 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchFooter.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchFooter.cs @@ -3,6 +3,7 @@ using NUnit.Framework; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; @@ -15,11 +16,13 @@ namespace osu.Game.Tests.Visual.Multiplayer { SelectedRoom.Value = new Room(); - Child = new MultiplayerMatchFooter + Child = new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Height = 50 + RelativeSizeAxes = Axes.X, + Height = 50, + Child = new MultiplayerMatchFooter() }; }); } From 4f1db5af40c79e038737eef75ab6859641b12fa4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Sep 2021 17:25:07 +0900 Subject: [PATCH 1935/2442] Attach migration memo to `DatabasedSetting` class for visibility --- osu.Game/Configuration/DatabasedSetting.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Configuration/DatabasedSetting.cs b/osu.Game/Configuration/DatabasedSetting.cs index f5c92b3029..fe1d51d57f 100644 --- a/osu.Game/Configuration/DatabasedSetting.cs +++ b/osu.Game/Configuration/DatabasedSetting.cs @@ -7,7 +7,7 @@ using osu.Game.Database; namespace osu.Game.Configuration { [Table("Settings")] - public class DatabasedSetting : IHasPrimaryKey + public class DatabasedSetting : IHasPrimaryKey // can be removed 20220315. { public int ID { get; set; } From 9b101ea9eb6576d7d579150ce90eb887ff61f6e8 Mon Sep 17 00:00:00 2001 From: AbstractQbit <38468635+AbstractQbit@users.noreply.github.com> Date: Wed, 15 Sep 2021 11:40:23 +0300 Subject: [PATCH 1936/2442] Add a test for `AllowTrackAdjustments` --- .../Menus/TestSceneMusicActionHandling.cs | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs index 9037338e23..50226ae2e2 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs @@ -9,6 +9,7 @@ using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Input.Bindings; using osu.Game.Overlays; +using osu.Game.Screens; using osu.Game.Tests.Resources; namespace osu.Game.Tests.Visual.Menus @@ -79,5 +80,55 @@ namespace osu.Game.Tests.Visual.Menus trackChangeQueue.Count == 1 && trackChangeQueue.Dequeue().changeDirection == TrackChangeDirection.Next); } + + [Test] + public void TestAllowTrackAdjustments() + { + AddStep("push allowing screen", () => Game.ScreenStack.Push(new AllowScreen())); + AddAssert("allows adjustments", () => Game.MusicController.AllowTrackAdjustments); + + AddStep("push inheriting screen", () => Game.ScreenStack.Push(new InheritScreen())); + AddAssert("allows adjustments", () => Game.MusicController.AllowTrackAdjustments); + + AddStep("push disallowing screen", () => Game.ScreenStack.Push(new DisallowScreen())); + AddAssert("disallows adjustments", () => !Game.MusicController.AllowTrackAdjustments); + + AddStep("push inheriting screen", () => Game.ScreenStack.Push(new InheritScreen())); + AddAssert("disallows adjustments", () => !Game.MusicController.AllowTrackAdjustments); + + AddStep("push inheriting screen", () => Game.ScreenStack.Push(new InheritScreen())); + AddAssert("disallows adjustments", () => !Game.MusicController.AllowTrackAdjustments); + + AddStep("push allowing screen", () => Game.ScreenStack.Push(new AllowScreen())); + AddAssert("allows adjustments", () => Game.MusicController.AllowTrackAdjustments); + + // Now start exiting from screens + AddStep("exit screen", () => Game.ScreenStack.Exit()); + AddAssert("disallows adjustments", () => !Game.MusicController.AllowTrackAdjustments); + + AddStep("exit screen", () => Game.ScreenStack.Exit()); + AddAssert("disallows adjustments", () => !Game.MusicController.AllowTrackAdjustments); + + AddStep("exit screen", () => Game.ScreenStack.Exit()); + AddAssert("disallows adjustments", () => !Game.MusicController.AllowTrackAdjustments); + + AddStep("exit screen", () => Game.ScreenStack.Exit()); + AddAssert("allows adjustments", () => Game.MusicController.AllowTrackAdjustments); + + AddStep("exit screen", () => Game.ScreenStack.Exit()); + AddAssert("allows adjustments", () => Game.MusicController.AllowTrackAdjustments); + } + + private class AllowScreen : OsuScreen + { + public override bool? AllowTrackAdjustments => true; + } + + private class DisallowScreen : OsuScreen + { + public override bool? AllowTrackAdjustments => false; + } + + private class InheritScreen : OsuScreen { } } } From 1181317c729455c2e81b409933c37167e84d55df Mon Sep 17 00:00:00 2001 From: AbstractQbit <38468635+AbstractQbit@users.noreply.github.com> Date: Wed, 15 Sep 2021 12:01:56 +0300 Subject: [PATCH 1937/2442] Fix issues found by code quality ci --- .../Visual/Menus/TestSceneMusicActionHandling.cs | 4 +++- osu.Game/Screens/IOsuScreen.cs | 2 +- osu.Game/Screens/OsuScreen.cs | 8 +------- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs index 50226ae2e2..79acaedb73 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs @@ -129,6 +129,8 @@ namespace osu.Game.Tests.Visual.Menus public override bool? AllowTrackAdjustments => false; } - private class InheritScreen : OsuScreen { } + private class InheritScreen : OsuScreen + { + } } } diff --git a/osu.Game/Screens/IOsuScreen.cs b/osu.Game/Screens/IOsuScreen.cs index 735853e462..fd884586d1 100644 --- a/osu.Game/Screens/IOsuScreen.cs +++ b/osu.Game/Screens/IOsuScreen.cs @@ -62,7 +62,7 @@ namespace osu.Game.Screens /// Whether mod track adjustments are allowed to be applied. /// A value means that the parent screen's value of this setting will be used. /// - bool? AllowTrackAdjustments { set; get; } + bool? AllowTrackAdjustments { get; set; } /// /// Invoked when the back button has been pressed to close any overlays before exiting this . diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 0deaa3e80e..9b4d7f9eda 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -81,13 +81,7 @@ namespace osu.Game.Screens public virtual float BackgroundParallaxAmount => 1; - private bool? allowTrackAdjustments = null; - - public virtual bool? AllowTrackAdjustments - { - set => allowTrackAdjustments = value; - get => allowTrackAdjustments; - } + public virtual bool? AllowTrackAdjustments { get; set; } public Bindable Beatmap { get; private set; } From 33e1273df8095a1afb0a64950d82d13e79a55475 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Wed, 15 Sep 2021 19:03:42 +1000 Subject: [PATCH 1938/2442] Include Flashlight in total SR calculation --- .../Difficulty/OsuDifficultyCalculator.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index da879cb02e..5087da6153 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -37,7 +37,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty double baseAimPerformance = Math.Pow(5 * Math.Max(1, aimRating / 0.0675) - 4, 3) / 100000; double baseSpeedPerformance = Math.Pow(5 * Math.Max(1, speedRating / 0.0675) - 4, 3) / 100000; - double basePerformance = Math.Pow(Math.Pow(baseAimPerformance, 1.1) + Math.Pow(baseSpeedPerformance, 1.1), 1 / 1.1); + double baseFlashlightPerformance = 0.0; + if (mods.Any(h => h is OsuModFlashlight)) + baseFlashlightPerformance = Math.Pow(flashlightRating, 2.0) * 25.0; + double basePerformance = + Math.Pow( + Math.Pow(baseAimPerformance, 1.1) + + Math.Pow(baseSpeedPerformance, 1.1) + + Math.Pow(baseFlashlightPerformance, 1.1), 1.0 / 1.1 + ); double starRating = basePerformance > 0.00001 ? Math.Cbrt(1.12) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0; HitWindows hitWindows = new OsuHitWindows(); @@ -95,6 +103,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty new OsuModHalfTime(), new OsuModEasy(), new OsuModHardRock(), + new OsuModFlashlight(), }; } -} +} \ No newline at end of file From a8539bc75b6e88589fa16619b729a13c73611fcf Mon Sep 17 00:00:00 2001 From: MBmasher Date: Wed, 15 Sep 2021 19:04:36 +1000 Subject: [PATCH 1939/2442] Add newline to end of file --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 5087da6153..411140c756 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -106,4 +106,4 @@ namespace osu.Game.Rulesets.Osu.Difficulty new OsuModFlashlight(), }; } -} \ No newline at end of file +} From 6d254fba0ac2001d559f5debb8ca1c953d60c23e Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Wed, 15 Sep 2021 10:27:18 +0100 Subject: [PATCH 1940/2442] digestify speed return --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 39acbf4027..19f9e3f849 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -84,7 +84,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } } - return (1 + (speedBonus - 1) * 0.75) * angleBonus * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / Math.Max(deltaTime, 1); + return (1 + (speedBonus - 1) * 0.75) + * angleBonus + * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) + / Math.Max(deltaTime, 1); } } } From 49658b6f82d097777705f6eaa670bd0fc4339be6 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Wed, 15 Sep 2021 10:29:30 +0100 Subject: [PATCH 1941/2442] set greatWindow to readonly --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 19f9e3f849..93d3227649 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -30,7 +30,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private const double min_speed_bonus = 75; // ~200BPM private const double speed_balancing_factor = 40; - private double greatWindow; + + private readonly double greatWindow; public Speed(Mod[] mods, float od, double clockRate) : base(mods) From 07fec268c028dc65b43f38a2cde6da8fe77cd212 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 Sep 2021 18:32:52 +0900 Subject: [PATCH 1942/2442] Limit maximum triangles to avoid GL buffer overflow --- osu.Game/Graphics/Backgrounds/Triangles.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 35c48a50d0..ab8763e576 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics.Primitives; using osu.Framework.Allocation; using System.Collections.Generic; using osu.Framework.Graphics.Batches; +using osu.Framework.Graphics.OpenGL.Buffers; using osu.Framework.Graphics.OpenGL.Vertices; using osu.Framework.Lists; @@ -181,7 +182,10 @@ namespace osu.Game.Graphics.Backgrounds private void addTriangles(bool randomY) { - AimCount = (int)(DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio); + // limited by the maximum size of QuadVertexBuffer for safety. + const int max_triangles = QuadVertexBuffer.MAX_QUADS; + + AimCount = (int)Math.Min(max_triangles, (DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio)); for (int i = 0; i < AimCount - parts.Count; i++) parts.Add(createTriangle(randomY)); From a0bd73c3562211320410bbd63d157af0143ba377 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Wed, 15 Sep 2021 10:52:50 +0100 Subject: [PATCH 1943/2442] refactor hit window calc --- .../Difficulty/OsuDifficultyCalculator.cs | 24 ++++++++++++------- .../Difficulty/Skills/Speed.cs | 6 ++--- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 74ede287b0..9107d8234f 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.CodeAnalysis.CSharp.Syntax; using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Difficulty.Preprocessing; @@ -21,6 +22,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty public class OsuDifficultyCalculator : DifficultyCalculator { private const double difficulty_multiplier = 0.0675; + private double hitWindowGreat; public OsuDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) : base(ruleset, beatmap) @@ -36,11 +38,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2; - HitWindows hitWindows = new OsuHitWindows(); - hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty); - - // Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future - double hitWindowGreat = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate; double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; int maxCombo = beatmap.HitObjects.Count; @@ -79,11 +76,20 @@ namespace osu.Game.Rulesets.Osu.Difficulty } } - protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[] + protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) { - new Aim(mods), - new Speed(mods, beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, clockRate), - }; + HitWindows hitWindows = new OsuHitWindows(); + hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty); + + // Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future + hitWindowGreat = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate; + + return new Skill[] + { + new Aim(mods), + new Speed(mods, hitWindowGreat), + }; + } protected override Mod[] DifficultyAdjustmentMods => new Mod[] { diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 93d3227649..8f0034ef79 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -33,12 +33,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private readonly double greatWindow; - public Speed(Mod[] mods, float od, double clockRate) + public Speed(Mod[] mods, double hitWindowGreat) : base(mods) { - HitWindows hitWindows = new OsuHitWindows(); - hitWindows.SetDifficulty(od); - greatWindow = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate; + greatWindow = hitWindowGreat; } protected override double StrainValueOf(DifficultyHitObject current) From 3a16ec277a8fdac95f890599d4d7db56491fae70 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Wed, 15 Sep 2021 11:12:36 +0100 Subject: [PATCH 1944/2442] refactor speed window ratios --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 8f0034ef79..89e5c39449 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -49,19 +49,19 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double distance = Math.Min(single_spacing_threshold, osuCurrent.TravelDistance + osuCurrent.JumpDistance); double deltaTime = current.DeltaTime; - // Aim to nerf cheesy rhythms (Very fast consecutive doubles with large deltatimes between) - double deltaTimeThreshold = greatWindow * 2; + double greatWindowFull = greatWindow * 2; + double speedWindowRatio = deltaTime / greatWindowFull; - if (Previous.Count > 0 && deltaTime < deltaTimeThreshold && Previous[0].DeltaTime > deltaTime) + // Aim to nerf cheesy rhythms (Very fast consecutive doubles with large deltatimes between) + if (Previous.Count > 0 && deltaTime < greatWindowFull && Previous[0].DeltaTime > deltaTime) { - double speedWindowRatio = deltaTime / deltaTimeThreshold; + deltaTime = Interpolation.Lerp(Previous[0].DeltaTime, deltaTime, speedWindowRatio); } // Cap deltatime to the OD 300 hitwindow. - // 0.93 is derived from making sure 260bpm OD8 streams aren't nerfed harshly - var hitWindowNerf = deltaTime / (greatWindow * 2 * 0.93); - deltaTime /= Math.Clamp(hitWindowNerf, 0.92, 1); + // 0.93 is derived from making sure 260bpm OD8 streams aren't nerfed harshly, + deltaTime /= Math.Clamp(speedWindowRatio * (1/0.93), 0.92, 1); double speedBonus = 1.0; if (deltaTime < min_speed_bonus) From f0439ef50b0dd9217e43508217b34c0e47dcba7b Mon Sep 17 00:00:00 2001 From: AbstractQbit <38468635+AbstractQbit@users.noreply.github.com> Date: Wed, 15 Sep 2021 13:12:57 +0300 Subject: [PATCH 1945/2442] Remove unnecessary `AllowTrackAdjustments` overrides, add true to `SongSelect` --- .../Settings/Sections/Maintenance/DirectorySelectScreen.cs | 2 -- osu.Game/Screens/Import/FileImportScreen.cs | 2 -- osu.Game/Screens/Menu/MainMenu.cs | 2 -- osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs | 2 -- osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs | 2 -- osu.Game/Screens/Select/SongSelect.cs | 2 ++ osu.Game/Screens/StartupScreen.cs | 2 -- 7 files changed, 2 insertions(+), 12 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs index 6d0e79e2c7..e509cac2f1 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs @@ -24,8 +24,6 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance private OsuDirectorySelector directorySelector; - public override bool? AllowTrackAdjustments => false; - /// /// Text to display in the header to inform the user of what they are selecting. /// diff --git a/osu.Game/Screens/Import/FileImportScreen.cs b/osu.Game/Screens/Import/FileImportScreen.cs index 69fcf31876..7e1d55b3e2 100644 --- a/osu.Game/Screens/Import/FileImportScreen.cs +++ b/osu.Game/Screens/Import/FileImportScreen.cs @@ -23,8 +23,6 @@ namespace osu.Game.Screens.Import { public override bool HideOverlaysOnEnter => true; - public override bool? AllowTrackAdjustments => false; - private OsuFileSelector fileSelector; private Container contentContainer; private TextFlowContainer currentFileText; diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 00885a91c5..221b31f855 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -36,8 +36,6 @@ namespace osu.Game.Screens.Menu public override bool AllowExternalScreenChange => true; - public override bool? AllowTrackAdjustments => false; - private Screen songSelect; private MenuSideFlashes sideFlashes; diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index e3945c9cac..fc20b21b60 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -24,8 +24,6 @@ namespace osu.Game.Screens.OnlinePlay [Cached] protected readonly OverlayColourProvider ColourProvider = new OverlayColourProvider(OverlayColourScheme.Plum); - public override bool? AllowTrackAdjustments => false; - public override bool CursorVisible => (screenStack?.CurrentScreen as IOnlinePlaySubScreen)?.CursorVisible ?? true; // this is required due to PlayerLoader eventually being pushed to the main stack diff --git a/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs b/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs index 9bec320e22..88dab88d42 100644 --- a/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs +++ b/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs @@ -10,8 +10,6 @@ namespace osu.Game.Screens.Play { protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value); - public override bool? AllowTrackAdjustments => true; - public void ApplyToBackground(Action action) => base.ApplyToBackground(b => action.Invoke((BackgroundScreenBeatmap)b)); } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index f11f9fd614..1f0f134ba7 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -53,6 +53,8 @@ namespace osu.Game.Screens.Select protected virtual bool DisplayStableImportPrompt => stableImportManager?.SupportsImportFromStable == true; + public override bool? AllowTrackAdjustments => true; + /// /// Can be null if is false. /// diff --git a/osu.Game/Screens/StartupScreen.cs b/osu.Game/Screens/StartupScreen.cs index 7b73d36fdf..be217d6b1f 100644 --- a/osu.Game/Screens/StartupScreen.cs +++ b/osu.Game/Screens/StartupScreen.cs @@ -16,8 +16,6 @@ namespace osu.Game.Screens public override bool CursorVisible => false; - public override bool? AllowTrackAdjustments => false; - protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled; } } From 4017598af0420a71ab769b4dde2b24ecd5adf512 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Wed, 15 Sep 2021 11:15:05 +0100 Subject: [PATCH 1946/2442] simplify algebra down --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 89e5c39449..aefebbe669 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills // Cap deltatime to the OD 300 hitwindow. // 0.93 is derived from making sure 260bpm OD8 streams aren't nerfed harshly, - deltaTime /= Math.Clamp(speedWindowRatio * (1/0.93), 0.92, 1); + deltaTime /= Math.Clamp(speedWindowRatio / 0.93, 0.92, 1); double speedBonus = 1.0; if (deltaTime < min_speed_bonus) From 7f6722e43fcd4aa19a5e3d65a189959714d64d5c Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Wed, 15 Sep 2021 11:24:48 +0100 Subject: [PATCH 1947/2442] throw math.max(N, 1) into straintime --- .../Preprocessing/OsuDifficultyHitObject.cs | 8 ++++++++ osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs | 6 +++--- .../Difficulty/Skills/Speed.cs | 17 ++++++++--------- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index 609ad4c995..65efe65129 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -16,6 +16,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing protected new OsuHitObject BaseObject => (OsuHitObject)base.BaseObject; + /// + /// Milliseconds elapsed since the start time of the previous , with a minimum of 1ms to account for simultaneous s. + /// + public double StrainTime { get; private set; } + /// /// Normalized distance from the end position of the previous to the start position of this . /// @@ -42,6 +47,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing this.lastObject = (OsuHitObject)lastObject; setDistances(); + + // Capped to 1ms to prevent difficulty calculation breaking from simulatenous objects. + StrainTime = Math.Max(DeltaTime, 1); } private void setDistances() diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index 7467feb009..16a18cbcb9 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills Math.Max(osuPrevious.JumpDistance - scale, 0) * Math.Pow(Math.Sin(osuCurrent.Angle.Value - angle_bonus_begin), 2) * Math.Max(osuCurrent.JumpDistance - scale, 0)); - result = 1.4 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, osuPrevious.DeltaTime); + result = 1.4 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, osuPrevious.StrainTime); } } @@ -54,8 +54,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double travelDistanceExp = applyDiminishingExp(osuCurrent.TravelDistance); return Math.Max( - result + (jumpDistanceExp + travelDistanceExp + Math.Sqrt(travelDistanceExp * jumpDistanceExp)) / Math.Max(osuCurrent.DeltaTime, timing_threshold), - (Math.Sqrt(travelDistanceExp * jumpDistanceExp) + jumpDistanceExp + travelDistanceExp) / Math.Max(osuCurrent.DeltaTime, 1) + result + (jumpDistanceExp + travelDistanceExp + Math.Sqrt(travelDistanceExp * jumpDistanceExp)) / Math.Max(osuCurrent.StrainTime, timing_threshold), + (Math.Sqrt(travelDistanceExp * jumpDistanceExp) + jumpDistanceExp + travelDistanceExp) / osuCurrent.StrainTime ); } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index aefebbe669..b2eacc5c6a 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -47,25 +47,24 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills var osuCurrent = (OsuDifficultyHitObject)current; double distance = Math.Min(single_spacing_threshold, osuCurrent.TravelDistance + osuCurrent.JumpDistance); - double deltaTime = current.DeltaTime; + double strainTime = osuCurrent.StrainTime; double greatWindowFull = greatWindow * 2; - double speedWindowRatio = deltaTime / greatWindowFull; + double speedWindowRatio = strainTime / greatWindowFull; // Aim to nerf cheesy rhythms (Very fast consecutive doubles with large deltatimes between) - if (Previous.Count > 0 && deltaTime < greatWindowFull && Previous[0].DeltaTime > deltaTime) + if (Previous.Count > 0 && strainTime < greatWindowFull && (Previous[0] as OsuDifficultyHitObject).StrainTime > strainTime) { - - deltaTime = Interpolation.Lerp(Previous[0].DeltaTime, deltaTime, speedWindowRatio); + strainTime = Interpolation.Lerp(Previous[0].DeltaTime, strainTime, speedWindowRatio); } // Cap deltatime to the OD 300 hitwindow. // 0.93 is derived from making sure 260bpm OD8 streams aren't nerfed harshly, - deltaTime /= Math.Clamp(speedWindowRatio / 0.93, 0.92, 1); + strainTime /= Math.Clamp(speedWindowRatio / 0.93, 0.92, 1); double speedBonus = 1.0; - if (deltaTime < min_speed_bonus) - speedBonus = 1 + Math.Pow((min_speed_bonus - deltaTime) / speed_balancing_factor, 2); + if (strainTime < min_speed_bonus) + speedBonus = 1 + Math.Pow((min_speed_bonus - strainTime) / speed_balancing_factor, 2); double angleBonus = 1.0; @@ -86,7 +85,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills return (1 + (speedBonus - 1) * 0.75) * angleBonus * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) - / Math.Max(deltaTime, 1); + / strainTime; } } } From 2fe0681310498718bea523b550109743c511a0d9 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Wed, 15 Sep 2021 12:03:47 +0100 Subject: [PATCH 1948/2442] elaborate comment --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index b2eacc5c6a..5b509b9edc 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } // Cap deltatime to the OD 300 hitwindow. - // 0.93 is derived from making sure 260bpm OD8 streams aren't nerfed harshly, + // 0.93 is derived from making sure 260bpm OD8 streams aren't nerfed harshly, whilst 0.92 limits the effect of the cap. strainTime /= Math.Clamp(speedWindowRatio / 0.93, 0.92, 1); double speedBonus = 1.0; From cf63a45f32dd10d7be11d959f8667a8b2ba544e4 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Wed, 15 Sep 2021 12:36:15 +0100 Subject: [PATCH 1949/2442] swap speedwindowratio in cap so values are correct --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 5b509b9edc..78c3db96a1 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills // Cap deltatime to the OD 300 hitwindow. // 0.93 is derived from making sure 260bpm OD8 streams aren't nerfed harshly, whilst 0.92 limits the effect of the cap. - strainTime /= Math.Clamp(speedWindowRatio / 0.93, 0.92, 1); + strainTime /= Math.Clamp((strainTime / greatWindowFull) / 0.93, 0.92, 1); double speedBonus = 1.0; if (strainTime < min_speed_bonus) From 463b92fcca42f0aa0e14307f21a7d60e46c7e080 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Wed, 15 Sep 2021 12:41:29 +0100 Subject: [PATCH 1950/2442] remove unused strings --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 1 - osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 2 -- 2 files changed, 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 9107d8234f..743494abac 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Microsoft.CodeAnalysis.CSharp.Syntax; using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Difficulty.Preprocessing; diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 78c3db96a1..a117570e61 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -7,8 +7,6 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Objects; using osu.Framework.Utils; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.Osu.Scoring; namespace osu.Game.Rulesets.Osu.Difficulty.Skills { From 2c3e7bfd2dceddb7d8400c40a3ec5bcad2c436c7 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Wed, 15 Sep 2021 15:27:36 +0100 Subject: [PATCH 1951/2442] moved 2b straintime cap up to 25ms --- .../Difficulty/Preprocessing/OsuDifficultyHitObject.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index 65efe65129..8e8f9bc06e 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing protected new OsuHitObject BaseObject => (OsuHitObject)base.BaseObject; /// - /// Milliseconds elapsed since the start time of the previous , with a minimum of 1ms to account for simultaneous s. + /// Milliseconds elapsed since the start time of the previous , with a minimum of 25ms to account for simultaneous s. /// public double StrainTime { get; private set; } @@ -48,8 +48,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing setDistances(); - // Capped to 1ms to prevent difficulty calculation breaking from simulatenous objects. - StrainTime = Math.Max(DeltaTime, 1); + // Capped to 25ms to prevent difficulty calculation breaking from simulatenous objects. + StrainTime = Math.Max(DeltaTime, 25); } private void setDistances() From 2637c063a951b675edf4abd3d815e9c9ae33ebf9 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Wed, 15 Sep 2021 15:40:26 +0100 Subject: [PATCH 1952/2442] forgot a deltatime --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index a117570e61..3cb0e3506b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills // Aim to nerf cheesy rhythms (Very fast consecutive doubles with large deltatimes between) if (Previous.Count > 0 && strainTime < greatWindowFull && (Previous[0] as OsuDifficultyHitObject).StrainTime > strainTime) { - strainTime = Interpolation.Lerp(Previous[0].DeltaTime, strainTime, speedWindowRatio); + strainTime = Interpolation.Lerp((Previous[0] as OsuDifficultyHitObject).StrainTime, strainTime, speedWindowRatio); } // Cap deltatime to the OD 300 hitwindow. From 318f0941ca8f53af7bff53cc8ce54226f8a0fbba Mon Sep 17 00:00:00 2001 From: AbstractQbit <38468635+AbstractQbit@users.noreply.github.com> Date: Wed, 15 Sep 2021 21:25:39 +0300 Subject: [PATCH 1953/2442] Move all the "inherit previous `AllowTrackAdjustments`" logic into `OsuScreen` --- osu.Game/OsuGame.cs | 5 +---- osu.Game/Screens/Edit/Editor.cs | 2 +- osu.Game/Screens/IOsuScreen.cs | 2 +- .../Multiplayer/Spectate/MultiSpectatorScreen.cs | 2 +- osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs | 2 +- osu.Game/Screens/OsuScreen.cs | 7 ++++++- osu.Game/Screens/Play/Player.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 2 +- 8 files changed, 13 insertions(+), 11 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index f2f925a778..2107b3a0e9 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1075,10 +1075,7 @@ namespace osu.Game OverlayActivationMode.BindTo(newOsuScreen.OverlayActivationMode); API.Activity.BindTo(newOsuScreen.Activity); - if (newOsuScreen.AllowTrackAdjustments.HasValue) - MusicController.AllowTrackAdjustments = newOsuScreen.AllowTrackAdjustments.Value; - else - newOsuScreen.AllowTrackAdjustments = MusicController.AllowTrackAdjustments; + MusicController.AllowTrackAdjustments = newOsuScreen.AllowTrackAdjustments; if (newOsuScreen.HideOverlaysOnEnter) CloseAllOverlays(); diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 028662172d..5bb47e1c11 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -56,7 +56,7 @@ namespace osu.Game.Screens.Edit public override bool DisallowExternalBeatmapRulesetChanges => true; - public override bool? AllowTrackAdjustments => false; + public override bool AllowTrackAdjustments => false; protected bool HasUnsavedChanges => lastSavedHash != changeHandler.CurrentStateHash; diff --git a/osu.Game/Screens/IOsuScreen.cs b/osu.Game/Screens/IOsuScreen.cs index fd884586d1..b12baf233f 100644 --- a/osu.Game/Screens/IOsuScreen.cs +++ b/osu.Game/Screens/IOsuScreen.cs @@ -62,7 +62,7 @@ namespace osu.Game.Screens /// Whether mod track adjustments are allowed to be applied. /// A value means that the parent screen's value of this setting will be used. /// - bool? AllowTrackAdjustments { get; set; } + bool AllowTrackAdjustments { get; } /// /// Invoked when the back button has been pressed to close any overlays before exiting this . diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs index c45e3a79da..bf7c738882 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs @@ -26,7 +26,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate public override bool DisallowExternalBeatmapRulesetChanges => true; // We are managing our own adjustments. For now, this happens inside the Player instances themselves. - public override bool? AllowTrackAdjustments => false; + public override bool AllowTrackAdjustments => false; /// /// Whether all spectating players have finished loading. diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs index 8c4f0c1394..054009a228 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs @@ -11,7 +11,7 @@ namespace osu.Game.Screens.OnlinePlay { public override bool DisallowExternalBeatmapRulesetChanges => false; - public override bool? AllowTrackAdjustments => true; + public override bool AllowTrackAdjustments => true; public virtual string ShortTitle => Title; diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 9b4d7f9eda..01dc703b66 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -81,7 +81,12 @@ namespace osu.Game.Screens public virtual float BackgroundParallaxAmount => 1; - public virtual bool? AllowTrackAdjustments { get; set; } + [Resolved] + private MusicController musicController { get; set; } + + private bool? allowTrackAdjustments; + + public virtual bool AllowTrackAdjustments => allowTrackAdjustments ??= (musicController?.AllowTrackAdjustments ?? false); public Bindable Beatmap { get; private set; } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 9927467bd6..e8a2790c94 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -56,7 +56,7 @@ namespace osu.Game.Screens.Play protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.UserTriggered; // We are managing our own adjustments (see OnEntering/OnExiting). - public override bool? AllowTrackAdjustments => false; + public override bool AllowTrackAdjustments => false; private readonly IBindable gameActive = new Bindable(true); diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 1f0f134ba7..9b6cbad7d1 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -53,7 +53,7 @@ namespace osu.Game.Screens.Select protected virtual bool DisplayStableImportPrompt => stableImportManager?.SupportsImportFromStable == true; - public override bool? AllowTrackAdjustments => true; + public override bool AllowTrackAdjustments => true; /// /// Can be null if is false. From 30c458c662e403bcadaf25a8ac975242989ba35a Mon Sep 17 00:00:00 2001 From: AbstractQbit <38468635+AbstractQbit@users.noreply.github.com> Date: Wed, 15 Sep 2021 21:34:41 +0300 Subject: [PATCH 1954/2442] Oops, fix not compiling test --- osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs index 79acaedb73..8f6ab5e755 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs @@ -121,12 +121,12 @@ namespace osu.Game.Tests.Visual.Menus private class AllowScreen : OsuScreen { - public override bool? AllowTrackAdjustments => true; + public override bool AllowTrackAdjustments => true; } private class DisallowScreen : OsuScreen { - public override bool? AllowTrackAdjustments => false; + public override bool AllowTrackAdjustments => false; } private class InheritScreen : OsuScreen From 32d65adb35c225dac77a43604012f0d426293508 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Wed, 15 Sep 2021 21:22:37 +0200 Subject: [PATCH 1955/2442] Fix cursor particle scale --- .../Skinning/Legacy/LegacyCursorParticles.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index ccccd1810c..73820b8df9 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -39,6 +39,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy var texture = skin.GetTexture("star2"); var starBreakAdditive = skin.GetConfig(OsuSkinColour.StarBreakAdditive)?.Value ?? new Color4(255, 182, 193, 255); + if (texture != null) + { + // stable "magic ratio". see OsuPlayfieldAdjustmentContainer for full explanation. + texture.ScaleAdjust *= 1.6f; + } + InternalChildren = new[] { breakSpewer = new LegacyCursorParticleSpewer(texture, 20) From 3cd3e133ce2a98205988c480cd44c4e61a5fae59 Mon Sep 17 00:00:00 2001 From: AbstractQbit <38468635+AbstractQbit@users.noreply.github.com> Date: Thu, 16 Sep 2021 01:21:29 +0300 Subject: [PATCH 1956/2442] Move `AllowTrackAdjustments` test to `TestSceneOsuScreenStack` --- .../Menus/TestSceneMusicActionHandling.cs | 53 +----------- .../Visual/TestSceneOsuScreenStack.cs | 83 ++++++++++++++++++- 2 files changed, 80 insertions(+), 56 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs index 8f6ab5e755..bf6491cd81 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Database; @@ -80,57 +81,5 @@ namespace osu.Game.Tests.Visual.Menus trackChangeQueue.Count == 1 && trackChangeQueue.Dequeue().changeDirection == TrackChangeDirection.Next); } - - [Test] - public void TestAllowTrackAdjustments() - { - AddStep("push allowing screen", () => Game.ScreenStack.Push(new AllowScreen())); - AddAssert("allows adjustments", () => Game.MusicController.AllowTrackAdjustments); - - AddStep("push inheriting screen", () => Game.ScreenStack.Push(new InheritScreen())); - AddAssert("allows adjustments", () => Game.MusicController.AllowTrackAdjustments); - - AddStep("push disallowing screen", () => Game.ScreenStack.Push(new DisallowScreen())); - AddAssert("disallows adjustments", () => !Game.MusicController.AllowTrackAdjustments); - - AddStep("push inheriting screen", () => Game.ScreenStack.Push(new InheritScreen())); - AddAssert("disallows adjustments", () => !Game.MusicController.AllowTrackAdjustments); - - AddStep("push inheriting screen", () => Game.ScreenStack.Push(new InheritScreen())); - AddAssert("disallows adjustments", () => !Game.MusicController.AllowTrackAdjustments); - - AddStep("push allowing screen", () => Game.ScreenStack.Push(new AllowScreen())); - AddAssert("allows adjustments", () => Game.MusicController.AllowTrackAdjustments); - - // Now start exiting from screens - AddStep("exit screen", () => Game.ScreenStack.Exit()); - AddAssert("disallows adjustments", () => !Game.MusicController.AllowTrackAdjustments); - - AddStep("exit screen", () => Game.ScreenStack.Exit()); - AddAssert("disallows adjustments", () => !Game.MusicController.AllowTrackAdjustments); - - AddStep("exit screen", () => Game.ScreenStack.Exit()); - AddAssert("disallows adjustments", () => !Game.MusicController.AllowTrackAdjustments); - - AddStep("exit screen", () => Game.ScreenStack.Exit()); - AddAssert("allows adjustments", () => Game.MusicController.AllowTrackAdjustments); - - AddStep("exit screen", () => Game.ScreenStack.Exit()); - AddAssert("allows adjustments", () => Game.MusicController.AllowTrackAdjustments); - } - - private class AllowScreen : OsuScreen - { - public override bool AllowTrackAdjustments => true; - } - - private class DisallowScreen : OsuScreen - { - public override bool AllowTrackAdjustments => false; - } - - private class InheritScreen : OsuScreen - { - } } } diff --git a/osu.Game.Tests/Visual/TestSceneOsuScreenStack.cs b/osu.Game.Tests/Visual/TestSceneOsuScreenStack.cs index c55988d1bb..ed3935e101 100644 --- a/osu.Game.Tests/Visual/TestSceneOsuScreenStack.cs +++ b/osu.Game.Tests/Visual/TestSceneOsuScreenStack.cs @@ -5,8 +5,8 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Screens; -using osu.Framework.Testing; using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; using osu.Game.Screens; using osu.Game.Screens.Play; using osuTK.Graphics; @@ -18,10 +18,20 @@ namespace osu.Game.Tests.Visual { private TestOsuScreenStack stack; - [SetUpSteps] - public void SetUpSteps() + [Cached] + private MusicController musicController = new MusicController(); + + [BackgroundDependencyLoader] + private void load() { - AddStep("Create new screen stack", () => { Child = stack = new TestOsuScreenStack { RelativeSizeAxes = Axes.Both }; }); + stack = new TestOsuScreenStack { RelativeSizeAxes = Axes.Both }; + stack.ScreenPushed += screenChanged; + stack.ScreenExited += screenChanged; + + Add(musicController); + Add(stack); + + LoadComponent(stack); } [Test] @@ -42,6 +52,44 @@ namespace osu.Game.Tests.Visual AddAssert("Parallax is off", () => stack.ParallaxAmount == 0); } + [Test] + public void AllowTrackAdjustmentsTest() + { + AddStep("push allowing screen", () => stack.Push(loadNewScreen())); + AddAssert("allows adjustments 1", () => musicController.AllowTrackAdjustments); + + AddStep("push inheriting screen", () => stack.Push(loadNewScreen())); + AddAssert("allows adjustments 2", () => musicController.AllowTrackAdjustments); + + AddStep("push disallowing screen", () => stack.Push(loadNewScreen())); + AddAssert("disallows adjustments 3", () => !musicController.AllowTrackAdjustments); + + AddStep("push inheriting screen", () => stack.Push(loadNewScreen())); + AddAssert("disallows adjustments 4", () => !musicController.AllowTrackAdjustments); + + AddStep("push inheriting screen", () => stack.Push(loadNewScreen())); + AddAssert("disallows adjustments 5", () => !musicController.AllowTrackAdjustments); + + AddStep("push allowing screen", () => stack.Push(loadNewScreen())); + AddAssert("allows adjustments 6", () => musicController.AllowTrackAdjustments); + + // Now start exiting from screens + AddStep("exit screen", () => stack.Exit()); + AddAssert("disallows adjustments 7", () => !musicController.AllowTrackAdjustments); + + AddStep("exit screen", () => stack.Exit()); + AddAssert("disallows adjustments 8", () => !musicController.AllowTrackAdjustments); + + AddStep("exit screen", () => stack.Exit()); + AddAssert("disallows adjustments 9", () => !musicController.AllowTrackAdjustments); + + AddStep("exit screen", () => stack.Exit()); + AddAssert("allows adjustments 10", () => musicController.AllowTrackAdjustments); + + AddStep("exit screen", () => stack.Exit()); + AddAssert("allows adjustments 11", () => musicController.AllowTrackAdjustments); + } + public class TestScreen : ScreenWithBeatmapBackground { private readonly string screenText; @@ -78,5 +126,32 @@ namespace osu.Game.Tests.Visual { public new float ParallaxAmount => base.ParallaxAmount; } + + private class AllowScreen : OsuScreen + { + public override bool AllowTrackAdjustments => true; + } + + public class DisallowScreen : OsuScreen + { + public override bool AllowTrackAdjustments => false; + } + + private class InheritScreen : OsuScreen + { + } + + private OsuScreen loadNewScreen() where T : OsuScreen, new() + { + OsuScreen screen = new T(); + LoadComponent(screen); + return screen; + } + + private void screenChanged(IScreen current, IScreen newScreen) + { + if (newScreen is IOsuScreen newOsuScreen) + musicController.AllowTrackAdjustments = newOsuScreen.AllowTrackAdjustments; + } } } From 9057be1a02069917e065a94290685933dd9cf73e Mon Sep 17 00:00:00 2001 From: AbstractQbit <38468635+AbstractQbit@users.noreply.github.com> Date: Thu, 16 Sep 2021 01:30:53 +0300 Subject: [PATCH 1957/2442] Remove unused usings --- osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs index bf6491cd81..9037338e23 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs @@ -4,13 +4,11 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Input.Bindings; using osu.Game.Overlays; -using osu.Game.Screens; using osu.Game.Tests.Resources; namespace osu.Game.Tests.Visual.Menus From 7976442aec419492c30fa0b121c3ce9e00c33a9a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 16 Sep 2021 14:20:42 +0900 Subject: [PATCH 1958/2442] Fix CI issues --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 3cb0e3506b..9364b11048 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -43,6 +43,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills return 0; var osuCurrent = (OsuDifficultyHitObject)current; + var osuPrevious = Previous.Count > 0 ? (OsuDifficultyHitObject)Previous[0] : null; double distance = Math.Min(single_spacing_threshold, osuCurrent.TravelDistance + osuCurrent.JumpDistance); double strainTime = osuCurrent.StrainTime; @@ -51,10 +52,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double speedWindowRatio = strainTime / greatWindowFull; // Aim to nerf cheesy rhythms (Very fast consecutive doubles with large deltatimes between) - if (Previous.Count > 0 && strainTime < greatWindowFull && (Previous[0] as OsuDifficultyHitObject).StrainTime > strainTime) - { - strainTime = Interpolation.Lerp((Previous[0] as OsuDifficultyHitObject).StrainTime, strainTime, speedWindowRatio); - } + if (osuPrevious != null && strainTime < greatWindowFull && osuPrevious.StrainTime > strainTime) + strainTime = Interpolation.Lerp(osuPrevious.StrainTime, strainTime, speedWindowRatio); // Cap deltatime to the OD 300 hitwindow. // 0.93 is derived from making sure 260bpm OD8 streams aren't nerfed harshly, whilst 0.92 limits the effect of the cap. @@ -81,9 +80,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } return (1 + (speedBonus - 1) * 0.75) - * angleBonus - * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) - / strainTime; + * angleBonus + * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) + / strainTime; } } } From 45b07aa362b7048ee9ceb3f31e34ad68b4399943 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Sep 2021 15:33:55 +0900 Subject: [PATCH 1959/2442] Add some basic animated textures to mania metric skin --- .../Resources/metrics-skin/mania-note1-0@2x.png | Bin 0 -> 1874 bytes .../Resources/metrics-skin/mania-note1-1@2x.png | Bin 0 -> 2828 bytes .../Resources/metrics-skin/mania-note1H-0@2x.png | Bin 0 -> 5154 bytes .../Resources/metrics-skin/mania-note1H-1@2x.png | Bin 0 -> 6025 bytes .../Resources/metrics-skin/mania-note2-0@2x.png | Bin 0 -> 1865 bytes .../Resources/metrics-skin/mania-note2-1@2x.png | Bin 0 -> 2847 bytes .../Resources/metrics-skin/mania-note2H@2x.png | Bin 0 -> 5294 bytes .../Resources/metrics-skin/mania-noteS@2x.png | Bin 0 -> 3963 bytes .../Resources/metrics-skin/mania-noteSH@2x.png | Bin 0 -> 3963 bytes 9 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-note1-0@2x.png create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-note1-1@2x.png create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-note1H-0@2x.png create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-note1H-1@2x.png create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-note2-0@2x.png create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-note2-1@2x.png create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-note2H@2x.png create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-noteS@2x.png create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-noteSH@2x.png diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-note1-0@2x.png b/osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-note1-0@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..2db5d76e7828a025037cbe434f15eb5737ab749f GIT binary patch literal 1874 zcmbVNeM}Q)7=K|Ub}TA!n*`?ca%75Q+_j}p?w~+zVKiH@gJw1oKd#qDXX3h3;tje?9c zQB37iguV2wQ&c(tPt9~%tb*0_I!>}QLP#};mv%{9AU)ITBFGX-fNWH;lhMMxEr(&q zY1hK@l1&PeOHVnRxn&$xP?l#Q%SuSh4rgXS>0VqUpeccXy!29r$GuuO$cu}VrGbt}$AMAAKkB#ZWKdLY0U@x>KmoWcu$?njWj>g?#Ga zy#GqfTS{FNnosepn13qL z>>$Ks!i@|s5DZBf4O&&A5D{So`?1q>hV4#bHos zw~y>PloACbxnt55gE8?_jHq}{ly;^2Y@hhWd~Y=9EZ)G)%X1x@BES#*SJI+~E8)a9 zu1D=_Jy!IQ`h}`BYgCh~qvJ-*+kamf^KD~a)6qOWg(CIn^J%g|t1R;Bkq?mR$NK8z zWm8s8v8Bq-|Fkw4M=mbZt?GU^qxM4EwqpO){r%-vGRf-t&A>dzJrhI$AOj!_WPvAg zpAc@%`tIrY-7oemi>v7^bu3Q_ObGME?n78lS9{<~O++}L8r5x?ZLLFh4|K2@a_~og z-uz4Fzj@ieC0yof@*)jF{b7s^2A}O@=6-l9_eN7XGcgLBw&^qe%w_ezei|%1zuJA_#|#`j%ZyzmY@qf`!86;i>d$AW%2l;<{TVWuZ+l_Y39&;oKWxRB z&WhuUC)I|TE%z}Y?b*SteeW>t?!|>E%|q`2bHVUQc2V~o?PODTLqSZ(CU+)v(hPy0 z>v}KnFMZNg-p?(&SmhaN8e(ReWZ_w7ufNuP`)1Pc$mMg#Hk3D_4GaAKO<`emCl<8b z=~;KL>af4Fg?8RkE(*x@bT;_n4<4C@*KBCRN+SCreOz;m(X;%Y%Y0%@grpR*|En(F z>befubAdevZ!gSfzS28?T5+00KZR{h&nGEL7LW!~t)F6g-lDOgG5&n3&)Qtt0mnT0v*p($g)g-rNDiW6aJo J?47f?;%`E8Pn`e& literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-note1-1@2x.png b/osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-note1-1@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..6e7aded39f315120b9eb7c9fbac3c543faf90c22 GIT binary patch literal 2828 zcmbVOeK?bQAOCGR<@6%;R7%MXPoda5v*k4-)Nn}%#ca1(Z1>v6#u3huUa3o^S`KsS zlvJpslt|}1qG+>99^yEak-VKyt~Knr*V{RN)VZ#w{qy_%zSrmT`F_6N-}k=n?G5l- zH)p2lOaQ@J^-dN-8+>8Ap$%|%*04}@q7_Y>TK~&mxk>1VVni# z9Yh@GZ1LVyC?f#l0Sh4v1&g<1;qiD3g^Fb}qnWJeXj=>sPbA>*WE_EPhsV=MR2rF# z`S7zq(h1oyv_QJohh)gf*&R2-g&BNFWpgq@tg#(-E@42xo6 z0ftD#k}=i{CX370M{M3v7z~;ZUnFMoS&$Fi*#Z&4a=C1pyC-3-mjl5A?@3=vAb5H> zIugn54(>?U(Tn14@9~jKhgk_ch%f%gW&g#cf03&n2c7`QOoxQrM2PJrgn5{ERnxei z@#07zk-Z2MuP^F-;IcpC)5YUWww1XR)XujZ1)_M# z)69Xi*gIaWx}@q~>mGEa?N&c|*rnACC!Jfyou2OSh-rX4?CAht05H@)_y7O)YdE%B zi{=IVzGl2-%k^`lJep{`y-CmmXI3({6u~)@{A~I)j@@N@54!>qV`5%=BpXP7Q=JxU>uTp324eo6pG54ULe z{hetml}73ts)%bZI@6f2ys3BhNKfBHiTa@Luqn>S4MY`ZsYQo1TU0Nlsi{t7^81z? zL1Xr^Ws0MH6BTuY^Oq}5P&8N!xM*;K>U7BNl*TVmck8qU_gKgi)u1`4&#iWuV9g3} zw8BH%B3+(WrGwf)ZWdJks^gLOU|Qrkb%6#~kX!pm7_xs_aL+dXZ#_Yj4WoDJ=lJ}) zq|r2&9P=!B^}j}oJJuY|@I2o#R@Sht$sCn_I!L)u$r<$?a+Dp+?+jnA{LL=7s=V$` zn;HJoGEE8=@owOCwlwDx_vE`oxi(K{0^hSa9_1Q|>Sgp+`{~F?v_u%0*lj9Tr5FS2 zlH>ym?KUlP@vf-fgB*)C!b@A9)%WCj?F4R{?ZZnMi;dAh9mLgaD~l;0Sr~wOZ!<2H z#umSt|2q$D02Gpz3%IazoCdwtb}&=kPd#?3Cj+=qH$CXe-M=rG<6n)MX9$i2gj}tV zHY#Nks&{D$Zk~!QmbJ+S9Q2Z=>A#L^e^SWvmK%71!Xv@m9$T(g#wHN8S%#=|;Y+(I z@ef{itV9RreFp;8wnSVf!nm;fhUfQZ=gmih6_1WRS=~n)3?#k2l+vPC|51Vr zxQUKmBrAS8aJJVb1F0`4n5}iIiI}(|e=Fd>uEn7N^e{JW#^|8$jr~Sv)%-FI0*a@N zUcK$qoYSIrQM0#$sdwRPA2Pe=_O|K!j2va=+3JncGlARPp=ZbD#z;6=Yg&};_kQdu zcckr?wc{)L%df3AS!jkYh|KDl_8!?$w!BFhHau04sZ5VQz7V;MCzIykISkJk8+uwR z;>(eyH78c-ysu(!4wx8#cFwrG;dxP_Zgcs``w6BfUWHa$?OKU0G?VBq4P4b`faYCQ zI)AUx)GAj)u%gO5vc^RlHZWy1Qmu-B12EtSJl^#rH)P=jN?cnKQBJlt2EM0HP_9Z# zVAAWH4r)R8oS9o2%F{!Ks?wfnC&(nyq3|=pGWQ>l3Odso-wv#5pIjXfAXE z)@^2z{**@$$yhj2|ECU$eEs!2972-Ks_MLHolV z*;zmJr`?G+(kwtBueQdNW7SvchUA(4sa<2l36d$b@B7=rUT&daWsmT`#>OC42__of zuTCsOLvV!yj25HOfLB!TG-g*53YG5QG?}I_wnmjr1A=e9;&b!2uN3-g>9yR+kaQF% YQlD)6McVfS`Fjd{JpJgUYomVrA1bYTegFUf literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-note1H-0@2x.png b/osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-note1H-0@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f11fb4f853f011a3e76be6851249fab8cfb31ace GIT binary patch literal 5154 zcmb_fcT`hZx4%J90Yygz6bFe|0FmB92`wOCC=!Y|fFT43m_iaD(os=FKvC&U0YitO zg&K+|=paZ72$5z(K&1EfF3vdbTW`(x{`lUzYh|5t&)xgCe|zuW$-U>UxvAlP9w8n8 z0QMVQ(7yx#9N-kl+`BUn_Cox@;TRQROSL4rP<_y3ETH2|bizW7@Mu@;B`n(cy60=G z1_10ji$mE`ZIKsY7$RO8y<;Oy!;^Nf01Y^egvNMasZb}ZD~_NkI$cvQ3dK2VirOe4 zWsxL3tQ+ovFBxm;Yl_17dSKL?Md4ad4H^stz+Oyj41Sn z3DrYW^iNW@NOPzjk&K0^NXtrLWMyTcDr(ZsXeTtr$>}^)URGXCMpjWqPEkr$7N(#E zQ&fchb%}!E$j&aXOZoYq8_Nq@rQpz`w4icoPR8iOP3tVsNDK_X#B1PT>Rz+jE^HAO)XX&lZOCaZ|h zQ_(jtkVWY0$;lz~R8{2_b(D2<5OS&pDmqGfKYjIy7%x1QK>g|K{L>c!HtgT|?#Ka8 z0we2V$vAJUvjLfihyKVKhWiIDU{GBHWqD-~Pxqg>{ORlb@3`n2ApR{E8IX+3PH+FE z*FTp)0qsoxmM(bnxA?IHQ1N6?+DG6gR{-GPLnD116m8(!ppQSl)mHPY7$?WcgNGl8 zo{8Lh;TK+UsMUk?uwdS>aOhW2o-kf<WL;#_4>Y^a^_gyFE_!6U z3Jwllkh8eF8EctUPA;dv?Hzu!F(5(YGyhmqI7D3u8_-Ni^FFCQK3+|-5R?}GoNt=i z75#Cb_=*R^ZS3u@Zsf)p*Ph&dR?_!x+SlJ72Yz|!)iT8>z?!TEM&4#wk-n(1=^+kZ zGuC`^wUvn<5C&g|1bX{CWZXBLBUkd|yEZ!|Vm{*Y%7{T)?9h@{3Ks(tr%de5El*sC zx%8Iu-qU&>=IaqpDAYMq`+*3oBFo(Z)Ey{i^K_WkL^ikUvw4J+ZP|ifJN+bbOAC2z zrmnCy(UE~BExjJEQOu3Z=eiOR(+GjS7x2j>+^?J5fXW0L(OQpcb>ZQeYunTO(#`3{ zp<%0NQBqV>_>D>FL0vb{xQ z^7TX0sd8^du7L$+dv@VtMoQTS$u^o}F*c_~DaXo)SL}+Q?dR;%kdfyO=bmKBO?0wc z#kT6L3UVK2j5qEnlfHb@&phJSE%y!VgIKW~BfW4Ppt2{QbFfd|Z}?&K z)7H4sWhs71w`LkEB4enEq$Equekhhr<$U?-_8uXeN6GF53tNluXodIZTj#Zo6*3B# zGTt9DV+%VbyzH13#~G9Q;frbLr`2kvUhddaZ+0m*Z#WdyF3`5!h zW$=ZS#3N`;GBwNnj?3kD{Mwj0PnMF`d8~E-3e1IMsgJ)VIK=iBn}|eq z`=vQNlUS^^CKT5xzFc!<-xgYkSX_Ca^O=SWFe|7$g`VygTzox7=Snx3Y<9DXVnur9 zdOpgvVeP+Mqhc>%P07j$Sl*tLe|K~ye)n?U;+Y&y$cVd=)54l0eSIrVS`wYVyB5b> zNaCl&+Bq`S-Cwkx`vq5H+v?&w&drpIJ@M*kKudnnDtu!NzT9gW$LG#A9vyY4tGI}3 z+erF8p4sLn@o78P#-4bXX>(xRGR^p;VY(6>T zEECbVH8pCzhO6^kMnyj#5_I_8)~CEP4Nkwtsl7E7%OM1N#aR4fzc+w&tb_n0vAGflfHjEk1K z603T5bvQT%RgrW=?%Tw;2*Hz5?g6j1ef#07j~=E6>iIL$Pm0*xeB57E6D`z}sggXm za&OJM4i|E%&!cCgp}?%o{j%?L!0NJlmjm<86*+(dIy@Oz@5TGBz2|K~jf$4y#4a{{ zmo>K&7Y&8g5qgW{G(f8T>0w{C3$dQsUmar2tkOzu zHq{3zFZ3UBciT?y^)aE&R8HF@u7u((oo}Unzfhs-wx-c`#m(*sezHM0&+C(F0ehx? zyUK-W=Uqm4GFu0Oj3_fIgtabdks3DGRS7EXQCaU6`!JCK$VSph7%er8%YS+M}E;NNVG7 zL3huwUTV@^1*_631LW19lfZ9(P?DLO0_@&$)6~WDSFmhUetdP=6H2nu%N0BA6gJ1& z^^UEg$t}Z$4hoF?P%{)D$-MRKrIeMIQlH(-OI?l3DMw&nEtk2|F3-PWb8}ntF+hHn zu8UKk4mg9^O3G2)3AH_qRz-sZ-%o`uN}%nrafjwRZm9dBApoh|eM#X(Dhw~2AzU7i zg}3xS18ea#v5vPRx|Ap%1irh_wp%r5S!UmUcSID+D&--!RV8OVx_KeRosdme@XaV~ zqhNZ*9JIR5b&ThDz^Lxx~W^JJf`Z=<4*9xA+m zMhLiWAYAe96PheJgMn8MeSJQ4?pH`2J{Hwl7vu7q#pm!hwWIlmC9L1E_wPA=>6N?F zkmL$qMetn2^U$gLt+>E}+q{Cv0<3YDNtB71N_q2RI+)1&Y`1Luqfssk5l6_;N6nPo z;lH(y9765o)DAY9?~{5GanaJ_BuB#m`v*GvwE@ofl4b?s{PbzF z$I_v3067Vr&le2Le19b;HS|DG<-9_-(A;t^&uU(h?!6hwXtoW2Ko;DIvOKwR5X@v> z(|k~%-$t{4j^~^r^Hb&XX6bi65j z;EzZ7YKFIDZO^}9NN5fU$p}S-E0|W3&W7&zN;DPuj6UZ%(E1rrjWZThlO8w<)F=Pw z^2Fm=KEDE5(6yUrZCDcuG86Mo+;Y-l3;Zf;DBu%T9{QCO2x|~UplWG+DhISIsvhQe z4MTni;7#e)8rpb>{el08Guu5ipGWH#L5%xg`JJ7RIUl#Xz72AdQNxYcrywgfsNJ?Xtj_E%9?uUhs(?Q|8zj&XNqd*s;P@p`qT)*3I~ zq5{?G1#_~e#C9OstFxkWye7@S?gi)JE9!ZVSm``Fm2B@9a~z+v^le_=?w3OQ6gIM8 zQu)%!gFqqUbzt)#2Uich_+}m-uhlihth6e+H$SWLb(waTe>f;(mcV(Xa^;-qk=&ZH zOFHW($6^aJDanf{o9@r+ll;?|x=t z9DlJw_3O&7tGyMAAFT8n@W3JL5HX9a1NmG>c8grzo&WZ^w9%f)IqB(&{NI zajyc8gsrGtc6~tgsEE+!G5b-!)R`@Glu*xn$vZ<#xtk`719I@e?tOisyo>@1{>7=& zR@EoR(%^(!1Lehdq*)(tQ08I(^Oa8w_V>Kkw^~}#YDs@4G;@|~7vx~JHJ)`#g3hHO zHa&lcHP?PEh}?f@&AZn3sG~%@vgPZ%an3aE1KkgX25R(ottU47Z^emJ3!RzVR;2XZsKXmTOK-s(YL0|W)w>Xo~fv&ScvTCC+F{ zO50o4H`+}!KOOU#p6@pt?CXk_O0LmPSvz+ohPhSW6V^8}=D(59zp2duRJqudh&r-r zh~lSTtUqsT438?QBR5bMrnl42`j5BE6W%mg+R^E_o^>Iu#1vspAP}?QYOI!Z%i=Qg zesU?sd^=9jJ64^SHk+-xFu}Kq9%PxlA5+@JmP693&+a?P&G@cbEE4Avh`H@ zPHbvMop?)0qJ9pyF0>vK>sk0EY6}XCw0+p0%d&fvC3&jJH7y|_=dJ4`!_R)JW301Q zF?DcUAu%SCK832!pufe`8%Tc;Ft2;)>tMeJr<;Cze~-Oa>GG}D!~%!Bz9F8ls@(^K zUw_^|!#OpyIZ%dhJnNzRP`;}_P6vP*3gma*xRYti?|9r%)hJObwU$x-_D!ihhrC<{w`MhpK+yK~IRSR18T`zVFgSB6zxBqAM%Z<` z`sA*VQ?*yX*DC;Q95;hN{$EbqJ81uM>i$0;;??HOAiIIF&?C4D1oL(f956zd>KE!> Gz42f2c@AFy literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-note1H-1@2x.png b/osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-note1H-1@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..4eac5f6f2a62e9ad22096d02b1ba310e69c00a82 GIT binary patch literal 6025 zcmb_g2UJr_w>}h61Vt%|NN9=*ND&A~=uMiT_bNi9gd|83dH_X5MZf|mz4sfR?J-uU8<-&X02rX{iMkFGF+#X|!7O3!o*_3oU}^xs za?Z!v9%B!^3h_W8WZZx2$OI$&e{%t9n!*0=9&i{&@$pm_vjaor zp#HirZy&?Z0GL&%skKKa+(X4vOjAQdEf~TeK)^8WBEbkhBpMQ|F7^j6gwg*!EGs7R zhYAL+F7^+n?4jl&x~Kq{h_Z~Fw1=FWoQSfDjHml`caQ7WFN(;^$zPI{QDvJF1h%w9sc;0|m>RtKM7~@M_%o~I8hseqX1qI0jDafD#yksw_sHn)w$;-;i zOEWa2(IH5Td$2STedaF)Js8>}z{elsgF=e@W^})f!eZ3L7_R;!2891VX_4qZ=ZP_6 zvcc~DvX^A!e#i8eA{6@Ht0EBp)J9`0VgK^?za&Omhxo%}En#RBHo$|iayQQWHsue| z4S>00PyyB`l;2;|Xzq=|pwQkZe-U|k8AXu`P~*ryrK+`^_jF;F0x-4L>Ph<0HPS;7^5*x04;|2pa1;dq5hLS?*@M3UcR&O<|K(F zT>R6T#KfF8LJy71O=<*u)2r$a+kh-aWs=G*MpdmjT=Y6w3nKNK0tRbZLemQufPuX*-r4tW+s?`3^y-8)it(vEI)!e3ETCd zns%YjWUbeN5^*V(2Wz*SOa-mJ#dJox7)0>&nV#Ae?i3(}S}mAix<3=CC*&D>qAjD) zamc2Wa22dy`pNF&m7&!e**7Wa`cHRzhFeQI1@qP%(A0K714Zyb^`*$1+_4aDSy7wlD*xur$U;Pm?S4r7rWFZgVU4=WR<6+IJqu0!mDp^P`#|^t z`8@IJ2}Jh{CN9L{3$nM4v^2Fqr^C_ud`niD&WBNE15AIC4s9(3qGQhU@ z@HytKGj`b&dqz~rVl+g6j>hGgd$`&5^BZ$tneR@;yPQEK_@~ezCEq}KK*?1DqciGMp-xn2&{dStE+u^HUg!0m(LzNxVVd!tFdT8idUb-%y*nn0NbL9W9&{nj z>7HmpWBbY+Rql?RmTu9#+cTnczsq*|nmD=i%QYCMUE9lbmfq+6#KEN4E#lDICb)#8 zEf49a!fs6e_Q2h-ANM8g?v0jXZf)Z0(%494)j=0Ty9u;6!pptk86!f)q3FY(+PUvZ zQJBV6dJ97Z;IUGWA}^IPj|)AbYqUjhf&nXI#Lcy73Gix8lwgp_M=}Ch57OkTMkZn zjga%#$(CUk2f}??K?JG54bo6EopCqfld9c6?g6(}j%!S-T=#wC6qG?|Mzg@z{$x$W4&ffbmx1F+jbNb))IjR_(Mg{DgkMva!3Jn!vin1b8)6%V4 z&#Ag%iC^ESaHhn(K=ug)s)D9-z$DHZ{-u{yQX1!54UF+)xRyOl!jv*`K>q43>w>Ex zb8FpJ!?IRsAGEN@Of} zfQ<>*{`doH8>=|Fohfj5lC)S^*H(DPdaES~`Q5x@d8}a&BKqR3W5rsToN#bR?GHhw zsC70XK{_mro_bz&a=UhQnwp-1iG8g!Nh!bK@p(!SM(5EFaKKiFm_K4So#qBKo?%ON zq@8l7?bJtoJRTfH$PsSDv#&TlI35s=uh{o|d7cuoYsII#QXnnZbHrneA7tFH@d9xgia!fFh zDMB}5*NrmdWP5o*MG?y8XP=*UI&mb19nhOtoIzCRo2X4-x6TcPBmKIkM44QB4AL+o z^%{EfA9*{V$>*1c><$)jvPNZ5g?#-WH$R`sQ{US0YAd7)LfX-Hh3ylL_7vzKjTaD^ zoJq%k%i>Jen(1YJugPJW$tb^v%Q=bc)P&bZiTI2crl3#l0P>advV)BtAhKcg6rJe3 z&rx-e`s+ZVlS1M40nC%vOM%O8cE&ywBaO+=_@C<)Ns4!kIPS7DMcp?6!QmAn%R3y! zFPkZ0M{{Evd!}%NVd@j>Pas@nE$)zWVpcb)xzKK>vqPO2z^+YV)5wd$`GU-K<)6$zWoY0U!Z7_1xtsu*`jd zy{^=;oqsx~cVWOD?tD}ULUbs2RhJCGPJ9aELj*VLqn{CI^4?ohypJPaU2cw^d6mZU z80-H)GtPCx8J$-FMgn|GZ)&9yRVUSGZc7)tjDe}a`%uN#cs)s=+-N{L-gRSNHXj*>WddBT<tT;ZSpyaM6d{ zZ!|kyemz<>IxYNFxHdNO5Rm0_xu4zD6U_peqq*CPf&E86oljC38`&Klhpg*egYz_Y zY1vH{5A4>`E^x-93yC%$Rk z$0&OZTI|T2@(Hj1K&nS4$gNV>d!Tm-m#uxTNbwJps=`}W#qy|GqJdl z4{B`?np$;&>xS(+l{Su{j37fkKHv6FO8d|PE+qmb%uO{yFXa$GDlS`@@?kpZP?>lL zB>RO25HhLNz_!1#7T)iCaZ+*Wx(D2+*_N1V$k>AR`;8vce4r=rb!jH^cIrTwdU-;4 z$xixVe8(ug@OUIj#-k+Jwf)sS)+iEl>u&s2f#VOpTyuE5#wrX1%Z6lE;Ok!;zWW>( z)0Wb+syOVsvu)G#0CF7Au!1$Up6S>#*a%#%T?-A-2JSxka1x=%hwiaht;;CQGk{02IZs}tg6Hf-C#gk{`v+3h-K*!WS`o`J+KiH~ngTpOp<&V>y=0M@JP z)d`y`O>DvY%$D5;Pj$yZ>Z2I279+y&(+ulkEIZkINo`9`Q^NgActuMlBj9Gc-IQvH zf|l#M!42C~(fLJsvs@Y9w^>*`9)AK=s8bQQuM1v9Y<0>bAJZ#`{W~<7dL;zGSvMXm z6>1DLg`YVqF%*7e+M+N^$V4Dd<2~ZEpuORfeaBxZslRMr^wFbx-Jl8uVgMvsw+m6> z?9~(TZd&>vtMqZx1?R#q$`pLK2(j zCJMbdqc|96bIXIa!uczF%{t`Z3zCj11AeD@%d-tvg=nO-Pn%%&DB}G36%Q-Ncy5D` z6Tr+P=*My9X3t78<6h;ttf0N=mTS1mPusq@bDc`w(Pz~vs)?))$>g6)zp$>;c9E=m zW5&~0Vsd(w8u>w*aKt#Ucm=dHINYa6cd2;MxnKrh`hj4b^PgjxRn8w)TX;2b`Vkbm zHAXu-Ih}(Rw(C*wJG~N|B8JCO>7GJayNuhsgZKQappC{NslamM@_18oH*|NZXe{I;Rt%_roEO>4 zw5hG-mwS$8TTL?!ls(y8sqy|%g#lq<$_jILPv@IDF8Y=1;hNDlx034z@>(iicnQ+d zmhit!SDUl8d6|0W!hh=Ko7MI)aS^Uw-HHSyW#3hG%;(jRBb>Da3+FwU4*%Mu*7n>pz- z*iAqXS_(JBF}LBlVZQ5v-ID&9w%!h#J3Z}@4YeC^?JE0DJ!lq6kOuGCB5s9iVK=T{ zxD|EExA9vs2%AU;!SP2UU?HoU*eXocb)6v7d;duQ&FHz znanRsz92fJhJQ5h2?bvOkJVTo;IL7Ay@gG)D)OEp`AnT12er7Bs#b0z;}@oTj84^4 zPi9i)qE#vnw6;nmY2+r+6CPUnad}M+ulW#Vhc+L{o_lGr`m;k^wRTpL+;L1hIs8ds zi%%HA4pBgi)Cz4Ke}2<1=vbzh7Vy()>xy)Pja39_zERtmD+7n$9Qsu*$zg*VRM8#l zU%6VT`>jvPR)*pZsJebGgg zC&8z-+EtjNxDp!+x2?e6`s9K*aV6s?dy?>)j>Ag3bS(qdl~h>y$P(#`ty=|_Yq2JB zu6ylF(ycd4nOwJ2qod7y`npRu1Caaz(aHNB&zsh2s!h)m_NaKi@?PN$ei}{LW90N5 z($h0hrnw7z(iaBrkl#dMyoxyw^5QxY?m$qlP7mqq<-F)u{P7a*wx7m{bOcTqdaEQpyi{?L-0b3qmJ!Re( z@kV<`*7q~6bKiHbQf|lT?lS(H*R9~;sqgQLLYqk|<;xDQpa&ucznu)ROd<^>mR8>Z zqBP0py6}E=XpWK58t{~*cz!Kqe7P)Rlc<_yvv@n(x?J)$Q?Gs5JszSi$hQmo>#-z< zHNom5$9P~K+lxzj*4ABU7fR4W+X*cX_yXl+-SC`xTUwO~{POfFcq_+^KsmCcti7n4 z9vk^o-cj>naaF*L>?}%eP7;J_sVBQh-@KZafL2~9;ZE(wFK-y{ITdfIL_AyCz7$ZO z?G$u!xDGO^yD9)?6=SKt4zN0Sr$g18ZZ1AFv9Nx$)MZVt^W|creH8;Lj`AmFo3KN% zQYIHzqLe^L3+Df~YSzEir9>Fzu>VtmEL(TqAc_f)s#Z}I{dPEzK?fM=o9b2ST)X`r DP%iL7 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-note2-0@2x.png b/osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-note2-0@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..456cee538227bd08e4edcb652d421db6cf08d5a2 GIT binary patch literal 1865 zcmbVLeNYr-7=MqDPDjhi$;45%GX=G~-TP3y^-ekOKuaWdPv0DemM{G1ZoUlPs+wBpa2N<|Hm)nCs;TGK&%s2bJk&jEc^0YZQpvX;i#D z!-m;7Gv#tG$m6NSd5OtnUKXi$D(22X3|<@((3D6ZUV1qr;9jF*fES0dq(&9UfQgu8 zR6GhQ)n-S`EKecPDojaY7=}daRZhY|kPgS2hz8TBQA~%bbxI7wwR&8qLk5omy5pTo z@gz&!pfmU~DqNz-;VA0ycvK#(isds=wO+4BF%7EGD4~T?$Yn&rt7L>p0R{^tki44{ z-7JGhjD&;D5seBM>Z2TJE=bD=gLQ%xL%jrts#TbjQ^3$>`*%~C4q6Lh67@LVeH7PLf=4xS6L2k>!(Fc6p$Tb{8wMf{W!4jYg$IqHF}|W+ctj0f^0p zTNyzl7?QGDj0#Aia=V?lDONo%Zl*?u#aia6)v@MTvot!>OcPYkii9TJPeU{I;IPxLyV zf&(Op!O?|};P6unoOm9N_9*3|yKrNCR*Nawd!_A{3|Go9aOzfL^WMv^C4BOAVc}(D z8%^%qf?1u1TjFo0qAywQy?lCI#n_0j@AtW)7DlaaJGri+cw%2k{I1D;$H#7zzn*4a zV;jRaU7^E%EE(x}e%g-wIc4SF2py~LwR6XIv{zKG^^FI0<<~>udKUnI03eq({n^F; zgMqH5yD*vm{Os*#kNaO-^=D!3Xs|DK{Qb(--G-m(;h~^T@UcI{7gye?I*@&1EwE3V zkgwqo<)YM((1M>_9kunv74G86F(7JFb+6%}=}2d7^%SxI*uyv1e0;65ynS4}^vJqe z(X}8i!`2r*@YnD{`RX^$9NY?IJ9TMCD9+H1riFa13n|E0>-C23`c{_6zsuEiinwXbER_R}vH9y6Ab={+~h zB{5YiN31__-x1ZgsJZu@_<~5Va4&!ET0|%SG7tm6XaGWk_YhjdKo z<~2#nYi$0bxEAIE;}1H1oKjHr#P3sxfr|VmyrB3-hAa%2e`niUCL4AEc&xFBmP7NF G6#fPM1W^yn0|%cV4pHWG6)E;Etaj9Un)2svYXhM0?)VP=Gx%C0>$bUCH7 zyRcD`B)i-igL5Ls$z=9b&d9G3Ohlef~I|=Q(}H{c z&&nI?&31vJqTLfY(C$Q^09s-UjTnJ)*nzYYkYEA^#G@hwj94a@B(O&<@RDF#<;I|p z3n9E1d(@|*0=<2aE-Vg&v_WGnXjm*3X+uOuP{XOT@Njb^9*f6eumlW_V1dPwEQurn z0r~ks!Ra^=ktBbz>*r+f$sQHO&@#E7b%GVc2&ilf4vkgyv>52^ z{l7yQj4#n#o9C)?4a;h@` z%QHR5P62|aZ+eq&bj5G1n^+a1>xCqH<{*N6wO33MP{U>ia85#v2Zv?u&y2}9t^OitR0|?*SnV$Ou`?G|i}y=Wz>O zKiT~Bj>OoTQ~Oh>PIrgjp6^U{-EYr}n{ltMvMH}#ElV)aU0UEoD6wAZpBI#Vp6#Yp zv-QL889RehQfmb-qN=Mc&)xiSD8@@0Ax<2+j>a)OwdCLLFaKC;_^LP=r|dp&)kxL` zm9NW~8#7x^oT{L7Tzxf)Q+|x}x?nhT!FxSIoZfZm%EYfn+sj+ti(Ysczidr!>S)%5 z{a*KSXIkFqT9Wh$mEthy-hEwb5`NE1DX43BKDmq1Jy3bIzj31MK+;yvV{X>85e?8; zwyPpt3dMDur5{SNz91V=$k+HymDrw@+?VFwnzw`adxfdtT5fi$yj9_FaGw3-8n=Hg=Hnna zDLG(+^3VW+Lcu|&O%Mn%i9<42GcM8WZwc*Egk`q1&OH}uHa8zvY-Hb?s^VvE-dMea zoZKZlWd2jOY(v!ZT;17AiueCG#COZW7~jk=*wlu`rCdZf_6kR(uj51Q-aZrd&Bfd> zeNeGKN!mTA1^lloUWNBxjynIgx4lS~H$HVN)e^YpsV^TKtu?+`9z%E50Qrx9c9HB{ zKhT z#v!QYmxlt_F42-@E_60=c0C=O ztWl~{COX$hWFqNbGoQ?9!z!B4DJ7RBMMG_k7wK9O-lWdzrK7D61s|H~gKIYu_3dY0 zr7VY$W^!5M4L2RWa(6-aWPl9b)WVLt@#aD2WmBTlVXcy(m}Ut<0fXO5clpHz8$4cT zVt8xx2UER+KDO%O&LFHw|5&ZkvB&z+Nm#5)*{wg~^49FzW;XDSRW-H-A?~a?wfL=ryM1 zH`Q{HTCX>4%c_T()+g_n@=7|CMuf+Xmdo6PL3gU`Cd3VGl;O~qu$Fguzv&Nc%X68n zSKnJK(Z2G@H|HE1c!@PFN}*}#=@r~|rEH!w#uj!Brv=KJ$5yoh)9PN)z3Z=gwoZuG zhozlm!Ldc@f23k^j5E^<6O^Jb|KzuMW~TcPELZTWcH{fSP?gk31&<$ zN94RsP(s@V9OS-KYmhe-9P5QN;$EC?t=vR%xO%RUbZ~OM7R>RYM>{(`Fkc`@bKX z-*oYWvpiO9%Z(=<-+_6I*ros$YIT zl0{1nx)XIuPo?K?v+0e<+c`J^?%3p_%C@uSx1qDYE+n^ja z`cHDkG7Ydc|LF1-fDj+K!_U@(zeXn$s>4<8%V}vn4Od|YyN?*VEdOw}x@t?s03ZR- nQ2mAe|K_(~7E(DD&s)?1(zQFTFZk)Z0sMGSe8@MQ|9s@%;dFKP literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-note2H@2x.png b/osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-note2H@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..e6da7a10550e46638ecb65a4460eea43eb4d66c8 GIT binary patch literal 5294 zcmb`KXHZjH)c5xZp#(%gX`(;`Q9&spMHE6*xKgBuND(Q5C`AY$z2%@+MO2C?5fDKs zp-6{Nq{K#*8tE;f^eTiF2zhcp+z;=je>q_qch-+06Ootsw7a zXH5VEP4#uon+Fcgr?kHK)gYjI0x}~>mw5$FXs=(|dR%4}_>Rjp3zhLl3YDguw=b0D zqB0dpaf=JwUrChX5p8Dfu8tuA(3aqR%rb;n`p2L31>p6K2M*@%Fo-{=7Cpq7)0;{7 zRHjT!q1D?YbGwq&l_;a2!0SO%jXl;A^2GQW`ibD4-hRm#Vrg#fRx##da9CgX46(pg z`zB>AzMYHV|8a9tw@}e7?x*vc9fC1)^X$$^l!G3AYoorqgW1%ZKe+M5z1m@t=a_G8 zYrMu0%I=Q@xj3DDCSYH*z@6l8qggFJnW9O$=k(S3AVbeAzEIrS7o5Bi=9}lYr2+-m zc)LI{{JLc###hUY;nTBhmfD=RSu@JLmNVjYyZcVX4%KaK`g~WTMH=5B5iNR7LIGQq zE}rtNaHo57VT0CM$f{=pG|-q+9Tokl@9|O0^JrUjW}_4&`xkb1EYBnA;gX==zDNtj zAT%~?+wt1T*W}sjWV`$ z&UCp$V{|j9@}BFh)pE5tPgEn)G+9=OfGN496?nH6r^CmCHEhhREGv1lm&f5<9Ld9Y zVY79dZQyH2CbG<(7T`1;;6$^3gl}LTqC@d)4Po(N4Ut2mf>S;tmtMp*RDs_IAU&*M zyF|XAly&Tujq{~DRh(Y7N&QbO1>s_mfok9KGblff-&R+QKik5AQ{Na_Z}wZswH|6; z*qzH9^N;EycI*vK%LYbtt8IAqfd-`_=1>!y6`t1rH(iYlbr+a`M}cP z7&4zk8nJ9jH+a)j@ONeL1=*7x`+Jh933pNchW?TRFNkG5bTvo1ID)@0q&(R!DpPJP zO3zFL4(vK9dtLKH6w`(2AV4M*j+uk&x!6N4+J9NtMO)@?fFC@vhJcQMTx#>~g>HTO zI&rN-u*XkH<1)f4lr*@MM(;qKur_g*o+lhaqTn3G_)8yc)g)w_fwW7V^MS4f! zC+Go4FeaO`4eAho3{4KuVz{v52>x{Ql0?grm$9}Py+p!XOe`o7-f$~u`{NyQc)+gd zP@CDA7j6|a8Nr+v3lUu<{zz&7>WdcaQ=Dyu$mOQ`bSX+QCVbM6f_#YQFWG${9Rn=g z0Gd9;OmcJrZe>{u5-?pn;&WW_UfANIk{k&82+9r(+=Qm%eBy_ZozV--@W7M~Mis7H zQsMsO%Z6}rgar8ry!cBwgE`SkXol=Rwob!2$9=7UBRBS0o=pjv76Kd-0!SLJ6))BC zE5czGq!xDcP3U(zmt)z4G$QssAkDUz|5Nl*yzBb&X-~zkYG&s_iH5aDN}~amb7<4- z&-Xz4uMZHN$Jo<6XO1d!eegegq4nTOB~UpD|K`F5FwH7<{LDe}!YQc|g&3pCJc}>3 zrRgFfaNRYq@S4eRBkC?H$lJ@n_5Sx{#w2$2A7-MfAtb`E! z6q9-5rf)Kv= zzN(^?ewCjhPKZoDE+l6}@0w}U)y71VWK_EqdVh&;*+O4W^qo08(R{IWRbgm9KV0`0 z2s(Fz4bkbtmAgpUg#qmvcr!JIsnDN*$VA14WBE~4}ez8p&G2Z1exJut( zSkY2*nNBo3V{=oPVYeT9_9&1YHIY(>^s+gNEFQ{$0UmS=wI(;5Y3>8vB^D#m$<)zx zJ&Tny$AL8=?ADs1b&Av+Bg_D7i9VUXPOAbL$HA)=>K2g(bZE*v)E95kx&-Av*O-8~ z1gb4?yZm0)&-U2bU~c;<6pD<&%q^VUxH69D{J`mT8Mtv_TPIRJFljGA_Z(7*v_m8@ zHII9_4ZZsrj(O^L;q9|k-0S*eQSs$dbqNJzhx>+oBRGx`yCp}@1?PVqhXif*fq3dJ=dqI<~Oc zNjrfr#MP9x6loy;V;}}a?K%aBqqf;g1SGYb<^g4}4t1L!7LyN?{wgWPX0?|3R)p+k z@SGPeWbagQYO7BPINwb>gB?;MQ1}Nu`cwy}49kPT1MO;0Z?X|vtLd^481dgj)kW}M z0yF2KdFgAjy;^FA*tLbBcgn2K#DHvz<;&%Coo&$)= zLc`VZw}QV$J*p&Y2g=rGLY2ps5ZpA-;slbp_CCHJzZiTOR4-^{Tu9WDJPGx%r@h)2 zD?&Lj6U;~+7bTY>BKIKX)J0%n_ReU&+31ehtcjqa!wllIeT!O{Vw?or&c`rb9ai-! z|0D&6&m%<>A$fp_Y6(#{pgDnh|nvsrHUE_7F_Uw};KFlfd_sSd8>NzcA=sgWR4 z5X-^9XXt4jD7g4>djdDR)QiuG(cRaCPS+P(@kK*TJLrC23_b?Y`5tsr5S>M! z`#kg=ACJ`mbTKF+2`olzw;|I^xE)*;?M>?bY!p;Y?5Z-RLN;WSInq`s_30c9mfG@j z>vR=pSi-YiqGW9_c_7eP*6dr7;hX6jCaJGv)P_pkWuMv@=WvsU)KDk~o7>)UvU9@0 z{;$iG(0M(xp{^>HJb2y4JG(L$oIc&t?1)K#RlHIg&atNzfU=|D-t#C>rp&QS6ePRt zac~uH^I+B`mIT)cxQGe7&e;H2iqu(HtJWAF)WWbXGFmmPxf4Ors>l_|2%Sr7CVcP3)dMEqB(U90 zly{R_8o{?vB+$|UX-b21;(bt84zl^DwakC%^%VH#$rT+(-xLA9B zme4F=S%O*mzy0tphW&RL(X3LiEWs@Cga04*{(GPQKI1=?|9hHPRtU57?;apo@qcUi zzh$I`Ox&tDw%VGm|6ZT&YGB=_!YB>C6J%eIYHT=sIkkH|Wld^=ekalW^wdi9u%*;S zW^qx_OD0tB-3u?u))jtln+jVKZo!32i zJbzT=R6flOYKlkj3j~=1`g?x!iz{~h!ApEgE-oQo->mT_2)Lh{@-V;i3OBHCWXDs2 zu(mF^w+d6GbzOregQV<7z&yG~i7q_+UYs|wr&mKmInl3M?_6CHN!&gxFH9Sk)Z*3o zdjz33_7Jpa!DX!57Y)3Tu@?|l6%!SdB^KnA4U}#KAYzHZz>FVDH-|E2G}Q}~dK}k- zQ)e0b*NCz=C}LxePNu4@o-NI(_$z-Y=c9+1mVprDaq_;ybxL^<}PE`Dahxyen@swY6~^G>3q^nKf6a$<0KSFRf=O*c80quKdx{b@kZS z9@HmiR3fk#%4+VPoF{)E#D9nxlY?m)prJG>~!_JWmO)Rx^$UW&Cyid%lg^1mw52`Si-0{<^RD@M8gKfLl!d zp$|!B#uW-|yj1Fx_pw%KEWm$P710TUZgml?9Cw;E((7!+)aqubmdE8zm0}5O=}DJo zyOgS>>TvX$Ycw;9@>8|AcJy0~jv=Dx(U{u8R~wXz2=3>;PIq=wQKZ`MJ3M63Nujx` z%^VT=6Cd2^CHZOx+~UF#tykRYED?h|WQCder?vu+UM8pM2M&#^21UpWM&L~vkxdIW zpM$LG4U5H(MS~v$9DfFzgrLBi`O#6ctoC{gxaFs>bx3RP4*a?2j#J!+^+{siy<*FQ zRYRPySM|ECqBK%3Ep~v-Wiac&?Sn`!Ow5e7hhKa{!6z!)LMYU93OZxd5W0u;p7=m@ z;zBR57vTgB%AgDN#tE>QMNP?<_D&Nk&|u-w;D6OyF`C=KK2nwsk!(L@C%UMm`|a>| zkr-%?{$R`25j(9xNrMzcP1$u&*3ahED8^>kc^`c54hR~(c97J|rpL*icH2nrsx_E_ zpp$+(cSo~9F3X88bGtJ9_8zJ;f(3sRni#v%-$AEe!DWMNRvKv@L|R|?-EEVL&Q2W; z`>=Q+Y)(czvE?E{?-UeyO<=UQMj86OHXQ|C$N>F3n+SiKx0VH^KAqK{-_E%0|Joo5 zkMyfWxjts6Mjpwmy*<2C>nmbf5O1rjJ-~K6M&`q4_Z8rnpJ9dX_+=JVWwV!EeYiYB zl2^6LaAB^7*G2#v{2cVAiaPUG1YQ$kTjq#xLg=MR!H$i}mb$rpyDIrZ&3#wk;%2;E zH$K>?jY;)L>zm+R%~*9c#wR|GANq8JP`ouJDU4NScQBeBdo*2+<#`a10N!9b!kh(7 zzR$QLiVSTMIT8Piw~TD?;xO!UzV0||z7HFYG*eA9%wfJi1?Rk$!XH%N(~o%C#`V<4 zb^0C2P6tc~JBGcXRsu6CFv~@@$r2!NvQwi!2^wmRembYg5sL^B%ob-o9t_uZRq7^b z%$d-$sSQfFdG~ie*kW^Vp+-znm9IxYSGWLnkQ>b&3?>v7PSAIsJ8~Mn)dQg``5>i zN~3#%lLVD^G}WiNkd#&E5>m)!BKww09_qZFqZN?9_hn@bXZ;-6c%V z_JN7rOF9ysb3+GCXWxA_K=*&JriEB8At}Hsc9n6qvh80(z{H}y2K7X|fjXw|_26PQ z=x#+i5YF$-in2B^_>=e)5&1Q*x0;tVSUL}*!jhiZJymb+dYj78_gbX#!ksIZ!NwiG zDPwZZs~O?62?} z&p%-yGnQbWshYrUnp|-NUg;hV!yAAIpXVFA5)2-R@uZxT>p97f8WuaCztd-P_L&Ce z1bfxKOMX2YFkh(!W0!kwbAMmMK$Q=ZU|{AOqw@P_wchyin6jYUu<9qwGEKfWZhX~u z5S6|&6=NIuD>Ic%n&?no{3X!!)O5I}d6I8fW?#_q2rjKLInD=Nbc7#!3UYC7+$%}{ ztRrT<%flkm%}r-S=Q}OR+LsQt{G!K5ew^~71bz*r#}ZO?5+_s>yzZ+1cDT{Np2H^| zZ|bgf!~f~s1Wctzqe18+&>Qo|m^YcBbFs+2IttKY3TFuRlpNL8#}x--30A@5ir6^f z(Xdd@35p%!5d`N9%at^dJ b9^wF<;)Fjl4vsg(S7D|NirDw%t_mPhsRH&ps0wx=kk!{c4Px{Wy7o(s`5trFz~m-V_g zT}vyxjFM-rAM#gRP|l%;0I<&Hse}Lk$w>QZ zHR$J=QF~o4Xr_ccigPF(4_a`qdO!~Q2Mmcr*HWv#4|eFBWo{?-K>;( z)U-b)v8zlhz&C-RQkC7xX^m@`jPU}`mTAR=P-2Fg5Ze!t@B9X6ex1w#5`~sea@ua) zZfgy7w%)QMc** zkdz^;`q7RGG}fh-W^Jgjgvo9#KMLtBez&3M?W3@GJgznSPGp^5W1$Z_EU;uH3Xv6U zmW0!5Q%`pZw_k|#fK0=))&5D%^_Crng9l*cfzAPpa`|7!ZM zXiJ$Hwf${km}jo>Wc+tDSU@VkUs=4%nXI-jWF=hy_IMUn+u!0dR^8P@X;Sa3#Ierg zR_{>idUn6kSV}>h*?d1tpoiip1gVm6#|7XO!c5Tb;Qgw$L!giT{$T#31!&ZIW)I?8 z?0K+x5kyOVf>uLs_iV4Lwb(r#enW$I^fxOV6K&Mf?e*)C@VoAihlp*8R^;!%yp&2_ zgBb_Wy>YG2mtR%*V+0Vgss{;FdgaWP zsyYxT$Yu!=!_tnhpr25!E$y!LoKgOkOy)<+x69bx?RYqB1Xy(x^hb$Hy0du#$s0E+$aV7~gYw#^SR@Po9B(HhDWXJgjn}GQvGl5L;C&SWjBRKFl&=ZlAxY{fm zu=$pku#7|vGSwr|$8^#KnJUW?NZtYt9N3su+N3q;xJi+0e^$mx_^5ylh&+ppU~vco zGn3gk4ZcS)&scFl#fbZICEuHG|LNWP=r;aVvRPfM6uC(BV-Qj87u;NRDXYMTQ%$3%@gV)m_*?z zyZc9FA0W^aqGySO4)-ylN78-BJSHqB)#g_*S1ef~&x&}7IC;fhMF&iy!F^lkW#{dQ zOsIs1;Qi&^dFtZ=R$t7UKjT`%lO&;KbGQ~hTFd>sLo?YH0^ zUR2DySum5dh5)mZ5(p;@@h5?lSQHe>_DqQeYYS zXY>5?5&vfQ|K#r9y!dZ6pc#(*$3>`1tVO0MbrajWJGzSkMWhevQkq-d)QT-ni!EmK zWj=V;_FZ5^ZK`KQNKP_2ZT{Sj?FA{BeKpJ#!?_z@UMBZRMBe(-)|c=51{bb7GDAM^ zJJMH-nm*c~af7DRF*#a4rVTDZ8MV)>$-(~IrTL%p8V*O2do%quuIg?|JITfaH($l;g5o4f_9)5RbuZJaA4#xA%#1J- zM6kBZgyBSI`9aCx?nsJXh0s@FC~`qA#f?S{N(?{oN=BZRmF9_d@3&1E+D_!LN!Oa7 zgruc;rd{}83`?^cY#$(-Nqi@1WoTVKM{5Xv@sieG$c<&ZLnN39uMnuD>9*%SM*sTt z-4;yf0{IBbzVhBfwRw5({AsD~K^L);d6L)+-ru;ncN@OF@pOvK4j*`{3|IJ&%39Q} z>Fp_8CB-v(k*UnfjjVVoYb!8kacj2{QITG-D4QVWkqEOAg5pI{8oOk6HnwGwu-1>A z3PqE+WewV%FMKBD8t9zUm&ez$66{UDuLIcmLK}{e5^E6=IPQU2ABCHEO4`4LX=Z(~YtdIT50~w4`%FbdZkpyY% zI48CUKGe|ZezrsvcD~r@HCWWlb%Pp6C}7EhwLjLl<<{V3BeXBo{@$9m>^A87@nC%o z*A|rXuRYsd1+3h>phnq#t}j{J9b-ffD?ApQJ@C`~_(2%V3W?HB<-gc@K9mc+ZzTQK z_C8a45)UMEE=8Gl4stBVF*|fPGCm zUYK%XE}wInQb!`U5ob(*v?zA9LM=4e9rn@n{z3r?{|*=oujf1e5#V1hiFFzhGqCO0 z5=)9RWZc^Byl9K=1$VFlceryqiL|8&_|F0}T;Jv~!W0^GpliC`yLKanJxzkiTxjdI z1&+{OU9S_h&z+-YPgPli{t0JnE(pQN^+_OP-@1rDE_*9X#PAd8(y)q>CgZaqy&ajh zNJ5;ZY|MJFG&k`mb%-Tv?l5&Uzzwn*m)l;kWF=7YZ9{Kay&RE>d3s^!B4Xy31PHBG zs_QF1Hu6Oh>&P)PkxgM7s`S?`u=o8tMKNC<_LRN8cI3L?!DAC(Kk#dvGm8~!$N?G3 zRpsY^d+dv!rGf_^U7>~^ErX6md7O`=?vj^tOUdo}9fNPIR~n=AQdPvE8bz3_rH{Wj zHXzoLH7fvbAMC|^wHioL!Y?xbZ0XfYS#s*!=*3r7t4HxV@JCSl9nZ^-VB`h84KIIE zI_`0_@DMr9K1EkAz+6@bpH$&>db^}5WLEQzPkz&(QWW0B z0&SEPmt#icvcW$@LSqP=v=*rsbunOJT#Sy;5`%U0tzG<az>% z$_#Ab4*u>hKt{fkpI_kY86Q6Mg6*;j%;TXq5TXR>DgvfXEkryowwM`;4uLiruq79|&Bl(8kZSB;w}kG2Pb zHh7El(E|zCDAgNYHH%v45vQiX8dTJO>B;7ImyUfEZr*q^xl%H@6)*WsIjO4k<|lmYrWH?Edx HIXwI~zp|9c literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-noteSH@2x.png b/osu.Game.Rulesets.Mania.Tests/Resources/metrics-skin/mania-noteSH@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c9bc23e8d9233fbb3b3adb132c50eef6d7f78a11 GIT binary patch literal 3963 zcmb`KXHe72`p5Sd6A2*Fi-HnVK&8V$0Vx4Qs!EfN(tA-l0w&iZ2nxgl3JNG4M0y8- z5T!{|Ff=LB4j=-C9v~#SIWPV*|1)#%t9xF2XLo0Ip6A)`?(Anb&iIBd8_P)+005i5 z-c?foV2m!vL;ltHA)!b{b0SE`D#*;=EhyA6z!hk^_&d3x^nD%OT}@pbUBd5myQ%`f zeoFtUrg_-->g(S7D|NirDw%t_mPhsRH&ps0wx=kk!{c4Px{Wy7o(s`5trFz~m-V_g zT}vyxjFM-rAM#gRP|l%;0I<&Hse}Lk$w>QZ zHR$J=QF~o4Xr_ccigPF(4_a`qdO!~Q2Mmcr*HWv#4|eFBWo{?-K>;( z)U-b)v8zlhz&C-RQkC7xX^m@`jPU}`mTAR=P-2Fg5Ze!t@B9X6ex1w#5`~sea@ua) zZfgy7w%)QMc** zkdz^;`q7RGG}fh-W^Jgjgvo9#KMLtBez&3M?W3@GJgznSPGp^5W1$Z_EU;uH3Xv6U zmW0!5Q%`pZw_k|#fK0=))&5D%^_Crng9l*cfzAPpa`|7!ZM zXiJ$Hwf${km}jo>Wc+tDSU@VkUs=4%nXI-jWF=hy_IMUn+u!0dR^8P@X;Sa3#Ierg zR_{>idUn6kSV}>h*?d1tpoiip1gVm6#|7XO!c5Tb;Qgw$L!giT{$T#31!&ZIW)I?8 z?0K+x5kyOVf>uLs_iV4Lwb(r#enW$I^fxOV6K&Mf?e*)C@VoAihlp*8R^;!%yp&2_ zgBb_Wy>YG2mtR%*V+0Vgss{;FdgaWP zsyYxT$Yu!=!_tnhpr25!E$y!LoKgOkOy)<+x69bx?RYqB1Xy(x^hb$Hy0du#$s0E+$aV7~gYw#^SR@Po9B(HhDWXJgjn}GQvGl5L;C&SWjBRKFl&=ZlAxY{fm zu=$pku#7|vGSwr|$8^#KnJUW?NZtYt9N3su+N3q;xJi+0e^$mx_^5ylh&+ppU~vco zGn3gk4ZcS)&scFl#fbZICEuHG|LNWP=r;aVvRPfM6uC(BV-Qj87u;NRDXYMTQ%$3%@gV)m_*?z zyZc9FA0W^aqGySO4)-ylN78-BJSHqB)#g_*S1ef~&x&}7IC;fhMF&iy!F^lkW#{dQ zOsIs1;Qi&^dFtZ=R$t7UKjT`%lO&;KbGQ~hTFd>sLo?YH0^ zUR2DySum5dh5)mZ5(p;@@h5?lSQHe>_DqQeYYS zXY>5?5&vfQ|K#r9y!dZ6pc#(*$3>`1tVO0MbrajWJGzSkMWhevQkq-d)QT-ni!EmK zWj=V;_FZ5^ZK`KQNKP_2ZT{Sj?FA{BeKpJ#!?_z@UMBZRMBe(-)|c=51{bb7GDAM^ zJJMH-nm*c~af7DRF*#a4rVTDZ8MV)>$-(~IrTL%p8V*O2do%quuIg?|JITfaH($l;g5o4f_9)5RbuZJaA4#xA%#1J- zM6kBZgyBSI`9aCx?nsJXh0s@FC~`qA#f?S{N(?{oN=BZRmF9_d@3&1E+D_!LN!Oa7 zgruc;rd{}83`?^cY#$(-Nqi@1WoTVKM{5Xv@sieG$c<&ZLnN39uMnuD>9*%SM*sTt z-4;yf0{IBbzVhBfwRw5({AsD~K^L);d6L)+-ru;ncN@OF@pOvK4j*`{3|IJ&%39Q} z>Fp_8CB-v(k*UnfjjVVoYb!8kacj2{QITG-D4QVWkqEOAg5pI{8oOk6HnwGwu-1>A z3PqE+WewV%FMKBD8t9zUm&ez$66{UDuLIcmLK}{e5^E6=IPQU2ABCHEO4`4LX=Z(~YtdIT50~w4`%FbdZkpyY% zI48CUKGe|ZezrsvcD~r@HCWWlb%Pp6C}7EhwLjLl<<{V3BeXBo{@$9m>^A87@nC%o z*A|rXuRYsd1+3h>phnq#t}j{J9b-ffD?ApQJ@C`~_(2%V3W?HB<-gc@K9mc+ZzTQK z_C8a45)UMEE=8Gl4stBVF*|fPGCm zUYK%XE}wInQb!`U5ob(*v?zA9LM=4e9rn@n{z3r?{|*=oujf1e5#V1hiFFzhGqCO0 z5=)9RWZc^Byl9K=1$VFlceryqiL|8&_|F0}T;Jv~!W0^GpliC`yLKanJxzkiTxjdI z1&+{OU9S_h&z+-YPgPli{t0JnE(pQN^+_OP-@1rDE_*9X#PAd8(y)q>CgZaqy&ajh zNJ5;ZY|MJFG&k`mb%-Tv?l5&Uzzwn*m)l;kWF=7YZ9{Kay&RE>d3s^!B4Xy31PHBG zs_QF1Hu6Oh>&P)PkxgM7s`S?`u=o8tMKNC<_LRN8cI3L?!DAC(Kk#dvGm8~!$N?G3 zRpsY^d+dv!rGf_^U7>~^ErX6md7O`=?vj^tOUdo}9fNPIR~n=AQdPvE8bz3_rH{Wj zHXzoLH7fvbAMC|^wHioL!Y?xbZ0XfYS#s*!=*3r7t4HxV@JCSl9nZ^-VB`h84KIIE zI_`0_@DMr9K1EkAz+6@bpH$&>db^}5WLEQzPkz&(QWW0B z0&SEPmt#icvcW$@LSqP=v=*rsbunOJT#Sy;5`%U0tzG<az>% z$_#Ab4*u>hKt{fkpI_kY86Q6Mg6*;j%;TXq5TXR>DgvfXEkryowwM`;4uLiruq79|&Bl(8kZSB;w}kG2Pb zHh7El(E|zCDAgNYHH%v45vQiX8dTJO>B;7ImyUfEZr*q^xl%H@6)*WsIjO4k<|lmYrWH?Edx HIXwI~zp|9c literal 0 HcmV?d00001 From fa693bb8a83ff8ec32c59e31f2a31211af76b8ea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Sep 2021 16:08:09 +0900 Subject: [PATCH 1960/2442] Move `MusicController` adjustment set to inside `OsuScreen` itself (and result `nullable`) --- .../Visual/TestSceneOsuScreenStack.cs | 12 ++--------- osu.Game/OsuGame.cs | 2 -- osu.Game/Screens/Edit/Editor.cs | 2 +- osu.Game/Screens/IOsuScreen.cs | 4 ++-- .../Spectate/MultiSpectatorScreen.cs | 2 +- .../Screens/OnlinePlay/OnlinePlaySubScreen.cs | 2 +- osu.Game/Screens/OsuScreen.cs | 20 +++++++++++++------ osu.Game/Screens/Play/Player.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 2 +- 9 files changed, 23 insertions(+), 25 deletions(-) diff --git a/osu.Game.Tests/Visual/TestSceneOsuScreenStack.cs b/osu.Game.Tests/Visual/TestSceneOsuScreenStack.cs index ed3935e101..7729ad0ff3 100644 --- a/osu.Game.Tests/Visual/TestSceneOsuScreenStack.cs +++ b/osu.Game.Tests/Visual/TestSceneOsuScreenStack.cs @@ -25,8 +25,6 @@ namespace osu.Game.Tests.Visual private void load() { stack = new TestOsuScreenStack { RelativeSizeAxes = Axes.Both }; - stack.ScreenPushed += screenChanged; - stack.ScreenExited += screenChanged; Add(musicController); Add(stack); @@ -129,12 +127,12 @@ namespace osu.Game.Tests.Visual private class AllowScreen : OsuScreen { - public override bool AllowTrackAdjustments => true; + public override bool? AllowTrackAdjustments => true; } public class DisallowScreen : OsuScreen { - public override bool AllowTrackAdjustments => false; + public override bool? AllowTrackAdjustments => false; } private class InheritScreen : OsuScreen @@ -147,11 +145,5 @@ namespace osu.Game.Tests.Visual LoadComponent(screen); return screen; } - - private void screenChanged(IScreen current, IScreen newScreen) - { - if (newScreen is IOsuScreen newOsuScreen) - musicController.AllowTrackAdjustments = newOsuScreen.AllowTrackAdjustments; - } } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 2107b3a0e9..ce84c4bd2a 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1075,8 +1075,6 @@ namespace osu.Game OverlayActivationMode.BindTo(newOsuScreen.OverlayActivationMode); API.Activity.BindTo(newOsuScreen.Activity); - MusicController.AllowTrackAdjustments = newOsuScreen.AllowTrackAdjustments; - if (newOsuScreen.HideOverlaysOnEnter) CloseAllOverlays(); else diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 5bb47e1c11..028662172d 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -56,7 +56,7 @@ namespace osu.Game.Screens.Edit public override bool DisallowExternalBeatmapRulesetChanges => true; - public override bool AllowTrackAdjustments => false; + public override bool? AllowTrackAdjustments => false; protected bool HasUnsavedChanges => lastSavedHash != changeHandler.CurrentStateHash; diff --git a/osu.Game/Screens/IOsuScreen.cs b/osu.Game/Screens/IOsuScreen.cs index b12baf233f..910a0c7d61 100644 --- a/osu.Game/Screens/IOsuScreen.cs +++ b/osu.Game/Screens/IOsuScreen.cs @@ -59,10 +59,10 @@ namespace osu.Game.Screens Bindable Ruleset { get; } /// - /// Whether mod track adjustments are allowed to be applied. + /// Whether mod track adjustments should be applied on entering this screen. /// A value means that the parent screen's value of this setting will be used. /// - bool AllowTrackAdjustments { get; } + bool? AllowTrackAdjustments { get; } /// /// Invoked when the back button has been pressed to close any overlays before exiting this . diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs index bf7c738882..c45e3a79da 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs @@ -26,7 +26,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate public override bool DisallowExternalBeatmapRulesetChanges => true; // We are managing our own adjustments. For now, this happens inside the Player instances themselves. - public override bool AllowTrackAdjustments => false; + public override bool? AllowTrackAdjustments => false; /// /// Whether all spectating players have finished loading. diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs index 054009a228..8c4f0c1394 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs @@ -11,7 +11,7 @@ namespace osu.Game.Screens.OnlinePlay { public override bool DisallowExternalBeatmapRulesetChanges => false; - public override bool AllowTrackAdjustments => true; + public override bool? AllowTrackAdjustments => true; public virtual string ShortTitle => Title; diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 01dc703b66..d3981b715c 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -11,11 +11,11 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Game.Beatmaps; -using osu.Game.Rulesets; -using osu.Game.Screens.Menu; using osu.Game.Overlays; -using osu.Game.Users; +using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Menu; +using osu.Game.Users; namespace osu.Game.Screens { @@ -84,9 +84,7 @@ namespace osu.Game.Screens [Resolved] private MusicController musicController { get; set; } - private bool? allowTrackAdjustments; - - public virtual bool AllowTrackAdjustments => allowTrackAdjustments ??= (musicController?.AllowTrackAdjustments ?? false); + public virtual bool? AllowTrackAdjustments => null; public Bindable Beatmap { get; private set; } @@ -96,6 +94,8 @@ namespace osu.Game.Screens private OsuScreenDependencies screenDependencies; + private bool trackAdjustmentStateAtSuspend; + internal void CreateLeasedDependencies(IReadOnlyDependencyContainer dependencies) => createDependencies(dependencies); protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) @@ -175,8 +175,11 @@ namespace osu.Game.Screens { if (PlayResumeSound) sampleExit?.Play(); + applyArrivingDefaults(true); + musicController.AllowTrackAdjustments = trackAdjustmentStateAtSuspend; + base.OnResuming(last); } @@ -184,6 +187,8 @@ namespace osu.Game.Screens { base.OnSuspending(next); + trackAdjustmentStateAtSuspend = musicController.AllowTrackAdjustments; + onSuspendingLogo(); } @@ -191,6 +196,9 @@ namespace osu.Game.Screens { applyArrivingDefaults(false); + if (AllowTrackAdjustments != null) + musicController.AllowTrackAdjustments = AllowTrackAdjustments.Value; + if (backgroundStack?.Push(ownedBackground = CreateBackground()) != true) { // If the constructed instance was not actually pushed to the background stack, we don't want to track it unnecessarily. diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index e8a2790c94..9927467bd6 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -56,7 +56,7 @@ namespace osu.Game.Screens.Play protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.UserTriggered; // We are managing our own adjustments (see OnEntering/OnExiting). - public override bool AllowTrackAdjustments => false; + public override bool? AllowTrackAdjustments => false; private readonly IBindable gameActive = new Bindable(true); diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 9b6cbad7d1..1f0f134ba7 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -53,7 +53,7 @@ namespace osu.Game.Screens.Select protected virtual bool DisplayStableImportPrompt => stableImportManager?.SupportsImportFromStable == true; - public override bool AllowTrackAdjustments => true; + public override bool? AllowTrackAdjustments => true; /// /// Can be null if is false. From b58415fe198b571dd00640f8689fe22f304bcebd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Sep 2021 16:12:14 +0900 Subject: [PATCH 1961/2442] Make suspend stored state nullable to ensure we don't break it --- osu.Game/Screens/OsuScreen.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index d3981b715c..f425144c6b 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -94,7 +95,7 @@ namespace osu.Game.Screens private OsuScreenDependencies screenDependencies; - private bool trackAdjustmentStateAtSuspend; + private bool? trackAdjustmentStateAtSuspend; internal void CreateLeasedDependencies(IReadOnlyDependencyContainer dependencies) => createDependencies(dependencies); @@ -178,7 +179,9 @@ namespace osu.Game.Screens applyArrivingDefaults(true); - musicController.AllowTrackAdjustments = trackAdjustmentStateAtSuspend; + Debug.Assert(trackAdjustmentStateAtSuspend != null); + + musicController.AllowTrackAdjustments = trackAdjustmentStateAtSuspend.Value; base.OnResuming(last); } From 3495fae5191eaca5fb0842873830071e3c65b9ce Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Sep 2021 16:31:41 +0900 Subject: [PATCH 1962/2442] Handle potential for `OnResuming` call without an `OnSuspending` first --- osu.Game/Screens/OsuScreen.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index f425144c6b..ccc891d3bf 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -179,9 +178,10 @@ namespace osu.Game.Screens applyArrivingDefaults(true); - Debug.Assert(trackAdjustmentStateAtSuspend != null); - - musicController.AllowTrackAdjustments = trackAdjustmentStateAtSuspend.Value; + // it's feasible to resume to a screen if the target screen never loaded successfully. + // in such a case there's no need to restore this value. + if (trackAdjustmentStateAtSuspend != null) + musicController.AllowTrackAdjustments = trackAdjustmentStateAtSuspend.Value; base.OnResuming(last); } From 29ce2f05bdfeaeb317220383f608b081f39195f1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Sep 2021 16:44:46 +0900 Subject: [PATCH 1963/2442] Remove implied defaults --- .../Skinning/Legacy/LegacyCursorParticles.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index 73820b8df9..3357508d24 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -53,10 +53,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Origin = Anchor.Centre, Colour = starBreakAdditive, Direction = SpewDirection.None, - Active = - { - Value = false, - } }, kiaiSpewer = new LegacyCursorParticleSpewer(texture, 60) { @@ -64,10 +60,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Origin = Anchor.Centre, Colour = starBreakAdditive, Direction = SpewDirection.None, - Active = - { - Value = false, - } }, }; From 2df4073946bed60dc96fe5f3e44b925af63a0a13 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Sep 2021 16:50:03 +0900 Subject: [PATCH 1964/2442] `SpawnParticle` -> `CreateParticle` (and set time outside of `virtual` call) Allows easier overriding (no need to call the `base.CreateParticle` call and worry about overwriting the time value. --- .../Skinning/Legacy/LegacyCursorParticles.cs | 35 ++++++++++--------- .../Gameplay/TestSceneParticleSpewer.cs | 26 +++++++------- osu.Game/Graphics/ParticleSpewer.cs | 13 +++---- 3 files changed, 35 insertions(+), 39 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index 3357508d24..858ba98b86 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -179,47 +179,48 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy velocityFrameLength = 0; } - protected override FallingParticle SpawnParticle() - { - var p = base.SpawnParticle(); + protected override FallingParticle CreateParticle() => + new FallingParticle + { + StartPosition = ToLocalSpace(cursorScreenPosition ?? Vector2.Zero), + Duration = RNG.NextSingle(particle_lifetime_min, particle_lifetime_max), + StartAngle = (float)(RNG.NextDouble() * 4 - 2), + EndAngle = RNG.NextSingle(-2f, 2f), + EndScale = RNG.NextSingle(2f), + Velocity = getVelocity(), + }; - p.StartPosition = ToLocalSpace(cursorScreenPosition ?? Vector2.Zero); - p.Duration = RNG.NextSingle(particle_lifetime_min, particle_lifetime_max); - p.StartAngle = (float)(RNG.NextDouble() * 4 - 2); - p.EndAngle = RNG.NextSingle(-2f, 2f); - p.EndScale = RNG.NextSingle(2f); + private Vector2 getVelocity() + { + Vector2 velocity = Vector2.Zero; switch (Direction) { - case SpewDirection.None: - p.Velocity = Vector2.Zero; - break; - case SpewDirection.Left: - p.Velocity = new Vector2( + velocity = new Vector2( RNG.NextSingle(-460f, 0), RNG.NextSingle(-40f, 40f) ); break; case SpewDirection.Right: - p.Velocity = new Vector2( + velocity = new Vector2( RNG.NextSingle(0, 460f), RNG.NextSingle(-40f, 40f) ); break; case SpewDirection.Omni: - p.Velocity = new Vector2( + velocity = new Vector2( RNG.NextSingle(-460f, 460f), RNG.NextSingle(-160f, 160f) ); break; } - p.Velocity += cursorVelocity * 40; + velocity += cursorVelocity * 40; - return p; + return velocity; } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs index e6b5763f2c..c259718a7a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs @@ -75,20 +75,18 @@ namespace osu.Game.Tests.Visual.Gameplay { } - protected override FallingParticle SpawnParticle() - { - var p = base.SpawnParticle(); - p.Velocity = new Vector2( - RNG.NextSingle(-MaxVelocity, MaxVelocity), - RNG.NextSingle(-MaxVelocity, MaxVelocity) - ); - p.Duration = RNG.NextSingle(lifetime); - p.StartAngle = RNG.NextSingle(MathF.PI * 2); - p.EndAngle = RNG.NextSingle(MathF.PI * 2); - p.EndScale = RNG.NextSingle(0.5f, 1.5f); - - return p; - } + protected override FallingParticle CreateParticle() => + new FallingParticle + { + Velocity = new Vector2( + RNG.NextSingle(-MaxVelocity, MaxVelocity), + RNG.NextSingle(-MaxVelocity, MaxVelocity) + ), + Duration = RNG.NextSingle(lifetime), + StartAngle = RNG.NextSingle(MathF.PI * 2), + EndAngle = RNG.NextSingle(MathF.PI * 2), + EndScale = RNG.NextSingle(0.5f, 1.5f) + }; } } } diff --git a/osu.Game/Graphics/ParticleSpewer.cs b/osu.Game/Graphics/ParticleSpewer.cs index 8b82dfb7c6..fc119b3ea2 100644 --- a/osu.Game/Graphics/ParticleSpewer.cs +++ b/osu.Game/Graphics/ParticleSpewer.cs @@ -53,7 +53,10 @@ namespace osu.Game.Graphics if (Active.Value && CanSpawnParticles && Time.Current > lastParticleAdded + cooldown) { - particles[currentIndex] = SpawnParticle(); + var newParticle = CreateParticle(); + newParticle.StartTime = (float)Time.Current; + + particles[currentIndex] = newParticle; currentIndex = (currentIndex + 1) % particles.Length; lastParticleAdded = Time.Current; @@ -65,13 +68,7 @@ namespace osu.Game.Graphics /// /// Called each time a new particle should be spawned. /// - protected virtual FallingParticle SpawnParticle() - { - return new FallingParticle - { - StartTime = (float)Time.Current, - }; - } + protected virtual FallingParticle CreateParticle() => new FallingParticle(); protected override DrawNode CreateDrawNode() => new ParticleSpewerDrawNode(this); From 9127a706ac2444daf504ae60fffb0dc38754fd0f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Sep 2021 16:54:22 +0900 Subject: [PATCH 1965/2442] Use `private` for internally used property --- osu.Game/Graphics/ParticleSpewer.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/ParticleSpewer.cs b/osu.Game/Graphics/ParticleSpewer.cs index fc119b3ea2..c022fd4598 100644 --- a/osu.Game/Graphics/ParticleSpewer.cs +++ b/osu.Game/Graphics/ParticleSpewer.cs @@ -26,12 +26,13 @@ namespace osu.Game.Graphics /// public readonly BindableBool Active = new BindableBool(); - public bool HasActiveParticles => Active.Value || (lastParticleAdded + maxLifetime) > Time.Current; - public override bool IsPresent => base.IsPresent && HasActiveParticles; + public override bool IsPresent => base.IsPresent && hasActiveParticles; protected virtual bool CanSpawnParticles => true; protected virtual float ParticleGravity => 0; + private bool hasActiveParticles => Active.Value || (lastParticleAdded + maxLifetime) > Time.Current; + protected ParticleSpewer(Texture texture, int perSecond, double maxLifetime) { Texture = texture; From 51997fa5330e6694220e7fa0e730d00efae58863 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Thu, 16 Sep 2021 16:02:04 +0800 Subject: [PATCH 1966/2442] Add localisation for GlobalActions --- .../Input/Bindings/GlobalActionContainer.cs | 50 ++++ .../GlobalActionKeyBindingStrings.cs | 254 ++++++++++++++++++ .../Settings/Sections/Input/KeyBindingRow.cs | 2 +- 3 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Localisation/GlobalActionKeyBindingStrings.cs diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 0176a00e9d..73d824a5d2 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -7,6 +7,8 @@ using System.Linq; using osu.Framework.Graphics; using osu.Framework.Input; using osu.Framework.Input.Bindings; +using osu.Framework.Localisation; +using osu.Game.Localisation; namespace osu.Game.Input.Bindings { @@ -137,151 +139,199 @@ namespace osu.Game.Input.Bindings public enum GlobalAction { + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleChat))] [Description("Toggle chat overlay")] ToggleChat, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleSocial))] [Description("Toggle social overlay")] ToggleSocial, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ResetInputSettings))] [Description("Reset input settings")] ResetInputSettings, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleToolbar))] [Description("Toggle toolbar")] ToggleToolbar, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleSettings))] [Description("Toggle settings")] ToggleSettings, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleBeatmapListing))] [Description("Toggle beatmap listing")] ToggleBeatmapListing, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.IncreaseVolume))] [Description("Increase volume")] IncreaseVolume, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.DecreaseVolume))] [Description("Decrease volume")] DecreaseVolume, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleMute))] [Description("Toggle mute")] ToggleMute, // In-Game Keybindings + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SkipCutscene))] [Description("Skip cutscene")] SkipCutscene, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.QuickRetry))] [Description("Quick retry (hold)")] QuickRetry, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.TakeScreenshot))] [Description("Take screenshot")] TakeScreenshot, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleGameplayMouseButtons))] [Description("Toggle gameplay mouse buttons")] ToggleGameplayMouseButtons, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.Back))] [Description("Back")] Back, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.IncreaseScrollSpeed))] [Description("Increase scroll speed")] IncreaseScrollSpeed, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.DecreaseScrollSpeed))] [Description("Decrease scroll speed")] DecreaseScrollSpeed, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.Select))] [Description("Select")] Select, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.QuickExit))] [Description("Quick exit (hold)")] QuickExit, // Game-wide beatmap music controller keybindings + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.MusicNext))] [Description("Next track")] MusicNext, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.MusicPrev))] [Description("Previous track")] MusicPrev, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.MusicPlay))] [Description("Play / pause")] MusicPlay, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleNowPlaying))] [Description("Toggle now playing overlay")] ToggleNowPlaying, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SelectPrevious))] [Description("Previous selection")] SelectPrevious, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SelectNext))] [Description("Next selection")] SelectNext, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.Home))] [Description("Home")] Home, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleNotifications))] [Description("Toggle notifications")] ToggleNotifications, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.PauseGameplay))] [Description("Pause gameplay")] PauseGameplay, // Editor + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorSetupMode))] [Description("Setup mode")] EditorSetupMode, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorComposeMode))] [Description("Compose mode")] EditorComposeMode, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorDesignMode))] [Description("Design mode")] EditorDesignMode, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorTimingMode))] [Description("Timing mode")] EditorTimingMode, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.HoldForHUD))] [Description("Hold for HUD")] HoldForHUD, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.RandomSkin))] [Description("Random skin")] RandomSkin, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.TogglePauseReplay))] [Description("Pause / resume replay")] TogglePauseReplay, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleInGameInterface))] [Description("Toggle in-game interface")] ToggleInGameInterface, // Song select keybindings + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleModSelection))] [Description("Toggle Mod Select")] ToggleModSelection, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SelectNextRandom))] [Description("Random")] SelectNextRandom, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SelectPreviousRandom))] [Description("Rewind")] SelectPreviousRandom, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleBeatmapOptions))] [Description("Beatmap Options")] ToggleBeatmapOptions, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorVerifyMode))] [Description("Verify mode")] EditorVerifyMode, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorNudgeLeft))] [Description("Nudge selection left")] EditorNudgeLeft, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorNudgeRight))] [Description("Nudge selection right")] EditorNudgeRight, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleSkinEditor))] [Description("Toggle skin editor")] ToggleSkinEditor, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.PreviousVolumeMeter))] [Description("Previous volume meter")] PreviousVolumeMeter, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.NextVolumeMeter))] [Description("Next volume meter")] NextVolumeMeter, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SeekReplayForward))] [Description("Seek replay forward")] SeekReplayForward, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SeekReplayBackward))] [Description("Seek replay backward")] SeekReplayBackward, + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleChatFocus))] [Description("Toggle chat focus")] ToggleChatFocus } diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs new file mode 100644 index 0000000000..14159f0d34 --- /dev/null +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -0,0 +1,254 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class GlobalActionKeyBindingStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.GlobalActionKeyBinding"; + + /// + /// "Toggle chat overlay" + /// + public static LocalisableString ToggleChat => new TranslatableString(getKey(@"toggle_chat"), @"Toggle chat overlay"); + + /// + /// "Toggle social overlay" + /// + public static LocalisableString ToggleSocial => new TranslatableString(getKey(@"toggle_social"), @"Toggle social overlay"); + + /// + /// "Reset input settings" + /// + public static LocalisableString ResetInputSettings => new TranslatableString(getKey(@"reset_input_settings"), @"Reset input settings"); + + /// + /// "Toggle toolbar" + /// + public static LocalisableString ToggleToolbar => new TranslatableString(getKey(@"toggle_toolbar"), @"Toggle toolbar"); + + /// + /// "Toggle settings" + /// + public static LocalisableString ToggleSettings => new TranslatableString(getKey(@"toggle_settings"), @"Toggle settings"); + + /// + /// "Toggle beatmap listing" + /// + public static LocalisableString ToggleBeatmapListing => new TranslatableString(getKey(@"toggle_beatmap_listing"), @"Toggle beatmap listing"); + + /// + /// "Increase volume" + /// + public static LocalisableString IncreaseVolume => new TranslatableString(getKey(@"increase_volume"), @"Increase volume"); + + /// + /// "Decrease volume" + /// + public static LocalisableString DecreaseVolume => new TranslatableString(getKey(@"decrease_volume"), @"Decrease volume"); + + /// + /// "Toggle mute" + /// + public static LocalisableString ToggleMute => new TranslatableString(getKey(@"toggle_mute"), @"Toggle mute"); + + /// + /// "Skip cutscene" + /// + public static LocalisableString SkipCutscene => new TranslatableString(getKey(@"skip_cutscene"), @"Skip cutscene"); + + /// + /// "Quick retry (hold)" + /// + public static LocalisableString QuickRetry => new TranslatableString(getKey(@"quick_retry"), @"Quick retry (hold)"); + + /// + /// "Take screenshot" + /// + public static LocalisableString TakeScreenshot => new TranslatableString(getKey(@"take_screenshot"), @"Take screenshot"); + + /// + /// "Toggle gameplay mouse buttons" + /// + public static LocalisableString ToggleGameplayMouseButtons => new TranslatableString(getKey(@"toggle_gameplay_mouse_buttons"), @"Toggle gameplay mouse buttons"); + + /// + /// "Back" + /// + public static LocalisableString Back => new TranslatableString(getKey(@"back"), @"Back"); + + /// + /// "Increase scroll speed" + /// + public static LocalisableString IncreaseScrollSpeed => new TranslatableString(getKey(@"increase_scroll_speed"), @"Increase scroll speed"); + + /// + /// "Decrease scroll speed" + /// + public static LocalisableString DecreaseScrollSpeed => new TranslatableString(getKey(@"decrease_scroll_speed"), @"Decrease scroll speed"); + + /// + /// "Select" + /// + public static LocalisableString Select => new TranslatableString(getKey(@"select"), @"Select"); + + /// + /// "Quick exit (hold)" + /// + public static LocalisableString QuickExit => new TranslatableString(getKey(@"quick_exit"), @"Quick exit (hold)"); + + /// + /// "Next track" + /// + public static LocalisableString MusicNext => new TranslatableString(getKey(@"music_next"), @"Next track"); + + /// + /// "Previous track" + /// + public static LocalisableString MusicPrev => new TranslatableString(getKey(@"music_prev"), @"Previous track"); + + /// + /// "Play / pause" + /// + public static LocalisableString MusicPlay => new TranslatableString(getKey(@"music_play"), @"Play / pause"); + + /// + /// "Toggle now playing overlay" + /// + public static LocalisableString ToggleNowPlaying => new TranslatableString(getKey(@"toggle_now_playing"), @"Toggle now playing overlay"); + + /// + /// "Previous selection" + /// + public static LocalisableString SelectPrevious => new TranslatableString(getKey(@"select_previous"), @"Previous selection"); + + /// + /// "Next selection" + /// + public static LocalisableString SelectNext => new TranslatableString(getKey(@"select_next"), @"Next selection"); + + /// + /// "Home" + /// + public static LocalisableString Home => new TranslatableString(getKey(@"home"), @"Home"); + + /// + /// "Toggle notifications" + /// + public static LocalisableString ToggleNotifications => new TranslatableString(getKey(@"toggle_notifications"), @"Toggle notifications"); + + /// + /// "Pause gameplay" + /// + public static LocalisableString PauseGameplay => new TranslatableString(getKey(@"pause_gameplay"), @"Pause gameplay"); + + /// + /// "Setup mode" + /// + public static LocalisableString EditorSetupMode => new TranslatableString(getKey(@"editor_setup_mode"), @"Setup mode"); + + /// + /// "Compose mode" + /// + public static LocalisableString EditorComposeMode => new TranslatableString(getKey(@"editor_compose_mode"), @"Compose mode"); + + /// + /// "Design mode" + /// + public static LocalisableString EditorDesignMode => new TranslatableString(getKey(@"editor_design_mode"), @"Design mode"); + + /// + /// "Timing mode" + /// + public static LocalisableString EditorTimingMode => new TranslatableString(getKey(@"editor_timing_mode"), @"Timing mode"); + + /// + /// "Hold for HUD" + /// + public static LocalisableString HoldForHUD => new TranslatableString(getKey(@"hold_for_hud"), @"Hold for HUD"); + + /// + /// "Random skin" + /// + public static LocalisableString RandomSkin => new TranslatableString(getKey(@"random_skin"), @"Random skin"); + + /// + /// "Pause / resume replay" + /// + public static LocalisableString TogglePauseReplay => new TranslatableString(getKey(@"toggle_pause_replay"), @"Pause / resume replay"); + + /// + /// "Toggle in-game interface" + /// + public static LocalisableString ToggleInGameInterface => new TranslatableString(getKey(@"toggle_in_game_interface"), @"Toggle in-game interface"); + + /// + /// "Toggle Mod Select" + /// + public static LocalisableString ToggleModSelection => new TranslatableString(getKey(@"toggle_mod_selection"), @"Toggle Mod Select"); + + /// + /// "Random" + /// + public static LocalisableString SelectNextRandom => new TranslatableString(getKey(@"select_next_random"), @"Random"); + + /// + /// "Rewind" + /// + public static LocalisableString SelectPreviousRandom => new TranslatableString(getKey(@"select_previous_random"), @"Rewind"); + + /// + /// "Beatmap Options" + /// + public static LocalisableString ToggleBeatmapOptions => new TranslatableString(getKey(@"toggle_beatmap_options"), @"Beatmap Options"); + + /// + /// "Verify mode" + /// + public static LocalisableString EditorVerifyMode => new TranslatableString(getKey(@"editor_verify_mode"), @"Verify mode"); + + /// + /// "Nudge selection left" + /// + public static LocalisableString EditorNudgeLeft => new TranslatableString(getKey(@"editor_nudge_left"), @"Nudge selection left"); + + /// + /// "Nudge selection right" + /// + public static LocalisableString EditorNudgeRight => new TranslatableString(getKey(@"editor_nudge_right"), @"Nudge selection right"); + + /// + /// "Toggle skin editor" + /// + public static LocalisableString ToggleSkinEditor => new TranslatableString(getKey(@"toggle_skin_editor"), @"Toggle skin editor"); + + /// + /// "Previous volume meter" + /// + public static LocalisableString PreviousVolumeMeter => new TranslatableString(getKey(@"previous_volume_meter"), @"Previous volume meter"); + + /// + /// "Next volume meter" + /// + public static LocalisableString NextVolumeMeter => new TranslatableString(getKey(@"next_volume_meter"), @"Next volume meter"); + + /// + /// "Seek replay forward" + /// + public static LocalisableString SeekReplayForward => new TranslatableString(getKey(@"seek_replay_forward"), @"Seek replay forward"); + + /// + /// "Seek replay backward" + /// + public static LocalisableString SeekReplayBackward => new TranslatableString(getKey(@"seek_replay_backward"), @"Seek replay backward"); + + /// + /// "Toggle chat focus" + /// + public static LocalisableString ToggleChatFocus => new TranslatableString(getKey(@"toggle_chat_focus"), @"Toggle chat focus"); + + private static string getKey(string key) => $"{prefix}:{key}"; + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs index c38c516f21..85d88c96f8 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs @@ -115,7 +115,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input }, text = new OsuSpriteText { - Text = action.GetDescription(), + Text = action.GetLocalisableDescription(), Margin = new MarginPadding(padding), }, buttons = new FillFlowContainer From 18e7d86dd4cd60fd81aeff9fd5b7b42723ebd63e Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Thu, 16 Sep 2021 17:08:19 +0800 Subject: [PATCH 1967/2442] Resolve test failure after localizing --- osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index 57ba051214..168d9fafcf 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -234,7 +234,7 @@ namespace osu.Game.Tests.Visual.Settings { AddAssert($"Check {name} is bound to {keyName}", () => { - var firstRow = panel.ChildrenOfType().First(r => r.ChildrenOfType().Any(s => s.Text == name)); + var firstRow = panel.ChildrenOfType().First(r => r.ChildrenOfType().Any(s => s.Text.ToString() == name)); var firstButton = firstRow.ChildrenOfType().First(); return firstButton.Text.Text == keyName; @@ -247,7 +247,7 @@ namespace osu.Game.Tests.Visual.Settings AddStep($"Scroll to {name}", () => { - var firstRow = panel.ChildrenOfType().First(r => r.ChildrenOfType().Any(s => s.Text == name)); + var firstRow = panel.ChildrenOfType().First(r => r.ChildrenOfType().Any(s => s.Text.ToString() == name)); firstButton = firstRow.ChildrenOfType().First(); panel.ChildrenOfType().First().ScrollTo(firstButton); From e323f10cd5334f19c9c2578f0e7ea5bd3f262373 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Thu, 16 Sep 2021 17:10:29 +0800 Subject: [PATCH 1968/2442] Remove unused [Description] --- .../Input/Bindings/GlobalActionContainer.cs | 48 ------------------- 1 file changed, 48 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 73d824a5d2..cfb377f74a 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -140,199 +140,151 @@ namespace osu.Game.Input.Bindings public enum GlobalAction { [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleChat))] - [Description("Toggle chat overlay")] ToggleChat, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleSocial))] - [Description("Toggle social overlay")] ToggleSocial, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ResetInputSettings))] - [Description("Reset input settings")] ResetInputSettings, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleToolbar))] - [Description("Toggle toolbar")] ToggleToolbar, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleSettings))] - [Description("Toggle settings")] ToggleSettings, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleBeatmapListing))] - [Description("Toggle beatmap listing")] ToggleBeatmapListing, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.IncreaseVolume))] - [Description("Increase volume")] IncreaseVolume, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.DecreaseVolume))] - [Description("Decrease volume")] DecreaseVolume, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleMute))] - [Description("Toggle mute")] ToggleMute, // In-Game Keybindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SkipCutscene))] - [Description("Skip cutscene")] SkipCutscene, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.QuickRetry))] - [Description("Quick retry (hold)")] QuickRetry, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.TakeScreenshot))] - [Description("Take screenshot")] TakeScreenshot, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleGameplayMouseButtons))] - [Description("Toggle gameplay mouse buttons")] ToggleGameplayMouseButtons, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.Back))] - [Description("Back")] Back, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.IncreaseScrollSpeed))] - [Description("Increase scroll speed")] IncreaseScrollSpeed, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.DecreaseScrollSpeed))] - [Description("Decrease scroll speed")] DecreaseScrollSpeed, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.Select))] - [Description("Select")] Select, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.QuickExit))] - [Description("Quick exit (hold)")] QuickExit, // Game-wide beatmap music controller keybindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.MusicNext))] - [Description("Next track")] MusicNext, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.MusicPrev))] - [Description("Previous track")] MusicPrev, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.MusicPlay))] - [Description("Play / pause")] MusicPlay, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleNowPlaying))] - [Description("Toggle now playing overlay")] ToggleNowPlaying, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SelectPrevious))] - [Description("Previous selection")] SelectPrevious, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SelectNext))] - [Description("Next selection")] SelectNext, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.Home))] - [Description("Home")] Home, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleNotifications))] - [Description("Toggle notifications")] ToggleNotifications, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.PauseGameplay))] - [Description("Pause gameplay")] PauseGameplay, // Editor [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorSetupMode))] - [Description("Setup mode")] EditorSetupMode, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorComposeMode))] - [Description("Compose mode")] EditorComposeMode, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorDesignMode))] - [Description("Design mode")] EditorDesignMode, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorTimingMode))] - [Description("Timing mode")] EditorTimingMode, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.HoldForHUD))] - [Description("Hold for HUD")] HoldForHUD, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.RandomSkin))] - [Description("Random skin")] RandomSkin, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.TogglePauseReplay))] - [Description("Pause / resume replay")] TogglePauseReplay, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleInGameInterface))] - [Description("Toggle in-game interface")] ToggleInGameInterface, // Song select keybindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleModSelection))] - [Description("Toggle Mod Select")] ToggleModSelection, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SelectNextRandom))] - [Description("Random")] SelectNextRandom, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SelectPreviousRandom))] - [Description("Rewind")] SelectPreviousRandom, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleBeatmapOptions))] - [Description("Beatmap Options")] ToggleBeatmapOptions, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorVerifyMode))] - [Description("Verify mode")] EditorVerifyMode, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorNudgeLeft))] - [Description("Nudge selection left")] EditorNudgeLeft, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorNudgeRight))] - [Description("Nudge selection right")] EditorNudgeRight, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleSkinEditor))] - [Description("Toggle skin editor")] ToggleSkinEditor, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.PreviousVolumeMeter))] - [Description("Previous volume meter")] PreviousVolumeMeter, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.NextVolumeMeter))] - [Description("Next volume meter")] NextVolumeMeter, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SeekReplayForward))] - [Description("Seek replay forward")] SeekReplayForward, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SeekReplayBackward))] - [Description("Seek replay backward")] SeekReplayBackward, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleChatFocus))] - [Description("Toggle chat focus")] ToggleChatFocus } } From f9d5abff8a8d3b6752343c185eb7767a3944a532 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 16 Sep 2021 18:26:12 +0900 Subject: [PATCH 1969/2442] Update with keybinding changes --- .../UI/PippidonCharacter.cs | 7 ++++--- osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs | 4 ++-- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 9 +++++---- .../Objects/Drawables/DrawableHoldNote.cs | 9 +++++---- .../Objects/Drawables/DrawableHoldNoteHead.cs | 5 +++-- .../Objects/Drawables/DrawableHoldNoteTail.cs | 5 +++-- .../Objects/Drawables/DrawableNote.cs | 7 ++++--- .../Skinning/Legacy/LegacyColumnBackground.cs | 9 +++++---- .../Skinning/Legacy/LegacyKeyArea.cs | 9 +++++---- osu.Game.Rulesets.Mania/UI/Column.cs | 7 ++++--- .../UI/Components/ColumnBackground.cs | 9 +++++---- .../UI/Components/DefaultColumnBackground.cs | 9 +++++---- .../UI/Components/DefaultKeyArea.cs | 9 +++++---- .../Components/PathControlPointVisualiser.cs | 6 +++--- .../Objects/Drawables/DrawableHitCircle.cs | 9 +++++---- .../UI/Cursor/OsuCursorContainer.cs | 9 +++++---- osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs | 6 +++--- .../DrawableTestHit.cs | 3 ++- .../DrawableTestStrongHit.cs | 3 ++- .../Objects/Drawables/DrawableDrumRoll.cs | 5 +++-- .../Objects/Drawables/DrawableDrumRollTick.cs | 7 ++++--- .../Objects/Drawables/DrawableHit.cs | 17 +++++++++-------- .../Objects/Drawables/DrawableSwell.cs | 5 +++-- .../Objects/Drawables/DrawableSwellTick.cs | 3 ++- .../Objects/Drawables/DrawableTaikoHitObject.cs | 5 +++-- .../Skinning/Legacy/LegacyInputDrum.cs | 9 +++++---- osu.Game.Rulesets.Taiko/UI/InputDrum.cs | 9 +++++---- .../Visual/Gameplay/TestSceneKeyBindings.cs | 7 ++++--- .../Visual/Gameplay/TestSceneReplayRecorder.cs | 4 ++-- .../Visual/Gameplay/TestSceneReplayRecording.cs | 4 ++-- .../Gameplay/TestSceneSpectatorPlayback.cs | 4 ++-- .../Containers/OsuFocusedOverlayContainer.cs | 6 +++--- osu.Game/Graphics/ScreenshotManager.cs | 7 ++++--- osu.Game/Graphics/UserInterface/BackButton.cs | 7 ++++--- .../Graphics/UserInterface/FocusedTextBox.cs | 6 +++--- .../Graphics/UserInterface/SearchTextBox.cs | 6 +++--- osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs | 7 ++++--- osu.Game/Input/IdleTracker.cs | 8 ++++---- osu.Game/OsuGame.cs | 7 ++++--- .../BeatmapListingSearchControl.cs | 4 ++-- osu.Game/Overlays/ChangelogOverlay.cs | 5 +++-- osu.Game/Overlays/ChatOverlay.cs | 6 +++--- osu.Game/Overlays/DialogOverlay.cs | 7 ++++--- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 +- .../Overlays/Music/MusicKeyBindingHandler.cs | 15 ++++++++------- osu.Game/Overlays/Toolbar/Toolbar.cs | 6 +++--- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 6 +++--- .../Overlays/Volume/VolumeControlReceptor.cs | 14 +++++++------- osu.Game/Overlays/Volume/VolumeMeter.cs | 6 +++--- osu.Game/Rulesets/UI/ReplayRecorder.cs | 8 ++++---- osu.Game/Rulesets/UI/RulesetInputManager.cs | 6 +++--- .../UI/Scrolling/DrawableScrollingRuleset.cs | 7 ++++--- .../Compose/Components/BlueprintContainer.cs | 6 +++--- .../Edit/Compose/Components/SelectionHandler.cs | 6 +++--- .../Timeline/TimelineSelectionHandler.cs | 7 ++++--- osu.Game/Screens/Edit/Compose/ComposeScreen.cs | 7 ++++--- osu.Game/Screens/Edit/Editor.cs | 12 ++++++------ osu.Game/Screens/Menu/ButtonSystem.cs | 6 +++--- osu.Game/Screens/Menu/ExitConfirmOverlay.cs | 9 +++++---- .../Lounge/Components/RoomsContainer.cs | 14 +++++++------- .../OnlinePlay/Lounge/DrawableLoungeRoom.cs | 6 +++--- .../Match/Components/CreateRoomButton.cs | 7 ++++--- .../Match/Components/RoomSettingsOverlay.cs | 7 ++++--- .../Multiplayer/GameplayChatDisplay.cs | 6 +++--- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 6 +++--- osu.Game/Screens/Play/HUD/HoldForMenuButton.cs | 8 ++++---- osu.Game/Screens/Play/HUDOverlay.cs | 9 +++++---- osu.Game/Screens/Play/HotkeyExitOverlay.cs | 9 +++++---- osu.Game/Screens/Play/HotkeyRetryOverlay.cs | 9 +++++---- osu.Game/Screens/Play/ReplayPlayer.cs | 9 +++++---- osu.Game/Screens/Play/SkipOverlay.cs | 6 +++--- osu.Game/Screens/Ranking/ResultsScreen.cs | 7 ++++--- osu.Game/Screens/Select/BeatmapCarousel.cs | 14 +++++++------- osu.Game/Screens/Select/FooterButton.cs | 6 +++--- osu.Game/Screens/Select/FooterButtonRandom.cs | 11 ++++++----- osu.Game/Screens/Select/SongSelect.cs | 6 +++--- osu.Game/Skinning/Editor/SkinEditorOverlay.cs | 7 ++++--- 77 files changed, 302 insertions(+), 257 deletions(-) diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/UI/PippidonCharacter.cs b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/UI/PippidonCharacter.cs index dd0a20f1b4..98dba622d0 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/UI/PippidonCharacter.cs +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/UI/PippidonCharacter.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics.Containers; using osuTK; @@ -61,9 +62,9 @@ namespace osu.Game.Rulesets.Pippidon.UI } } - public bool OnPressed(PippidonAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case PippidonAction.MoveUp: changeLane(-1); @@ -78,7 +79,7 @@ namespace osu.Game.Rulesets.Pippidon.UI } } - public void OnReleased(PippidonAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs index 73b60f51a4..d0a94767d1 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs @@ -44,9 +44,9 @@ namespace osu.Game.Rulesets.Catch.Mods } // disable keyboard controls - public bool OnPressed(CatchAction action) => true; + public bool OnPressed(KeyBindingPressEvent e) => true; - public void OnReleased(CatchAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index b30c3d82a4..604e878782 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Rulesets.Catch.Judgements; using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Catch.Replays; @@ -144,9 +145,9 @@ namespace osu.Game.Rulesets.Catch.UI Catcher.VisualDirection = Direction.Left; } - public bool OnPressed(CatchAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case CatchAction.MoveLeft: currentDirection--; @@ -164,9 +165,9 @@ namespace osu.Game.Rulesets.Catch.UI return false; } - public void OnReleased(CatchAction action) + public void OnReleased(KeyBindingReleaseEvent e) { - switch (action) + switch (e.Action) { case CatchAction.MoveLeft: currentDirection++; diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 2923a2af2f..4e9781f336 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -7,6 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Rulesets.Mania.Skinning.Default; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; @@ -253,12 +254,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables HoldBrokenTime = Time.Current; } - public bool OnPressed(ManiaAction action) + public bool OnPressed(KeyBindingPressEvent e) { if (AllJudged) return false; - if (action != Action.Value) + if (e.Action != Action.Value) return false; // do not run any of this logic when rewinding, as it inverts order of presses/releases. @@ -288,12 +289,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables isHitting.Value = true; } - public void OnReleased(ManiaAction action) + public void OnReleased(KeyBindingReleaseEvent e) { if (AllJudged) return; - if (action != Action.Value) + if (e.Action != Action.Value) return; // do not run any of this logic when rewinding, as it inverts order of presses/releases. diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs index 8458345998..6722ad8ab8 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; +using osu.Framework.Input.Events; using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Mania.Objects.Drawables @@ -43,9 +44,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables // it will be hidden along with its parenting hold note when required. } - public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note + public override bool OnPressed(KeyBindingPressEvent e) => false; // Handled by the hold note - public override void OnReleased(ManiaAction action) + public override void OnReleased(KeyBindingReleaseEvent e) { } } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs index 18aa3f66d4..803685363c 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using osu.Framework.Graphics; +using osu.Framework.Input.Events; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mania.Objects.Drawables @@ -68,9 +69,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables }); } - public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note + public override bool OnPressed(KeyBindingPressEvent e) => false; // Handled by the hold note - public override void OnReleased(ManiaAction action) + public override void OnReleased(KeyBindingReleaseEvent e) { } } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index d53c28868d..51727908c9 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Rulesets.Mania.Configuration; @@ -97,9 +98,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables ApplyResult(r => r.Type = result); } - public virtual bool OnPressed(ManiaAction action) + public virtual bool OnPressed(KeyBindingPressEvent e) { - if (action != Action.Value) + if (e.Action != Action.Value) return false; if (CheckHittable?.Invoke(this, Time.Current) == false) @@ -108,7 +109,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables return UpdateResult(true); } - public virtual void OnReleased(ManiaAction action) + public virtual void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyColumnBackground.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyColumnBackground.cs index 661e7f66f4..54ddcbd5fe 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyColumnBackground.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyColumnBackground.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Skinning; using osuTK; @@ -76,9 +77,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy } } - public bool OnPressed(ManiaAction action) + public bool OnPressed(KeyBindingPressEvent e) { - if (action == Column.Action.Value) + if (e.Action == Column.Action.Value) { light.FadeIn(); light.ScaleTo(Vector2.One); @@ -87,12 +88,12 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy return false; } - public void OnReleased(ManiaAction action) + public void OnReleased(KeyBindingReleaseEvent e) { // Todo: Should be 400 * 100 / CurrentBPM const double animation_length = 250; - if (action == Column.Action.Value) + if (e.Action == Column.Action.Value) { light.FadeTo(0, animation_length); light.ScaleTo(new Vector2(1, 0), animation_length); diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyKeyArea.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyKeyArea.cs index 10319a7d4d..9c339345c4 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyKeyArea.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyKeyArea.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Skinning; @@ -86,9 +87,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy } } - public bool OnPressed(ManiaAction action) + public bool OnPressed(KeyBindingPressEvent e) { - if (action == column.Action.Value) + if (e.Action == column.Action.Value) { upSprite.FadeTo(0); downSprite.FadeTo(1); @@ -97,9 +98,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy return false; } - public void OnReleased(ManiaAction action) + public void OnReleased(KeyBindingReleaseEvent e) { - if (action == column.Action.Value) + if (e.Action == column.Action.Value) { upSprite.Delay(LegacyHitExplosion.FADE_IN_DURATION).FadeTo(1); downSprite.Delay(LegacyHitExplosion.FADE_IN_DURATION).FadeTo(0); diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index f5e30efd91..9d060944cd 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -10,6 +10,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Pooling; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.UI.Components; using osu.Game.Rulesets.UI.Scrolling; @@ -122,16 +123,16 @@ namespace osu.Game.Rulesets.Mania.UI HitObjectArea.Explosions.Add(hitExplosionPool.Get(e => e.Apply(result))); } - public bool OnPressed(ManiaAction action) + public bool OnPressed(KeyBindingPressEvent e) { - if (action != Action.Value) + if (e.Action != Action.Value) return false; sampleTriggerSource.Play(); return true; } - public void OnReleased(ManiaAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs index 75cc351310..77ddc6fbbf 100644 --- a/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs +++ b/osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Rulesets.UI.Scrolling; using osuTK.Graphics; @@ -91,16 +92,16 @@ namespace osu.Game.Rulesets.Mania.UI.Components direction.Value == ScrollingDirection.Up ? dimPoint : brightPoint); } - public bool OnPressed(ManiaAction action) + public bool OnPressed(KeyBindingPressEvent e) { - if (action == this.action.Value) + if (e.Action == action.Value) backgroundOverlay.FadeTo(1, 50, Easing.OutQuint).Then().FadeTo(0.5f, 250, Easing.OutQuint); return false; } - public void OnReleased(ManiaAction action) + public void OnReleased(KeyBindingReleaseEvent e) { - if (action == this.action.Value) + if (e.Action == action.Value) backgroundOverlay.FadeTo(0, 250, Easing.OutQuint); } } diff --git a/osu.Game.Rulesets.Mania/UI/Components/DefaultColumnBackground.cs b/osu.Game.Rulesets.Mania/UI/Components/DefaultColumnBackground.cs index 4b4bc157d5..807f6a77d9 100644 --- a/osu.Game.Rulesets.Mania/UI/Components/DefaultColumnBackground.cs +++ b/osu.Game.Rulesets.Mania/UI/Components/DefaultColumnBackground.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Rulesets.UI.Scrolling; using osuTK.Graphics; @@ -74,16 +75,16 @@ namespace osu.Game.Rulesets.Mania.UI.Components } } - public bool OnPressed(ManiaAction action) + public bool OnPressed(KeyBindingPressEvent e) { - if (action == column.Action.Value) + if (e.Action == column.Action.Value) backgroundOverlay.FadeTo(1, 50, Easing.OutQuint).Then().FadeTo(0.5f, 250, Easing.OutQuint); return false; } - public void OnReleased(ManiaAction action) + public void OnReleased(KeyBindingReleaseEvent e) { - if (action == column.Action.Value) + if (e.Action == column.Action.Value) backgroundOverlay.FadeTo(0, 250, Easing.OutQuint); } } diff --git a/osu.Game.Rulesets.Mania/UI/Components/DefaultKeyArea.cs b/osu.Game.Rulesets.Mania/UI/Components/DefaultKeyArea.cs index 47cb9bd45a..267ed1f5f4 100644 --- a/osu.Game.Rulesets.Mania/UI/Components/DefaultKeyArea.cs +++ b/osu.Game.Rulesets.Mania/UI/Components/DefaultKeyArea.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Rulesets.UI.Scrolling; using osuTK; using osuTK.Graphics; @@ -101,16 +102,16 @@ namespace osu.Game.Rulesets.Mania.UI.Components } } - public bool OnPressed(ManiaAction action) + public bool OnPressed(KeyBindingPressEvent e) { - if (action == column.Action.Value) + if (e.Action == column.Action.Value) keyIcon.ScaleTo(1.4f, 50, Easing.OutQuint).Then().ScaleTo(1.3f, 250, Easing.OutQuint); return false; } - public void OnReleased(ManiaAction action) + public void OnReleased(KeyBindingReleaseEvent e) { - if (action == column.Action.Value) + if (e.Action == column.Action.Value) keyIcon.ScaleTo(1f, 125, Easing.OutQuint); } } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 6269a41350..1be9b5bf2e 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -127,9 +127,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components return false; } - public bool OnPressed(PlatformAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case PlatformAction.Delete: return DeleteSelected(); @@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components return false; } - public void OnReleased(PlatformAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 46fc8f99b2..b6ac62d370 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Judgements; @@ -228,15 +229,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables CornerExponent = 2; } - public bool OnPressed(OsuAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case OsuAction.LeftButton: case OsuAction.RightButton: if (IsHovered && (Hit?.Invoke() ?? false)) { - HitAction = action; + HitAction = e.Action; return true; } @@ -246,7 +247,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables return false; } - public void OnReleased(OsuAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } } diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs index 5812e8cf75..8c76f8a128 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Textures; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets.Osu.Configuration; @@ -115,9 +116,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor (ActiveCursor as OsuCursor)?.Contract(); } - public bool OnPressed(OsuAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case OsuAction.LeftButton: case OsuAction.RightButton: @@ -129,9 +130,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor return false; } - public void OnReleased(OsuAction action) + public void OnReleased(KeyBindingReleaseEvent e) { - switch (action) + switch (e.Action) { case OsuAction.LeftButton: case OsuAction.RightButton: diff --git a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs index 27d48d1296..4d4340936d 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs @@ -89,9 +89,9 @@ namespace osu.Game.Rulesets.Osu.UI base.OnHoverLost(e); } - public bool OnPressed(OsuAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case OsuAction.LeftButton: case OsuAction.RightButton: @@ -106,7 +106,7 @@ namespace osu.Game.Rulesets.Osu.UI return false; } - public void OnReleased(OsuAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game.Rulesets.Taiko.Tests/DrawableTestHit.cs b/osu.Game.Rulesets.Taiko.Tests/DrawableTestHit.cs index f048cad18c..6d4cac0ebe 100644 --- a/osu.Game.Rulesets.Taiko.Tests/DrawableTestHit.cs +++ b/osu.Game.Rulesets.Taiko.Tests/DrawableTestHit.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Scoring; @@ -37,6 +38,6 @@ namespace osu.Game.Rulesets.Taiko.Tests Result.Type = Type; } - public override bool OnPressed(TaikoAction action) => false; + public override bool OnPressed(KeyBindingPressEvent e) => false; } } diff --git a/osu.Game.Rulesets.Taiko.Tests/DrawableTestStrongHit.cs b/osu.Game.Rulesets.Taiko.Tests/DrawableTestStrongHit.cs index 829bcf34a1..ea877c9e17 100644 --- a/osu.Game.Rulesets.Taiko.Tests/DrawableTestStrongHit.cs +++ b/osu.Game.Rulesets.Taiko.Tests/DrawableTestStrongHit.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; +using osu.Framework.Input.Events; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects.Drawables; @@ -30,6 +31,6 @@ namespace osu.Game.Rulesets.Taiko.Tests nestedStrongHit.Result.Type = hitBoth ? Type : HitResult.Miss; } - public override bool OnPressed(TaikoAction action) => false; + public override bool OnPressed(KeyBindingPressEvent e) => false; } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index d066abf767..521189d36c 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -11,6 +11,7 @@ using osu.Game.Rulesets.Objects.Drawables; using osuTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Events; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; @@ -112,7 +113,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override SkinnableDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.DrumRollBody), _ => new ElongatedCirclePiece()); - public override bool OnPressed(TaikoAction action) => false; + public override bool OnPressed(KeyBindingPressEvent e) => false; private void onNewResult(DrawableHitObject obj, JudgementResult result) { @@ -196,7 +197,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult); } - public override bool OnPressed(TaikoAction action) => false; + public override bool OnPressed(KeyBindingPressEvent e) => false; } } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index 0df45c424d..dc2ed200a1 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -4,6 +4,7 @@ using System; using JetBrains.Annotations; using osu.Framework.Graphics; +using osu.Framework.Input.Events; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Taiko.Skinning.Default; using osu.Game.Skinning; @@ -61,9 +62,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } } - public override bool OnPressed(TaikoAction action) + public override bool OnPressed(KeyBindingPressEvent e) { - JudgementType = action == TaikoAction.LeftRim || action == TaikoAction.RightRim ? HitType.Rim : HitType.Centre; + JudgementType = e.Action == TaikoAction.LeftRim || e.Action == TaikoAction.RightRim ? HitType.Rim : HitType.Centre; return UpdateResult(true); } @@ -91,7 +92,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult); } - public override bool OnPressed(TaikoAction action) => false; + public override bool OnPressed(KeyBindingPressEvent e) => false; } } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 1e9fc187eb..97adc5f197 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -8,6 +8,7 @@ using System.Linq; using JetBrains.Annotations; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Input.Events; using osu.Game.Audio; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; @@ -145,19 +146,19 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables ApplyResult(r => r.Type = result); } - public override bool OnPressed(TaikoAction action) + public override bool OnPressed(KeyBindingPressEvent e) { if (pressHandledThisFrame) return true; if (Judged) return false; - validActionPressed = HitActions.Contains(action); + validActionPressed = HitActions.Contains(e.Action); // Only count this as handled if the new judgement is a hit var result = UpdateResult(true); if (IsHit) - HitAction = action; + HitAction = e.Action; // Regardless of whether we've hit or not, any secondary key presses in the same frame should be discarded // E.g. hitting a non-strong centre as a strong should not fall through and perform a hit on the next note @@ -165,11 +166,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables return result; } - public override void OnReleased(TaikoAction action) + public override void OnReleased(KeyBindingReleaseEvent e) { - if (action == HitAction) + if (e.Action == HitAction) HitAction = null; - base.OnReleased(action); + base.OnReleased(e); } protected override void Update() @@ -265,7 +266,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables ApplyResult(r => r.Type = r.Judgement.MaxResult); } - public override bool OnPressed(TaikoAction action) + public override bool OnPressed(KeyBindingPressEvent e) { // Don't process actions until the main hitobject is hit if (!ParentHitObject.IsHit) @@ -276,7 +277,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables return false; // Don't handle invalid hit action presses - if (!ParentHitObject.HitActions.Contains(action)) + if (!ParentHitObject.HitActions.Contains(e.Action)) return false; return UpdateResult(true); diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index 888f47d341..2d19296d06 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -12,6 +12,7 @@ using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osuTK.Graphics; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Skinning.Default; @@ -266,13 +267,13 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables private bool? lastWasCentre; - public override bool OnPressed(TaikoAction action) + public override bool OnPressed(KeyBindingPressEvent e) { // Don't handle keys before the swell starts if (Time.Current < HitObject.StartTime) return false; - var isCentre = action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre; + var isCentre = e.Action == TaikoAction.LeftCentre || e.Action == TaikoAction.RightCentre; // Ensure alternating centre and rim hits if (lastWasCentre == isCentre) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs index 47fc7e5ab3..d4ea9ed29f 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs @@ -3,6 +3,7 @@ using JetBrains.Annotations; using osu.Framework.Graphics; +using osu.Framework.Input.Events; using osu.Game.Rulesets.Taiko.Skinning.Default; using osu.Game.Skinning; @@ -34,7 +35,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { } - public override bool OnPressed(TaikoAction action) => false; + public override bool OnPressed(KeyBindingPressEvent e) => false; protected override SkinnableDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.DrumRollTick), _ => new TickPiece()); diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index 6a8d8a611c..eb64ba72f2 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Audio; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Skinning; @@ -76,9 +77,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables /// public Drawable CreateProxiedContent() => proxiedContent.CreateProxy(); - public abstract bool OnPressed(TaikoAction action); + public abstract bool OnPressed(KeyBindingPressEvent e); - public virtual void OnReleased(TaikoAction action) + public virtual void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs index 9d35093591..86be40dea8 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.UI; using osu.Game.Skinning; @@ -141,16 +142,16 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy Centre.Texture = skin.GetTexture(@"taiko-drum-inner"); } - public bool OnPressed(TaikoAction action) + public bool OnPressed(KeyBindingPressEvent e) { Drawable target = null; - if (action == CentreAction) + if (e.Action == CentreAction) { target = Centre; sampleTriggerSource.Play(HitType.Centre); } - else if (action == RimAction) + else if (e.Action == RimAction) { target = Rim; sampleTriggerSource.Play(HitType.Rim); @@ -173,7 +174,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy return false; } - public void OnReleased(TaikoAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } } diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs index ddfaf64549..861b800038 100644 --- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs +++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.UI; @@ -151,19 +152,19 @@ namespace osu.Game.Rulesets.Taiko.UI [Resolved(canBeNull: true)] private GameplayClock gameplayClock { get; set; } - public bool OnPressed(TaikoAction action) + public bool OnPressed(KeyBindingPressEvent e) { Drawable target = null; Drawable back = null; - if (action == CentreAction) + if (e.Action == CentreAction) { target = centreHit; back = centre; sampleTriggerSource.Play(HitType.Centre); } - else if (action == RimAction) + else if (e.Action == RimAction) { target = rimHit; back = rim; @@ -195,7 +196,7 @@ namespace osu.Game.Rulesets.Taiko.UI return false; } - public void OnReleased(TaikoAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyBindings.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyBindings.cs index 6de85499c5..0a39d94027 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyBindings.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyBindings.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Input.Bindings; @@ -80,13 +81,13 @@ namespace osu.Game.Tests.Visual.Gameplay { public bool ReceivedAction; - public bool OnPressed(TestAction action) + public bool OnPressed(KeyBindingPressEvent e) { - ReceivedAction = action == TestAction.Down; + ReceivedAction = e.Action == TestAction.Down; return true; } - public void OnReleased(TestAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs index 2ce0213ea2..0a3fedaf8e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs @@ -226,13 +226,13 @@ namespace osu.Game.Tests.Visual.Gameplay return base.OnMouseMove(e); } - public bool OnPressed(TestAction action) + public bool OnPressed(KeyBindingPressEvent e) { box.Colour = Color4.White; return true; } - public void OnReleased(TestAction action) + public void OnReleased(KeyBindingReleaseEvent e) { box.Colour = Color4.Black; } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs index 85a2870bf9..dfd5e2dc58 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs @@ -159,13 +159,13 @@ namespace osu.Game.Tests.Visual.Gameplay return base.OnMouseMove(e); } - public bool OnPressed(TestAction action) + public bool OnPressed(KeyBindingPressEvent e) { box.Colour = Color4.White; return true; } - public void OnReleased(TestAction action) + public void OnReleased(KeyBindingReleaseEvent e) { box.Colour = Color4.Black; } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs index d9d0dc6c58..6f5f774758 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs @@ -279,13 +279,13 @@ namespace osu.Game.Tests.Visual.Gameplay return base.OnMouseMove(e); } - public bool OnPressed(TestAction action) + public bool OnPressed(KeyBindingPressEvent e) { box.Colour = Color4.White; return true; } - public void OnReleased(TestAction action) + public void OnReleased(KeyBindingReleaseEvent e) { box.Colour = Color4.Black; } diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index b9b098df80..16ec7ab838 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -88,9 +88,9 @@ namespace osu.Game.Graphics.Containers base.OnMouseUp(e); } - public virtual bool OnPressed(GlobalAction action) + public virtual bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case GlobalAction.Back: Hide(); @@ -103,7 +103,7 @@ namespace osu.Game.Graphics.Containers return false; } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index fb7fe4947b..9cd403f409 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -12,6 +12,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Framework.Platform; using osu.Framework.Threading; using osu.Game.Configuration; @@ -57,9 +58,9 @@ namespace osu.Game.Graphics shutter = audio.Samples.Get("UI/shutter"); } - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case GlobalAction.TakeScreenshot: shutter.Play(); @@ -70,7 +71,7 @@ namespace osu.Game.Graphics return false; } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game/Graphics/UserInterface/BackButton.cs b/osu.Game/Graphics/UserInterface/BackButton.cs index 1607762908..c965fbcf45 100644 --- a/osu.Game/Graphics/UserInterface/BackButton.cs +++ b/osu.Game/Graphics/UserInterface/BackButton.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Input.Bindings; namespace osu.Game.Graphics.UserInterface @@ -61,9 +62,9 @@ namespace osu.Game.Graphics.UserInterface { public Action OnBackPressed; - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case GlobalAction.Back: OnBackPressed?.Invoke(); @@ -73,7 +74,7 @@ namespace osu.Game.Graphics.UserInterface return false; } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } } diff --git a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs index 6c8238a1b8..ceea9620c8 100644 --- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs +++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs @@ -70,11 +70,11 @@ namespace osu.Game.Graphics.UserInterface return base.OnKeyDown(e); } - public virtual bool OnPressed(GlobalAction action) + public virtual bool OnPressed(KeyBindingPressEvent e) { if (!HasFocus) return false; - if (action == GlobalAction.Back) + if (e.Action == GlobalAction.Back) { if (Text.Length > 0) { @@ -86,7 +86,7 @@ namespace osu.Game.Graphics.UserInterface return false; } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game/Graphics/UserInterface/SearchTextBox.cs b/osu.Game/Graphics/UserInterface/SearchTextBox.cs index 4a91741ce6..6937782be6 100644 --- a/osu.Game/Graphics/UserInterface/SearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/SearchTextBox.cs @@ -30,9 +30,9 @@ namespace osu.Game.Graphics.UserInterface PlaceholderText = "type to search"; } - public override bool OnPressed(PlatformAction action) + public override bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case PlatformAction.MoveBackwardLine: case PlatformAction.MoveForwardLine: @@ -43,7 +43,7 @@ namespace osu.Game.Graphics.UserInterface return false; } - return base.OnPressed(action); + return base.OnPressed(e); } protected override bool OnKeyDown(KeyDownEvent e) diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs b/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs index 2cb696be0a..226c39c030 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Input.Bindings; using osu.Game.Overlays; using osuTK; @@ -55,12 +56,12 @@ namespace osu.Game.Graphics.UserInterfaceV2 this.FadeOut(fade_duration, Easing.OutQuint); } - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { if (State.Value == Visibility.Hidden) return false; - if (action == GlobalAction.Back) + if (e.Action == GlobalAction.Back) { Hide(); return true; @@ -69,7 +70,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 return false; } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } } diff --git a/osu.Game/Input/IdleTracker.cs b/osu.Game/Input/IdleTracker.cs index f3d531cf6c..bfe21f650a 100644 --- a/osu.Game/Input/IdleTracker.cs +++ b/osu.Game/Input/IdleTracker.cs @@ -55,13 +55,13 @@ namespace osu.Game.Input isIdle.Value = TimeSpentIdle > timeToIdle && AllowIdle; } - public bool OnPressed(PlatformAction action) => updateLastInteractionTime(); + public bool OnPressed(KeyBindingPressEvent e) => updateLastInteractionTime(); - public void OnReleased(PlatformAction action) => updateLastInteractionTime(); + public void OnReleased(KeyBindingReleaseEvent e) => updateLastInteractionTime(); - public bool OnPressed(GlobalAction action) => updateLastInteractionTime(); + public bool OnPressed(KeyBindingPressEvent e) => updateLastInteractionTime(); - public void OnReleased(GlobalAction action) => updateLastInteractionTime(); + public void OnReleased(KeyBindingReleaseEvent e) => updateLastInteractionTime(); protected override bool Handle(UIEvent e) { diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index ce84c4bd2a..0e55a65aec 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -27,6 +27,7 @@ using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics.Sprites; using osu.Framework.Input; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Collections; @@ -968,11 +969,11 @@ namespace osu.Game return component; } - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { if (introScreen == null) return false; - switch (action) + switch (e.Action) { case GlobalAction.ResetInputSettings: Host.ResetInputHandlers(); @@ -1006,7 +1007,7 @@ namespace osu.Game #endregion - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs index 07a0cfb57f..bbde03aa10 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs @@ -186,9 +186,9 @@ namespace osu.Game.Overlays.BeatmapListing return true; } - public override bool OnPressed(GlobalAction action) + public override bool OnPressed(KeyBindingPressEvent e) { - if (!base.OnPressed(action)) + if (!base.OnPressed(e)) return false; TextChanged?.Invoke(); diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index a8f2e654d7..ce12e9554d 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -10,6 +10,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Input.Events; using osu.Game.Input.Bindings; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; @@ -91,9 +92,9 @@ namespace osu.Game.Overlays Show(); } - public override bool OnPressed(GlobalAction action) + public override bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case GlobalAction.Back: if (Current.Value == null) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 0445c63eb4..25c5154d4a 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -372,9 +372,9 @@ namespace osu.Game.Overlays return base.OnKeyDown(e); } - public bool OnPressed(PlatformAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case PlatformAction.TabNew: ChannelTabControl.SelectChannelSelectorTab(); @@ -392,7 +392,7 @@ namespace osu.Game.Overlays return false; } - public void OnReleased(PlatformAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index 43ef42a809..d5d31343f2 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -7,6 +7,7 @@ using osu.Game.Overlays.Dialog; using osu.Game.Graphics.Containers; using osu.Game.Input.Bindings; using System.Linq; +using osu.Framework.Input.Events; namespace osu.Game.Overlays { @@ -83,16 +84,16 @@ namespace osu.Game.Overlays this.FadeOut(PopupDialog.EXIT_DURATION, Easing.InSine); } - public override bool OnPressed(GlobalAction action) + public override bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case GlobalAction.Select: CurrentDialog?.Buttons.OfType().FirstOrDefault()?.TriggerClick(); return true; } - return base.OnPressed(action); + return base.OnPressed(e); } } } diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index fdef48d556..ec7e49920c 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -416,7 +416,7 @@ namespace osu.Game.Overlays.Mods return base.OnKeyDown(e); } - public override bool OnPressed(GlobalAction action) => false; // handled by back button + public override bool OnPressed(KeyBindingPressEvent e) => false; // handled by back button private void updateAvailableMods() { diff --git a/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs b/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs index f06e02e5e1..dba4bf926f 100644 --- a/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs +++ b/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Input.Bindings; @@ -26,23 +27,23 @@ namespace osu.Game.Overlays.Music [Resolved(canBeNull: true)] private OnScreenDisplay onScreenDisplay { get; set; } - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { if (beatmap.Disabled) return false; - switch (action) + switch (e.Action) { case GlobalAction.MusicPlay: // use previous state as TogglePause may not update the track's state immediately (state update is run on the audio thread see https://github.com/ppy/osu/issues/9880#issuecomment-674668842) bool wasPlaying = musicController.IsPlaying; if (musicController.TogglePause()) - onScreenDisplay?.Display(new MusicActionToast(wasPlaying ? "Pause track" : "Play track", action)); + onScreenDisplay?.Display(new MusicActionToast(wasPlaying ? "Pause track" : "Play track", e.Action)); return true; case GlobalAction.MusicNext: - musicController.NextTrack(() => onScreenDisplay?.Display(new MusicActionToast("Next track", action))); + musicController.NextTrack(() => onScreenDisplay?.Display(new MusicActionToast("Next track", e.Action))); return true; @@ -52,11 +53,11 @@ namespace osu.Game.Overlays.Music switch (res) { case PreviousTrackResult.Restart: - onScreenDisplay?.Display(new MusicActionToast("Restart track", action)); + onScreenDisplay?.Display(new MusicActionToast("Restart track", e.Action)); break; case PreviousTrackResult.Previous: - onScreenDisplay?.Display(new MusicActionToast("Previous track", action)); + onScreenDisplay?.Display(new MusicActionToast("Previous track", e.Action)); break; } }); @@ -67,7 +68,7 @@ namespace osu.Game.Overlays.Music return false; } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 7481cfdbf5..dc0b06b255 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -178,12 +178,12 @@ namespace osu.Game.Overlays.Toolbar this.FadeOut(transition_time, Easing.InQuint); } - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { if (OverlayActivationMode.Value == OverlayActivation.Disabled) return false; - switch (action) + switch (e.Action) { case GlobalAction.ToggleToolbar: hiddenByUser = State.Value == Visibility.Visible; // set before toggling to allow the operation to always succeed. @@ -194,7 +194,7 @@ namespace osu.Game.Overlays.Toolbar return false; } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } } diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index 6da41b2b5f..dd554200ca 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -184,9 +184,9 @@ namespace osu.Game.Overlays.Toolbar tooltipContainer.FadeOut(100); } - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { - if (action == Hotkey) + if (e.Action == Hotkey) { TriggerClick(); return true; @@ -195,7 +195,7 @@ namespace osu.Game.Overlays.Toolbar return false; } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game/Overlays/Volume/VolumeControlReceptor.cs b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs index b24214ff3d..4129b46ce3 100644 --- a/osu.Game/Overlays/Volume/VolumeControlReceptor.cs +++ b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs @@ -19,27 +19,27 @@ namespace osu.Game.Overlays.Volume private ScheduledDelegate keyRepeat; - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case GlobalAction.DecreaseVolume: case GlobalAction.IncreaseVolume: keyRepeat?.Cancel(); - keyRepeat = this.BeginKeyRepeat(Scheduler, () => ActionRequested?.Invoke(action), 150); + keyRepeat = this.BeginKeyRepeat(Scheduler, () => ActionRequested?.Invoke(e.Action), 150); return true; case GlobalAction.ToggleMute: case GlobalAction.NextVolumeMeter: case GlobalAction.PreviousVolumeMeter: - ActionRequested?.Invoke(action); + ActionRequested?.Invoke(e.Action); return true; } return false; } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { keyRepeat?.Cancel(); } @@ -54,7 +54,7 @@ namespace osu.Game.Overlays.Volume return true; } - public bool OnScroll(GlobalAction action, float amount, bool isPrecise) => - ScrollActionRequested?.Invoke(action, amount, isPrecise) ?? false; + public bool OnScroll(KeyBindingScrollEvent e) => + ScrollActionRequested?.Invoke(e.Action, e.ScrollAmount, e.IsPrecise) ?? false; } } diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs index 7249dd77e5..ff28b45ebb 100644 --- a/osu.Game/Overlays/Volume/VolumeMeter.cs +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -365,12 +365,12 @@ namespace osu.Game.Overlays.Volume { } - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { if (!IsHovered) return false; - switch (action) + switch (e.Action) { case GlobalAction.SelectPrevious: State = SelectionState.Selected; @@ -386,7 +386,7 @@ namespace osu.Game.Overlays.Volume return false; } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game/Rulesets/UI/ReplayRecorder.cs b/osu.Game/Rulesets/UI/ReplayRecorder.cs index d18e0f9541..b57c224059 100644 --- a/osu.Game/Rulesets/UI/ReplayRecorder.cs +++ b/osu.Game/Rulesets/UI/ReplayRecorder.cs @@ -70,16 +70,16 @@ namespace osu.Game.Rulesets.UI return base.OnMouseMove(e); } - public bool OnPressed(T action) + public bool OnPressed(KeyBindingPressEvent e) { - pressedActions.Add(action); + pressedActions.Add(e.Action); recordFrame(true); return false; } - public void OnReleased(T action) + public void OnReleased(KeyBindingReleaseEvent e) { - pressedActions.Remove(action); + pressedActions.Remove(e.Action); recordFrame(true); } diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index e6cd2aa3dc..a3f311c7a6 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -152,12 +152,12 @@ namespace osu.Game.Rulesets.UI { } - public bool OnPressed(T action) => Target.Children.OfType>().Any(c => c.OnPressed(action, Clock.Rate >= 0)); + public bool OnPressed(KeyBindingPressEvent e) => Target.Children.OfType>().Any(c => c.OnPressed(e.Action, Clock.Rate >= 0)); - public void OnReleased(T action) + public void OnReleased(KeyBindingReleaseEvent e) { foreach (var c in Target.Children.OfType>()) - c.OnReleased(action, Clock.Rate >= 0); + c.OnReleased(e.Action, Clock.Rate >= 0); } } diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs index f8d5a6c5a9..7b30bb9574 100644 --- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs +++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs @@ -8,6 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Framework.Lists; using osu.Framework.Threading; using osu.Game.Beatmaps; @@ -188,12 +189,12 @@ namespace osu.Game.Rulesets.UI.Scrolling /// The amount to adjust by. Greater than 0 if the scroll speed should be increased, less than 0 if it should be decreased. protected virtual void AdjustScrollSpeed(int amount) => this.TransformBindableTo(TimeRange, TimeRange.Value - amount * time_span_step, 200, Easing.OutQuint); - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { if (!UserScrollSpeedAdjustment) return false; - switch (action) + switch (e.Action) { case GlobalAction.IncreaseScrollSpeed: scheduleScrollSpeedAdjustment(1); @@ -209,7 +210,7 @@ namespace osu.Game.Rulesets.UI.Scrolling private ScheduledDelegate scheduledScrollSpeedAdjustment; - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { scheduledScrollSpeedAdjustment?.Cancel(); scheduledScrollSpeedAdjustment = null; diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index b99dacbd4a..75d4d13f94 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -228,9 +228,9 @@ namespace osu.Game.Screens.Edit.Compose.Components return false; } - public bool OnPressed(PlatformAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case PlatformAction.SelectAll: SelectAll(); @@ -240,7 +240,7 @@ namespace osu.Game.Screens.Edit.Compose.Components return false; } - public void OnReleased(PlatformAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 1d1d95890f..44eb062db8 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -137,9 +137,9 @@ namespace osu.Game.Screens.Edit.Compose.Components /// Whether any items could be reversed. public virtual bool HandleReverse() => false; - public bool OnPressed(PlatformAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case PlatformAction.Delete: DeleteSelected(); @@ -149,7 +149,7 @@ namespace osu.Game.Screens.Edit.Compose.Components return false; } - public void OnReleased(PlatformAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs index 354013a5fd..c43e554b85 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs @@ -4,6 +4,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Input.Bindings; using osu.Game.Rulesets.Objects; using osuTK; @@ -20,9 +21,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline // for now we always allow movement. snapping is provided by the Timeline's "distance" snap implementation public override bool HandleMovement(MoveSelectionEvent moveEvent) => true; - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case GlobalAction.EditorNudgeLeft: nudgeSelection(-1); @@ -36,7 +37,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline return false; } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs index e324f1edeb..926a2ad4e0 100644 --- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs +++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.Extensions; @@ -77,15 +78,15 @@ namespace osu.Game.Screens.Edit.Compose #region Input Handling - public bool OnPressed(PlatformAction action) + public bool OnPressed(KeyBindingPressEvent e) { - if (action == PlatformAction.Copy) + if (e.Action == PlatformAction.Copy) host.GetClipboard()?.SetText(formatSelectionAsString()); return false; } - public void OnReleased(PlatformAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 028662172d..2ff0101dc0 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -347,9 +347,9 @@ namespace osu.Game.Screens.Edit clock.ProcessFrame(); } - public bool OnPressed(PlatformAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case PlatformAction.Cut: Cut(); @@ -379,7 +379,7 @@ namespace osu.Game.Screens.Edit return false; } - public void OnReleased(PlatformAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } @@ -434,9 +434,9 @@ namespace osu.Game.Screens.Edit return true; } - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case GlobalAction.Back: // as we don't want to display the back button, manual handling of exit action is required. @@ -468,7 +468,7 @@ namespace osu.Game.Screens.Edit } } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 6c712e9d5b..5f76176aab 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -218,9 +218,9 @@ namespace osu.Game.Screens.Menu return base.OnKeyDown(e); } - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case GlobalAction.Back: return goBack(); @@ -234,7 +234,7 @@ namespace osu.Game.Screens.Menu } } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game/Screens/Menu/ExitConfirmOverlay.cs b/osu.Game/Screens/Menu/ExitConfirmOverlay.cs index a491283e5f..364da2f887 100644 --- a/osu.Game/Screens/Menu/ExitConfirmOverlay.cs +++ b/osu.Game/Screens/Menu/ExitConfirmOverlay.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Input.Bindings; using osu.Game.Overlays; @@ -18,9 +19,9 @@ namespace osu.Game.Screens.Menu { } - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { - if (action == GlobalAction.Back) + if (e.Action == GlobalAction.Back) { BeginConfirm(); return true; @@ -29,9 +30,9 @@ namespace osu.Game.Screens.Menu return false; } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { - if (action == GlobalAction.Back) + if (e.Action == GlobalAction.Back) { if (!Fired) AbortConfirm(); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index 76cb02199b..907b7e308a 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -141,29 +141,29 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components #region Key selection logic (shared with BeatmapCarousel) - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case GlobalAction.SelectNext: - beginRepeatSelection(() => selectNext(1), action); + beginRepeatSelection(() => selectNext(1), e.Action); return true; case GlobalAction.SelectPrevious: - beginRepeatSelection(() => selectNext(-1), action); + beginRepeatSelection(() => selectNext(-1), e.Action); return true; } return false; } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { - switch (action) + switch (e.Action) { case GlobalAction.SelectNext: case GlobalAction.SelectPrevious: - endRepeatSelection(action); + endRepeatSelection(e.Action); break; } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index 95fa34ab43..d55cccd414 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -132,12 +132,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge }) }; - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { if (SelectedRoom.Value != Room) return false; - switch (action) + switch (e.Action) { case GlobalAction.Select: TriggerClick(); @@ -147,7 +147,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge return false; } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/CreateRoomButton.cs b/osu.Game/Screens/OnlinePlay/Match/Components/CreateRoomButton.cs index 3801463095..53131ab90e 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/CreateRoomButton.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/CreateRoomButton.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Input; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; namespace osu.Game.Screens.OnlinePlay.Match.Components { @@ -16,12 +17,12 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components Triangles.TriangleScale = 1.5f; } - public bool OnPressed(PlatformAction action) + public bool OnPressed(KeyBindingPressEvent e) { if (!Enabled.Value) return false; - switch (action) + switch (e.Action) { case PlatformAction.DocumentNew: // might as well also handle new tab. it's a bit of an undefined flow on this screen. @@ -33,7 +34,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components return false; } - public void OnReleased(PlatformAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } } diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs index 7a8839cdad..a6cdde14f6 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -63,9 +64,9 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components Settings.Delay(TRANSITION_DURATION / 2).FadeOut(TRANSITION_DURATION / 2); } - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case GlobalAction.Select: if (IsLoading) @@ -86,7 +87,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components return false; } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs index 3af72fa25a..af0c50a848 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs @@ -71,9 +71,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer }, true); } - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case GlobalAction.ToggleChatFocus: if (Textbox.HasFocus) @@ -94,7 +94,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer return false; } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index 0efa66bac0..9e3400b09d 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -187,9 +187,9 @@ namespace osu.Game.Screens.Play InternalButtons.Add(button); } - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case GlobalAction.SelectPrevious: InternalButtons.SelectPrevious(); @@ -211,7 +211,7 @@ namespace osu.Game.Screens.Play return false; } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs index 284ac899ed..850543136c 100644 --- a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs +++ b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs @@ -206,9 +206,9 @@ namespace osu.Game.Screens.Play.HUD base.OnHoverLost(e); } - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case GlobalAction.Back: case GlobalAction.PauseGameplay: // in the future this behaviour will differ for replays etc. @@ -220,9 +220,9 @@ namespace osu.Game.Screens.Play.HUD return false; } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { - switch (action) + switch (e.Action) { case GlobalAction.Back: case GlobalAction.PauseGameplay: diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 13df9fefa7..54c74a7177 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -11,6 +11,7 @@ using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Configuration; using osu.Game.Input.Bindings; using osu.Game.Overlays; @@ -280,9 +281,9 @@ namespace osu.Game.Screens.Play protected PlayerSettingsOverlay CreatePlayerSettingsOverlay() => new PlayerSettingsOverlay(); - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case GlobalAction.HoldForHUD: holdingForHUD = true; @@ -311,9 +312,9 @@ namespace osu.Game.Screens.Play return false; } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { - switch (action) + switch (e.Action) { case GlobalAction.HoldForHUD: holdingForHUD = false; diff --git a/osu.Game/Screens/Play/HotkeyExitOverlay.cs b/osu.Game/Screens/Play/HotkeyExitOverlay.cs index 8d7e2481bf..13b72ffaf6 100644 --- a/osu.Game/Screens/Play/HotkeyExitOverlay.cs +++ b/osu.Game/Screens/Play/HotkeyExitOverlay.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Input.Bindings; using osu.Game.Overlays; @@ -9,17 +10,17 @@ namespace osu.Game.Screens.Play { public class HotkeyExitOverlay : HoldToConfirmOverlay, IKeyBindingHandler { - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { - if (action != GlobalAction.QuickExit) return false; + if (e.Action != GlobalAction.QuickExit) return false; BeginConfirm(); return true; } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { - if (action != GlobalAction.QuickExit) return; + if (e.Action != GlobalAction.QuickExit) return; AbortConfirm(); } diff --git a/osu.Game/Screens/Play/HotkeyRetryOverlay.cs b/osu.Game/Screens/Play/HotkeyRetryOverlay.cs index 58fd941f36..308befe372 100644 --- a/osu.Game/Screens/Play/HotkeyRetryOverlay.cs +++ b/osu.Game/Screens/Play/HotkeyRetryOverlay.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Input.Bindings; using osu.Game.Overlays; @@ -9,17 +10,17 @@ namespace osu.Game.Screens.Play { public class HotkeyRetryOverlay : HoldToConfirmOverlay, IKeyBindingHandler { - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { - if (action != GlobalAction.QuickRetry) return false; + if (e.Action != GlobalAction.QuickRetry) return false; BeginConfirm(); return true; } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { - if (action != GlobalAction.QuickRetry) return; + if (e.Action != GlobalAction.QuickRetry) return; AbortConfirm(); } diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index adbb5a53f6..0c6f1ed911 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Extensions; @@ -49,11 +50,11 @@ namespace osu.Game.Screens.Play private ScheduledDelegate keyboardSeekDelegate; - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { const double keyboard_seek_amount = 5000; - switch (action) + switch (e.Action) { case GlobalAction.SeekReplayBackward: keyboardSeekDelegate?.Cancel(); @@ -83,9 +84,9 @@ namespace osu.Game.Screens.Play } } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { - switch (action) + switch (e.Action) { case GlobalAction.SeekReplayBackward: case GlobalAction.SeekReplayForward: diff --git a/osu.Game/Screens/Play/SkipOverlay.cs b/osu.Game/Screens/Play/SkipOverlay.cs index a2145b7014..b04fcba0c6 100644 --- a/osu.Game/Screens/Play/SkipOverlay.cs +++ b/osu.Game/Screens/Play/SkipOverlay.cs @@ -144,9 +144,9 @@ namespace osu.Game.Screens.Play return base.OnMouseMove(e); } - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case GlobalAction.SkipCutscene: if (!button.Enabled.Value) @@ -159,7 +159,7 @@ namespace osu.Game.Screens.Play return false; } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 822ee1cf90..af60296344 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Framework.Screens; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -327,9 +328,9 @@ namespace osu.Game.Screens.Ranking } } - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case GlobalAction.Select: statisticsPanel.ToggleVisibility(); @@ -339,7 +340,7 @@ namespace osu.Game.Screens.Ranking return false; } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index b05b7aeb32..5eceae3c6e 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -514,29 +514,29 @@ namespace osu.Game.Screens.Select base.OnKeyUp(e); } - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case GlobalAction.SelectNext: - beginRepeatSelection(() => SelectNext(1, false), action); + beginRepeatSelection(() => SelectNext(1, false), e.Action); return true; case GlobalAction.SelectPrevious: - beginRepeatSelection(() => SelectNext(-1, false), action); + beginRepeatSelection(() => SelectNext(-1, false), e.Action); return true; } return false; } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { - switch (action) + switch (e.Action) { case GlobalAction.SelectNext: case GlobalAction.SelectPrevious: - endRepeatSelection(action); + endRepeatSelection(e.Action); break; } } diff --git a/osu.Game/Screens/Select/FooterButton.cs b/osu.Game/Screens/Select/FooterButton.cs index 9c0a68133c..8d2ea47757 100644 --- a/osu.Game/Screens/Select/FooterButton.cs +++ b/osu.Game/Screens/Select/FooterButton.cs @@ -172,9 +172,9 @@ namespace osu.Game.Screens.Select return base.OnClick(e); } - public virtual bool OnPressed(GlobalAction action) + public virtual bool OnPressed(KeyBindingPressEvent e) { - if (action == Hotkey) + if (e.Action == Hotkey) { TriggerClick(); return true; @@ -183,6 +183,6 @@ namespace osu.Game.Screens.Select return false; } - public virtual void OnReleased(GlobalAction action) { } + public virtual void OnReleased(KeyBindingReleaseEvent e) { } } } diff --git a/osu.Game/Screens/Select/FooterButtonRandom.cs b/osu.Game/Screens/Select/FooterButtonRandom.cs index 1eaf2c591e..1d4722cf5d 100644 --- a/osu.Game/Screens/Select/FooterButtonRandom.cs +++ b/osu.Game/Screens/Select/FooterButtonRandom.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Input.Bindings; @@ -58,11 +59,11 @@ namespace osu.Game.Screens.Select }; } - public override bool OnPressed(GlobalAction action) + public override bool OnPressed(KeyBindingPressEvent e) { - rewindSearch = action == GlobalAction.SelectPreviousRandom; + rewindSearch = e.Action == GlobalAction.SelectPreviousRandom; - if (action != GlobalAction.SelectNextRandom && action != GlobalAction.SelectPreviousRandom) + if (e.Action != GlobalAction.SelectNextRandom && e.Action != GlobalAction.SelectPreviousRandom) { return false; } @@ -71,9 +72,9 @@ namespace osu.Game.Screens.Select return true; } - public override void OnReleased(GlobalAction action) + public override void OnReleased(KeyBindingReleaseEvent e) { - if (action == GlobalAction.SelectPreviousRandom) + if (e.Action == GlobalAction.SelectPreviousRandom) { rewindSearch = false; } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 1f0f134ba7..9801098952 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -812,11 +812,11 @@ namespace osu.Game.Screens.Select Schedule(() => BeatmapDetails.Refresh()))); } - public virtual bool OnPressed(GlobalAction action) + public virtual bool OnPressed(KeyBindingPressEvent e) { if (!this.IsCurrentScreen()) return false; - switch (action) + switch (e.Action) { case GlobalAction.Select: FinaliseSelection(); @@ -826,7 +826,7 @@ namespace osu.Game.Screens.Select return false; } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } diff --git a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs index 2562e9c57c..396852365f 100644 --- a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs +++ b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Input.Bindings; @@ -32,9 +33,9 @@ namespace osu.Game.Skinning.Editor RelativeSizeAxes = Axes.Both; } - public bool OnPressed(GlobalAction action) + public bool OnPressed(KeyBindingPressEvent e) { - switch (action) + switch (e.Action) { case GlobalAction.Back: if (skinEditor?.State.Value != Visibility.Visible) @@ -97,7 +98,7 @@ namespace osu.Game.Skinning.Editor } } - public void OnReleased(GlobalAction action) + public void OnReleased(KeyBindingReleaseEvent e) { } From e8dea0138cb8fc4de9d6eba5ab9df355489aaf90 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 16 Sep 2021 18:28:35 +0900 Subject: [PATCH 1970/2442] Fix one more issue --- osu.Game/Screens/Edit/Verify/IssueTable.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index 05a8fdd26d..b38a62d838 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -8,6 +8,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Input.Bindings; @@ -52,7 +53,7 @@ namespace osu.Game.Screens.Edit.Verify if (issue.Time != null) { clock.Seek(issue.Time.Value); - editor.OnPressed(GlobalAction.EditorComposeMode); + editor.OnPressed(new KeyBindingPressEvent(GetContainingInputManager().CurrentState, GlobalAction.EditorComposeMode)); } if (!issue.HitObjects.Any()) From a18fed0da49ba399974bf62322a52da9db1a3300 Mon Sep 17 00:00:00 2001 From: kj415j45 Date: Thu, 16 Sep 2021 17:38:33 +0800 Subject: [PATCH 1971/2442] Remove unused using --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index cfb377f74a..f62131e2d7 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -2,7 +2,6 @@ // 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.Graphics; using osu.Framework.Input; From d8f27633a4f05a75475fa330b8f758af2d81632d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 16 Sep 2021 19:35:15 +0900 Subject: [PATCH 1972/2442] Fix legacy approach circles accepting alpha --- .../TestSceneSkinFallbacks.cs | 5 +- .../Objects/Drawables/DrawableHitCircle.cs | 18 +++++-- osu.Game.Rulesets.Osu/OsuSkinComponents.cs | 1 + .../Skinning/Default/ApproachCircle.cs | 50 ------------------- .../Skinning/Default/DefaultApproachCircle.cs | 49 ++++++++++++++++++ .../Skinning/Legacy/LegacyApproachCircle.cs | 49 ++++++++++++++++++ .../Legacy/OsuLegacySkinTransformer.cs | 3 ++ 7 files changed, 120 insertions(+), 55 deletions(-) delete mode 100644 osu.Game.Rulesets.Osu/Skinning/Default/ApproachCircle.cs create mode 100644 osu.Game.Rulesets.Osu/Skinning/Default/DefaultApproachCircle.cs create mode 100644 osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyApproachCircle.cs diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs index 662cbaee68..0f362851a9 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs @@ -20,6 +20,7 @@ using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.Skinning.Default; using osu.Game.Skinning; using osu.Game.Storyboards; using osu.Game.Tests.Visual; @@ -86,9 +87,9 @@ namespace osu.Game.Rulesets.Osu.Tests if (firstObject == null) return false; - var skinnable = firstObject.ApproachCircle.Child as SkinnableDrawable; + var skinnable = firstObject.ApproachCircle; - if (skin == null && skinnable?.Drawable is Sprite) + if (skin == null && skinnable?.Drawable is DefaultApproachCircle) // check for default skin provider return true; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 46fc8f99b2..7d28ec822d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public OsuAction? HitAction => HitArea.HitAction; protected virtual OsuSkinComponents CirclePieceComponent => OsuSkinComponents.HitCircle; - public ApproachCircle ApproachCircle { get; private set; } + public SkinnableDrawable ApproachCircle { get; private set; } public HitReceptor HitArea { get; private set; } public SkinnableDrawable CirclePiece { get; private set; } @@ -74,8 +74,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Anchor = Anchor.Centre, Origin = Anchor.Centre, }, - ApproachCircle = new ApproachCircle + ApproachCircle = new ProxyableSkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.ApproachCircle), _ => new DefaultApproachCircle()) { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, Alpha = 0, Scale = new Vector2(4), } @@ -88,7 +91,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables PositionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition); StackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition); ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue)); - AccentColour.BindValueChanged(accent => ApproachCircle.Colour = accent.NewValue); } protected override void LoadComplete() @@ -250,5 +252,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { } } + + private class ProxyableSkinnableDrawable : SkinnableDrawable + { + public override bool RemoveWhenNotAlive => false; + + public ProxyableSkinnableDrawable(ISkinComponent component, Func defaultImplementation = null, ConfineMode confineMode = ConfineMode.NoScaling) + : base(component, defaultImplementation, confineMode) + { + } + } } } diff --git a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs index 46e501758b..59fe353bd2 100644 --- a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs +++ b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs @@ -18,5 +18,6 @@ namespace osu.Game.Rulesets.Osu SliderBall, SliderBody, SpinnerBody, + ApproachCircle, } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/ApproachCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Default/ApproachCircle.cs deleted file mode 100644 index 62f00a2b49..0000000000 --- a/osu.Game.Rulesets.Osu/Skinning/Default/ApproachCircle.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Textures; -using osu.Game.Skinning; -using osuTK; - -namespace osu.Game.Rulesets.Osu.Skinning.Default -{ - public class ApproachCircle : Container - { - public override bool RemoveWhenNotAlive => false; - - public ApproachCircle() - { - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - - RelativeSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - Child = new SkinnableApproachCircle(); - } - - private class SkinnableApproachCircle : SkinnableSprite - { - public SkinnableApproachCircle() - : base("Gameplay/osu/approachcircle") - { - } - - protected override Drawable CreateDefault(ISkinComponent component) - { - var drawable = base.CreateDefault(component); - - // account for the sprite being used for the default approach circle being taken from stable, - // when hitcircles have 5px padding on each size. this should be removed if we update the sprite. - drawable.Scale = new Vector2(128 / 118f); - - return drawable; - } - } - } -} diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultApproachCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultApproachCircle.cs new file mode 100644 index 0000000000..a522367fe6 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultApproachCircle.cs @@ -0,0 +1,49 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Skinning; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Osu.Skinning.Default +{ + public class DefaultApproachCircle : SkinnableSprite + { + private readonly IBindable accentColour = new Bindable(); + + [Resolved] + private DrawableHitObject drawableObject { get; set; } + + public DefaultApproachCircle() + : base("Gameplay/osu/approachcircle") + { + } + + [BackgroundDependencyLoader] + private void load() + { + accentColour.BindTo(drawableObject.AccentColour); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + accentColour.BindValueChanged(colour => Colour = colour.NewValue, true); + } + + protected override Drawable CreateDefault(ISkinComponent component) + { + var drawable = base.CreateDefault(component); + + // Although this is a non-legacy component, osu-resources currently stores approach circle as a legacy-like texture. + // See LegacyApproachCircle for documentation as to why this is required. + drawable.Scale = new Vector2(128 / 118f); + + return drawable; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyApproachCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyApproachCircle.cs new file mode 100644 index 0000000000..09f759fe7d --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyApproachCircle.cs @@ -0,0 +1,49 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Skinning; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Osu.Skinning.Legacy +{ + public class LegacyApproachCircle : SkinnableSprite + { + private readonly IBindable accentColour = new Bindable(); + + [Resolved] + private DrawableHitObject drawableObject { get; set; } + + public LegacyApproachCircle() + : base("approachcircle") + { + } + + [BackgroundDependencyLoader] + private void load() + { + accentColour.BindTo(drawableObject.AccentColour); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + accentColour.BindValueChanged(colour => Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true); + } + + protected override Drawable CreateDefault(ISkinComponent component) + { + var drawable = base.CreateDefault(component); + + // account for the sprite being used for the default approach circle being taken from stable, + // when hitcircles have 5px padding on each size. this should be removed if we update the sprite. + drawable.Scale = new Vector2(128 / 118f); + + return drawable; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index 41b0a88f11..190195f0a6 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -108,6 +108,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy return new LegacyOldStyleSpinner(); return null; + + case OsuSkinComponents.ApproachCircle: + return new LegacyApproachCircle(); } } From bcbd0e096165cd7d1cf3ad871e43b9fc1a79e3e6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 16 Sep 2021 20:06:20 +0900 Subject: [PATCH 1973/2442] Revert ctor param --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyApproachCircle.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyApproachCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyApproachCircle.cs index 09f759fe7d..6a2cb871b1 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyApproachCircle.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyApproachCircle.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private DrawableHitObject drawableObject { get; set; } public LegacyApproachCircle() - : base("approachcircle") + : base("Gameplay/osu/approachcircle") { } From a1d33c1a46f954ae94705a3106ddc9b37e267a68 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Sep 2021 22:38:19 +0900 Subject: [PATCH 1974/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index d4331a5e65..7707b717e9 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 5a302c5349..390b026497 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 73e0030114..6e852c4321 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From bb132f9509ee954625e75c6461b5b293832ea72b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Sep 2021 22:41:35 +0900 Subject: [PATCH 1975/2442] Update a few more missed event changes in tests --- .../TestSceneDrawableManiaHitObject.cs | 3 ++- osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs | 5 +++-- osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleArea.cs | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableManiaHitObject.cs index 4a6c59e297..92c95b8fde 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneDrawableManiaHitObject.cs @@ -3,6 +3,7 @@ using NUnit.Framework; using osu.Framework.Graphics; +using osu.Framework.Input.Events; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -58,7 +59,7 @@ namespace osu.Game.Rulesets.Mania.Tests AddStep("Hold key", () => { clock.CurrentTime = 0; - note.OnPressed(ManiaAction.Key1); + note.OnPressed(new KeyBindingPressEvent(GetContainingInputManager().CurrentState, ManiaAction.Key1)); }); AddStep("progress time", () => clock.CurrentTime = 500); AddAssert("head is visible", () => note.Head.Alpha == 1); diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs index 2326a0c391..f9dc9abd75 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; using osu.Framework.Input; +using osu.Framework.Input.Events; using osu.Framework.Testing.Input; using osu.Framework.Utils; using osu.Game.Audio; @@ -143,9 +144,9 @@ namespace osu.Game.Rulesets.Osu.Tests pressed = value; if (value) - OnPressed(OsuAction.LeftButton); + OnPressed(new KeyBindingPressEvent(GetContainingInputManager().CurrentState, OsuAction.LeftButton)); else - OnReleased(OsuAction.LeftButton); + OnReleased(new KeyBindingReleaseEvent(GetContainingInputManager().CurrentState, OsuAction.LeftButton)); } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleArea.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleArea.cs index 1fdcd73dde..575523b168 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleArea.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleArea.cs @@ -4,6 +4,7 @@ using System; using NUnit.Framework; using osu.Framework.Graphics; +using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Osu.Objects; @@ -97,7 +98,7 @@ namespace osu.Game.Rulesets.Osu.Tests private void scheduleHit() => AddStep("schedule action", () => { var delay = hitCircle.StartTime - hitCircle.HitWindows.WindowFor(HitResult.Great) - Time.Current; - Scheduler.AddDelayed(() => hitAreaReceptor.OnPressed(OsuAction.LeftButton), delay); + Scheduler.AddDelayed(() => hitAreaReceptor.OnPressed(new KeyBindingPressEvent(GetContainingInputManager().CurrentState, OsuAction.LeftButton)), delay); }); } } From edb1230111ef1cc1680ee1a032dc1ca951e8fcef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Sep 2021 22:48:09 +0900 Subject: [PATCH 1976/2442] Fix potential nullref throw on failed startup --- osu.Game/Database/DatabaseContextFactory.cs | 7 +++++-- osu.Game/OsuGameBase.cs | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/DatabaseContextFactory.cs b/osu.Game/Database/DatabaseContextFactory.cs index 1cceb59b11..d402195f29 100644 --- a/osu.Game/Database/DatabaseContextFactory.cs +++ b/osu.Game/Database/DatabaseContextFactory.cs @@ -163,8 +163,11 @@ namespace osu.Game.Database public void FlushConnections() { - foreach (var context in threadContexts.Values) - context.Dispose(); + if (threadContexts != null) + { + foreach (var context in threadContexts.Values) + context.Dispose(); + } recycleThreadContexts(); } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 59a05aec4f..7aa460981a 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -527,7 +527,7 @@ namespace osu.Game BeatmapManager?.Dispose(); LocalConfig?.Dispose(); - contextFactory.FlushConnections(); + contextFactory?.FlushConnections(); } } } From 414735b8d083117491ff8cbca9d078b62c5fe0ca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Sep 2021 00:18:08 +0900 Subject: [PATCH 1977/2442] Bump mobile project props realm references --- osu.Android.props | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 7707b717e9..0e60a5a99e 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -56,6 +56,6 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 6e852c4321..2158772b83 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -99,6 +99,6 @@ - + From cdd813926b53558be371f66b193e452babdaae86 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Fri, 17 Sep 2021 00:27:42 +0000 Subject: [PATCH 1978/2442] nerf to high bpm rhythm changes, nerf to 1/3->1/4 --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 7b99ceac6f..0e32ea915d 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -77,9 +77,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double effectiveRatio = Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta); if (effectiveRatio > 0.5) - effectiveRatio = 0.5 + (effectiveRatio - 0.5) * 10; // large buff for 1/3 -> 1/4 type transitions. + effectiveRatio = 0.5 + (effectiveRatio - 0.5) * 5; // large buff for 1/3 -> 1/4 type transitions. - effectiveRatio *= Math.Sqrt(100 / ((currDelta + prevDelta) / 2)) * currHistoricalDecay; // scale with bpm slightly and with time + effectiveRatio *= currHistoricalDecay; // scale with time if (firstDeltaSwitch) { From 2983d5468247a5d7db672eaa7db1f6868103370c Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Thu, 16 Sep 2021 17:32:23 -0700 Subject: [PATCH 1979/2442] Fix wiki main page blurb overflowing at higher ui scale --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index 3fb0aa450e..c9ee2cbfd5 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -9,7 +9,7 @@ using osu.Framework.Graphics.Containers; using HtmlAgilityPack; using osu.Framework.Allocation; using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Wiki { @@ -56,12 +56,12 @@ namespace osu.Game.Overlays.Wiki { Vertical = 30, }, - Child = new OsuSpriteText + Child = new OsuTextFlowContainer(t => t.Font = OsuFont.GetFont(Typeface.Inter, size: 12, weight: FontWeight.Light)) { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Text = blurbNode.InnerText, - Font = OsuFont.GetFont(Typeface.Inter, size: 12, weight: FontWeight.Light), - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, + TextAnchor = Anchor.TopCentre, } }; } From 55feb47e61878ef97cf890de0e58f731150908cb Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Thu, 16 Sep 2021 20:17:21 -0700 Subject: [PATCH 1980/2442] Disallow track adjustments on playlists / multi lounge --- osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 08bdd0487a..9481ffc3e4 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -37,6 +37,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { public override string Title => "Lounge"; + public override bool? AllowTrackAdjustments => false; + protected override BackgroundScreen CreateBackground() => new LoungeBackgroundScreen { SelectedRoom = { BindTarget = SelectedRoom } From cd4ba71a6b012f0dd106a099df0ecffdb97d0fbb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Sep 2021 13:22:39 +0900 Subject: [PATCH 1981/2442] Revert "Merge pull request #14772 from Joehuu/lounge-disallow-track-adj" This reverts commit 06ff4838fb55f3362019ac0e173f10b64ec372b2, reversing changes made to 5453ea0ce99bea69a9f87163a500140b1d7b318a. --- osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 9481ffc3e4..08bdd0487a 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -37,8 +37,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { public override string Title => "Lounge"; - public override bool? AllowTrackAdjustments => false; - protected override BackgroundScreen CreateBackground() => new LoungeBackgroundScreen { SelectedRoom = { BindTarget = SelectedRoom } From f524e913e1e456fa342f75d6d90b0174ae451940 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Sep 2021 13:23:02 +0900 Subject: [PATCH 1982/2442] Move `AllowTrackAdjustments` specification to `RoomSubScreen` --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 2 ++ osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 9095b78eb4..bcb793062b 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -29,6 +29,8 @@ namespace osu.Game.Screens.OnlinePlay.Match [Cached(typeof(IBindable))] protected readonly Bindable SelectedItem = new Bindable(); + public override bool? AllowTrackAdjustments => true; + protected override BackgroundScreen CreateBackground() => new RoomBackgroundScreen(Room.Playlist.FirstOrDefault()) { SelectedItem = { BindTarget = SelectedItem } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs index 8c4f0c1394..3411c4afb1 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs @@ -11,8 +11,6 @@ namespace osu.Game.Screens.OnlinePlay { public override bool DisallowExternalBeatmapRulesetChanges => false; - public override bool? AllowTrackAdjustments => true; - public virtual string ShortTitle => Title; [Resolved(CanBeNull = true)] From 1b13b74740eb42050effc28b0da2f951a0468426 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Sep 2021 13:48:19 +0900 Subject: [PATCH 1983/2442] Fix skin editor potentially leaving game-wide masking in the wrong state Just going with the simplest way to solve this. Closes https://github.com/ppy/osu/issues/14769. --- osu.Game/Skinning/Editor/SkinEditorOverlay.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs index 396852365f..a892ec1ca3 100644 --- a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs +++ b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs @@ -82,7 +82,7 @@ namespace osu.Game.Skinning.Editor { if (visibility.NewValue == Visibility.Visible) { - target.Masking = true; + updateMasking(); target.AllowScaling = false; target.RelativePositionAxes = Axes.Both; @@ -93,11 +93,14 @@ namespace osu.Game.Skinning.Editor { target.AllowScaling = true; - target.ScaleTo(1, SkinEditor.TRANSITION_DURATION, Easing.OutQuint).OnComplete(_ => target.Masking = false); + target.ScaleTo(1, SkinEditor.TRANSITION_DURATION, Easing.OutQuint).OnComplete(_ => updateMasking()); target.MoveToX(0f, SkinEditor.TRANSITION_DURATION, Easing.OutQuint); } } + private void updateMasking() => + target.Masking = skinEditor.State.Value == Visibility.Visible; + public void OnReleased(KeyBindingReleaseEvent e) { } From 2c071a4d223481f3a14e770eaedcdd14f5415a3c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Sep 2021 14:40:29 +0900 Subject: [PATCH 1984/2442] Add test coverage of pausing with a large audio offset --- .../Visual/Gameplay/TestScenePause.cs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs index bddc7ab731..324953eb9f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs @@ -3,10 +3,12 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Framework.Testing; +using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; using osu.Game.Rulesets; @@ -39,6 +41,45 @@ namespace osu.Game.Tests.Visual.Gameplay confirmClockRunning(true); } + [Test] + public void TestPauseWithLargeOffset() + { + double lastTime; + bool alwaysGoingForward = true; + + AddStep("force large offset", () => + { + var offset = (BindableDouble)LocalConfig.GetBindable(OsuSetting.AudioOffset); + + // use a large negative offset to avoid triggering a fail from forwards seeking. + offset.MinValue = -5000; + offset.Value = -5000; + }); + + AddStep("add time forward check hook", () => + { + lastTime = double.MinValue; + alwaysGoingForward = true; + + Player.OnUpdate += _ => + { + double currentTime = Player.GameplayClockContainer.CurrentTime; + alwaysGoingForward &= currentTime >= lastTime; + lastTime = currentTime; + }; + }); + + AddStep("move cursor outside", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft - new Vector2(10))); + + pauseAndConfirm(); + + resumeAndConfirm(); + + AddAssert("time didn't go backwards", () => alwaysGoingForward); + + AddStep("reset offset", () => LocalConfig.SetValue(OsuSetting.AudioOffset, 0)); + } + [Test] public void TestPauseResume() { From 5f27f1c099c0b1e369cf01662bcf758de91ae42d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Sep 2021 15:39:03 +0900 Subject: [PATCH 1985/2442] Avoid accounting for the pause pitch adjust effect when "fixing" hardware offset adjustments Bit of an unfortunate one. Because we are applying the pitch adjustment to the lowest level (`Track`), it's hard to filter out in parent clock calculations. Tried a few solutions but this feels the best. Note that we can't just undo the `pauseFreqAdjust` adjustment as it will div-by-zero. Closes https://github.com/ppy/osu/issues/14773. --- .../Play/MasterGameplayClockContainer.cs | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 3fbb55872b..aa46522dec 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -158,10 +158,10 @@ namespace osu.Game.Screens.Play { // Lazer's audio timings in general doesn't match stable. This is the result of user testing, albeit limited. // This only seems to be required on windows. We need to eventually figure out why, with a bit of luck. - platformOffsetClock = new HardwareCorrectionOffsetClock(source) { Offset = RuntimeInfo.OS == RuntimeInfo.Platform.Windows ? 15 : 0 }; + platformOffsetClock = new HardwareCorrectionOffsetClock(source, pauseFreqAdjust) { Offset = RuntimeInfo.OS == RuntimeInfo.Platform.Windows ? 15 : 0 }; // the final usable gameplay clock with user-set offsets applied. - userOffsetClock = new HardwareCorrectionOffsetClock(platformOffsetClock); + userOffsetClock = new HardwareCorrectionOffsetClock(platformOffsetClock, pauseFreqAdjust); return masterGameplayClock = new MasterGameplayClock(userOffsetClock); } @@ -216,11 +216,25 @@ namespace osu.Game.Screens.Play { // we always want to apply the same real-time offset, so it should be adjusted by the difference in playback rate (from realtime) to achieve this. // base implementation already adds offset at 1.0 rate, so we only add the difference from that here. - public override double CurrentTime => base.CurrentTime + Offset * (Rate - 1); + public override double CurrentTime => base.CurrentTime + offsetAdjust; - public HardwareCorrectionOffsetClock(IClock source, bool processSource = true) - : base(source, processSource) + private readonly BindableDouble pauseRateAdjust; + + private double offsetAdjust; + + public HardwareCorrectionOffsetClock(IClock source, BindableDouble pauseRateAdjust) + : base(source) { + this.pauseRateAdjust = pauseRateAdjust; + } + + public override void ProcessFrame() + { + base.ProcessFrame(); + + // changing this during the pause transform effect will cause a potentially large offset to be suddenly applied as we approach zero rate. + if (pauseRateAdjust.Value == 1) + offsetAdjust = Offset * (Rate - 1); } } From e0bbc677d26cec949720200038a09fd6e0c0331d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Sep 2021 16:23:09 +0900 Subject: [PATCH 1986/2442] Fix `TestRollbackOnFailure` not cleaning up after itself --- osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 5dc25d6643..b2bd60d342 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -424,14 +424,14 @@ namespace osu.Game.Tests.Beatmaps.IO checkBeatmapCount(osu, 12); checkSingleReferencedFileCount(osu, 18); - var breakTemp = TestResources.GetTestBeatmapForImport(); + var brokenTempFilename = TestResources.GetTestBeatmapForImport(); MemoryStream brokenOsu = new MemoryStream(); - MemoryStream brokenOsz = new MemoryStream(await File.ReadAllBytesAsync(breakTemp)); + MemoryStream brokenOsz = new MemoryStream(await File.ReadAllBytesAsync(brokenTempFilename)); - File.Delete(breakTemp); + File.Delete(brokenTempFilename); - using (var outStream = File.Open(breakTemp, FileMode.CreateNew)) + using (var outStream = File.Open(brokenTempFilename, FileMode.CreateNew)) using (var zip = ZipArchive.Open(brokenOsz)) { zip.AddEntry("broken.osu", brokenOsu, false); @@ -441,7 +441,7 @@ namespace osu.Game.Tests.Beatmaps.IO // this will trigger purging of the existing beatmap (online set id match) but should rollback due to broken osu. try { - await manager.Import(new ImportTask(breakTemp)); + await manager.Import(new ImportTask(brokenTempFilename)); } catch { @@ -456,6 +456,8 @@ namespace osu.Game.Tests.Beatmaps.IO checkSingleReferencedFileCount(osu, 18); Assert.AreEqual(1, loggedExceptionCount); + + File.Delete(brokenTempFilename); } finally { From 2ab235ebe72956b34844f1966b7fca7fa1a5861d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Sep 2021 16:24:21 +0900 Subject: [PATCH 1987/2442] Use new temporary folder storage for beatmap import tests --- osu.Game.Tests/Resources/TestResources.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Resources/TestResources.cs b/osu.Game.Tests/Resources/TestResources.cs index 7170a76b8b..839366d98e 100644 --- a/osu.Game.Tests/Resources/TestResources.cs +++ b/osu.Game.Tests/Resources/TestResources.cs @@ -1,9 +1,11 @@ // 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.IO; using NUnit.Framework; using osu.Framework.IO.Stores; +using osu.Framework.Testing; namespace osu.Game.Tests.Resources { @@ -11,6 +13,8 @@ namespace osu.Game.Tests.Resources { public const double QUICK_BEATMAP_LENGTH = 10000; + private static readonly TemporaryNativeStorage temp_storage = new TemporaryNativeStorage("TestResources"); + public static DllResourceStore GetStore() => new DllResourceStore(typeof(TestResources).Assembly); public static Stream OpenResource(string name) => GetStore().GetStream($"Resources/{name}"); @@ -25,7 +29,7 @@ namespace osu.Game.Tests.Resources /// A path to a copy of a beatmap archive (osz). Should be deleted after use. public static string GetQuickTestBeatmapForImport() { - var tempPath = Path.GetTempFileName() + ".osz"; + var tempPath = getTempFilename(); using (var stream = OpenResource("Archives/241526 Soleily - Renatus_virtual_quick.osz")) using (var newFile = File.Create(tempPath)) stream.CopyTo(newFile); @@ -41,7 +45,7 @@ namespace osu.Game.Tests.Resources /// A path to a copy of a beatmap archive (osz). Should be deleted after use. public static string GetTestBeatmapForImport(bool virtualTrack = false) { - var tempPath = Path.GetTempFileName() + ".osz"; + var tempPath = getTempFilename(); using (var stream = GetTestBeatmapStream(virtualTrack)) using (var newFile = File.Create(tempPath)) @@ -50,5 +54,7 @@ namespace osu.Game.Tests.Resources Assert.IsTrue(File.Exists(tempPath)); return tempPath; } + + private static string getTempFilename() => temp_storage.GetFullPath(Guid.NewGuid() + ".osz"); } } From 45caeb84d38eaa4404e6c3dc2bd1ce465ebe3131 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Sep 2021 16:28:47 +0900 Subject: [PATCH 1988/2442] Fix incorrect type specification when restoring bindable value --- osu.Game.Tests/Visual/Gameplay/TestScenePause.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs index 324953eb9f..04676f656f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs @@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("time didn't go backwards", () => alwaysGoingForward); - AddStep("reset offset", () => LocalConfig.SetValue(OsuSetting.AudioOffset, 0)); + AddStep("reset offset", () => LocalConfig.SetValue(OsuSetting.AudioOffset, 0.0)); } [Test] From c9e76783e64630cd1b9d9b08bebe108d41511886 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Sep 2021 17:14:56 +0900 Subject: [PATCH 1989/2442] Fix taiko HD not calculating pre-empt correctly --- .../Mods/TaikoModHidden.cs | 29 +++++++------------ .../UI/DrawableTaikoRuleset.cs | 10 +++++++ 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs index a6b3fe1cd9..5104fd9cff 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs @@ -2,43 +2,41 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects.Drawables; +using osu.Game.Rulesets.Taiko.UI; +using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Taiko.Mods { - public class TaikoModHidden : ModHidden + public class TaikoModHidden : ModHidden, IApplicableToDrawableRuleset { public override string Description => @"Beats fade out before you hit them!"; public override double ScoreMultiplier => 1.06; - private ControlPointInfo controlPointInfo; + private DrawableTaikoRuleset drawableRuleset; + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + this.drawableRuleset = (DrawableTaikoRuleset)drawableRuleset; + } protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) { ApplyNormalVisibilityState(hitObject, state); } - protected double MultiplierAt(double position) - { - double beatLength = controlPointInfo.TimingPointAt(position).BeatLength; - double speedMultiplier = controlPointInfo.DifficultyPointAt(position).SpeedMultiplier; - - return speedMultiplier * TimingControlPoint.DEFAULT_BEAT_LENGTH / beatLength; - } - protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) { switch (hitObject) { case DrawableDrumRollTick _: case DrawableHit _: - double preempt = 10000 / MultiplierAt(hitObject.HitObject.StartTime); + double preempt = drawableRuleset.TimeRange.Value / drawableRuleset.ControlPointAt(hitObject.HitObject.StartTime).Multiplier; double start = hitObject.HitObject.StartTime - preempt * 0.6; double duration = preempt * 0.3; @@ -56,10 +54,5 @@ namespace osu.Game.Rulesets.Taiko.Mods break; } } - - public override void ApplyToBeatmap(IBeatmap beatmap) - { - controlPointInfo = beatmap.ControlPointInfo; - } } } diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index 6ddbf3c16b..824b95639b 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -1,6 +1,7 @@ // 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 osu.Framework.Allocation; using osu.Framework.Bindables; @@ -16,6 +17,7 @@ using osu.Game.Input.Handlers; using osu.Game.Replays; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Timing; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Scoring; using osu.Game.Skinning; @@ -60,6 +62,14 @@ namespace osu.Game.Rulesets.Taiko.UI scroller.Height = ToLocalSpace(playfieldScreen.TopLeft + new Vector2(0, playfieldScreen.Height / 20)).Y; } + public MultiplierControlPoint ControlPointAt(double time) + { + int result = ControlPoints.BinarySearch(new MultiplierControlPoint(time)); + if (result < 0) + result = Math.Clamp(~result - 1, 0, ControlPoints.Count); + return ControlPoints[result]; + } + public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new TaikoPlayfieldAdjustmentContainer(); protected override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo); From ea68be08cb3fa6badd2f5df51f3fc5bc8a1901e8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Sep 2021 17:27:54 +0900 Subject: [PATCH 1990/2442] Split magic values into named constants --- osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs index 5104fd9cff..b3a54521d8 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs @@ -18,6 +18,18 @@ namespace osu.Game.Rulesets.Taiko.Mods public override string Description => @"Beats fade out before you hit them!"; public override double ScoreMultiplier => 1.06; + /// + /// How far away from the hit target should hitobjects start to fade out. + /// Range: [0, 1] + /// + private const float fade_out_start_time = 0.6f; + + /// + /// How long hitobjects take to fade out, in terms of the scrolling length. + /// Range: [0, 1] + /// + private const float fade_out_duration = 0.3f; + private DrawableTaikoRuleset drawableRuleset; public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) @@ -37,8 +49,8 @@ namespace osu.Game.Rulesets.Taiko.Mods case DrawableDrumRollTick _: case DrawableHit _: double preempt = drawableRuleset.TimeRange.Value / drawableRuleset.ControlPointAt(hitObject.HitObject.StartTime).Multiplier; - double start = hitObject.HitObject.StartTime - preempt * 0.6; - double duration = preempt * 0.3; + double start = hitObject.HitObject.StartTime - preempt * fade_out_start_time; + double duration = preempt * fade_out_duration; using (hitObject.BeginAbsoluteSequence(start)) { From 5dd0e0d961dacbd71a27d0e340c71a62a7bd3e35 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Sep 2021 17:33:32 +0900 Subject: [PATCH 1991/2442] Don't apply normal visibility to increased visibility state --- osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs index b3a54521d8..baad65297c 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs @@ -39,7 +39,6 @@ namespace osu.Game.Rulesets.Taiko.Mods protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) { - ApplyNormalVisibilityState(hitObject, state); } protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) From a4238e49a70b30a6f3a97f61c7731b29d91b2afb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Sep 2021 17:39:34 +0900 Subject: [PATCH 1992/2442] Revert "Don't apply normal visibility to increased visibility state" This reverts commit 5dd0e0d961dacbd71a27d0e340c71a62a7bd3e35. --- osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs index baad65297c..b3a54521d8 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs @@ -39,6 +39,7 @@ namespace osu.Game.Rulesets.Taiko.Mods protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) { + ApplyNormalVisibilityState(hitObject, state); } protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) From f584d6593acbd028287049eed49f335832480a16 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Sep 2021 18:10:53 +0900 Subject: [PATCH 1993/2442] Fix flashlight alignment --- osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs index 1253b7c8ae..3578e3a734 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Taiko.Mods if (!flashlightProperties.IsValid) { - FlashlightPosition = taikoPlayfield.HitTarget.ToSpaceOfOtherDrawable(taikoPlayfield.HitTarget.OriginPosition, this); + FlashlightPosition = ToLocalSpace(taikoPlayfield.HitTarget.ScreenSpaceDrawQuad.Centre); flashlightProperties.Validate(); } } From 1c8e17cf11158720af08f0cda6e5999b68879815 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Sep 2021 18:14:39 +0900 Subject: [PATCH 1994/2442] Fix the default background parallax being set incorrectly when no screen is present --- osu.Game/Screens/OsuScreenStack.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OsuScreenStack.cs b/osu.Game/Screens/OsuScreenStack.cs index e2a0414df7..ebbcbd7650 100644 --- a/osu.Game/Screens/OsuScreenStack.cs +++ b/osu.Game/Screens/OsuScreenStack.cs @@ -51,6 +51,6 @@ namespace osu.Game.Screens } private void setParallax(IScreen next) => - parallaxContainer.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT * ((IOsuScreen)next)?.BackgroundParallaxAmount ?? 1.0f; + parallaxContainer.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT * (((IOsuScreen)next)?.BackgroundParallaxAmount ?? 1.0f); } } From a1f587f2c54436e5eff098dcf054085beeea928d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Sep 2021 18:25:23 +0900 Subject: [PATCH 1995/2442] Add failing test coverage of password entry textbox not regaining focus --- .../TestSceneMultiplayerLoungeSubScreen.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs index 4e2a91af78..b7da31a2b5 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs @@ -83,7 +83,7 @@ namespace osu.Game.Tests.Visual.Multiplayer } [Test] - public void TestJoinRoomWithIncorrectPassword() + public void TestJoinRoomWithIncorrectPasswordViaButton() { DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null; @@ -96,6 +96,24 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("room not joined", () => loungeScreen.IsCurrentScreen()); AddUntilStep("password prompt still visible", () => passwordEntryPopover.State.Value == Visibility.Visible); + AddAssert("textbox still focused", () => InputManager.FocusedDrawable is OsuPasswordTextBox); + } + + [Test] + public void TestJoinRoomWithIncorrectPasswordViaEnter() + { + DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null; + + AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true)); + AddStep("select room", () => InputManager.Key(Key.Down)); + AddStep("attempt join room", () => InputManager.Key(Key.Enter)); + AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null); + AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "wrong"); + AddStep("press enter", () => InputManager.Key(Key.Enter)); + + AddAssert("room not joined", () => loungeScreen.IsCurrentScreen()); + AddUntilStep("password prompt still visible", () => passwordEntryPopover.State.Value == Visibility.Visible); + AddAssert("textbox still focused", () => InputManager.FocusedDrawable is OsuPasswordTextBox); } [Test] From 027912d4f666db7b7439d76f14294fb11d635a60 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Sep 2021 18:23:36 +0900 Subject: [PATCH 1996/2442] Refocus the multiplayer password entry textbox on failed join --- osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index d55cccd414..a823db282c 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -234,6 +234,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { passwordTextbox.Text = string.Empty; + GetContainingInputManager().ChangeFocus(passwordTextbox); + errorText.Text = error; errorText .FadeIn() From 50f155e4b936e9858fde9d51054330804e94c6c7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Sep 2021 18:30:38 +0900 Subject: [PATCH 1997/2442] Move login panel related files to own namespace and tidy up class nesting --- osu.Game/Overlays/Login/LoginForm.cs | 108 +++++ osu.Game/Overlays/Login/LoginPanel.cs | 199 +++++++++ osu.Game/Overlays/Login/UserAction.cs | 18 + osu.Game/Overlays/Login/UserDropdown.cs | 121 +++++ osu.Game/Overlays/LoginOverlay.cs | 12 +- .../Sections/General/LoginSettings.cs | 421 ------------------ 6 files changed, 452 insertions(+), 427 deletions(-) create mode 100644 osu.Game/Overlays/Login/LoginForm.cs create mode 100644 osu.Game/Overlays/Login/LoginPanel.cs create mode 100644 osu.Game/Overlays/Login/UserAction.cs create mode 100644 osu.Game/Overlays/Login/UserDropdown.cs delete mode 100644 osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs diff --git a/osu.Game/Overlays/Login/LoginForm.cs b/osu.Game/Overlays/Login/LoginForm.cs new file mode 100644 index 0000000000..9d229c2b3e --- /dev/null +++ b/osu.Game/Overlays/Login/LoginForm.cs @@ -0,0 +1,108 @@ +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; +using osu.Game.Configuration; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; +using osu.Game.Overlays.Settings; +using osuTK; + +namespace osu.Game.Overlays.Login +{ + public class LoginForm : FillFlowContainer + { + private TextBox username; + private TextBox password; + private ShakeContainer shakeSignIn; + + [Resolved(CanBeNull = true)] + private IAPIProvider api { get; set; } + + public Action RequestHide; + + private void performLogin() + { + if (!string.IsNullOrEmpty(username.Text) && !string.IsNullOrEmpty(password.Text)) + api?.Login(username.Text, password.Text); + else + shakeSignIn.Shake(); + } + + [BackgroundDependencyLoader(permitNulls: true)] + private void load(OsuConfigManager config, AccountCreationOverlay accountCreation) + { + Direction = FillDirection.Vertical; + Spacing = new Vector2(0, 5); + AutoSizeAxes = Axes.Y; + RelativeSizeAxes = Axes.X; + Children = new Drawable[] + { + username = new OsuTextBox + { + PlaceholderText = "username", + RelativeSizeAxes = Axes.X, + Text = api?.ProvidedUsername ?? string.Empty, + TabbableContentContainer = this + }, + password = new OsuPasswordTextBox + { + PlaceholderText = "password", + RelativeSizeAxes = Axes.X, + TabbableContentContainer = this, + }, + new SettingsCheckbox + { + LabelText = "Remember username", + Current = config.GetBindable(OsuSetting.SaveUsername), + }, + new SettingsCheckbox + { + LabelText = "Stay signed in", + Current = config.GetBindable(OsuSetting.SavePassword), + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + shakeSignIn = new ShakeContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Child = new SettingsButton + { + Text = "Sign in", + Action = performLogin + }, + } + } + }, + new SettingsButton + { + Text = "Register", + Action = () => + { + RequestHide(); + accountCreation.Show(); + } + } + }; + + password.OnCommit += (sender, newText) => performLogin(); + } + + public override bool AcceptsFocus => true; + + protected override bool OnClick(ClickEvent e) => true; + + protected override void OnFocus(FocusEvent e) + { + Schedule(() => { GetContainingInputManager().ChangeFocus(string.IsNullOrEmpty(username.Text) ? username : password); }); + } + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/Login/LoginPanel.cs b/osu.Game/Overlays/Login/LoginPanel.cs new file mode 100644 index 0000000000..d1e5bfe809 --- /dev/null +++ b/osu.Game/Overlays/Login/LoginPanel.cs @@ -0,0 +1,199 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Events; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; +using osu.Game.Users; +using osuTK; +using RectangleF = osu.Framework.Graphics.Primitives.RectangleF; +using Container = osu.Framework.Graphics.Containers.Container; + +namespace osu.Game.Overlays.Login +{ + public class LoginPanel : FillFlowContainer + { + private bool bounding = true; + private LoginForm form; + + [Resolved] + private OsuColour colours { get; set; } + + private UserGridPanel panel; + private UserDropdown dropdown; + + /// + /// Called to request a hide of a parent displaying this container. + /// + public Action RequestHide; + + private readonly IBindable apiState = new Bindable(); + + [Resolved] + private IAPIProvider api { get; set; } + + public override RectangleF BoundingBox => bounding ? base.BoundingBox : RectangleF.Empty; + + public bool Bounding + { + get => bounding; + set + { + bounding = value; + Invalidate(Invalidation.MiscGeometry); + } + } + + public LoginPanel() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Direction = FillDirection.Vertical; + Spacing = new Vector2(0f, 5f); + } + + [BackgroundDependencyLoader] + private void load() + { + apiState.BindTo(api.State); + apiState.BindValueChanged(onlineStateChanged, true); + } + + private void onlineStateChanged(ValueChangedEvent state) => Schedule(() => + { + form = null; + + switch (state.NewValue) + { + case APIState.Offline: + Children = new Drawable[] + { + new OsuSpriteText + { + Text = "ACCOUNT", + Margin = new MarginPadding { Bottom = 5 }, + Font = OsuFont.GetFont(weight: FontWeight.Bold), + }, + form = new LoginForm + { + RequestHide = RequestHide + } + }; + break; + + case APIState.Failing: + case APIState.Connecting: + LinkFlowContainer linkFlow; + + Children = new Drawable[] + { + new LoadingSpinner + { + State = { Value = Visibility.Visible }, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, + linkFlow = new LinkFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + TextAnchor = Anchor.TopCentre, + AutoSizeAxes = Axes.Both, + Text = state.NewValue == APIState.Failing ? "Connection is failing, will attempt to reconnect... " : "Attempting to connect... ", + Margin = new MarginPadding { Top = 10, Bottom = 10 }, + }, + }; + + linkFlow.AddLink("cancel", api.Logout, string.Empty); + break; + + case APIState.Online: + Children = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Left = 20, Right = 20 }, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0f, 10f), + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new[] + { + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = "Signed in", + Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold), + Margin = new MarginPadding { Top = 5, Bottom = 5 }, + }, + }, + }, + panel = new UserGridPanel(api.LocalUser.Value) + { + RelativeSizeAxes = Axes.X, + Action = RequestHide + }, + dropdown = new UserDropdown { RelativeSizeAxes = Axes.X }, + }, + }, + }; + + panel.Status.BindTo(api.LocalUser.Value.Status); + panel.Activity.BindTo(api.LocalUser.Value.Activity); + + dropdown.Current.BindValueChanged(action => + { + switch (action.NewValue) + { + case UserAction.Online: + api.LocalUser.Value.Status.Value = new UserStatusOnline(); + dropdown.StatusColour = colours.Green; + break; + + case UserAction.DoNotDisturb: + api.LocalUser.Value.Status.Value = new UserStatusDoNotDisturb(); + dropdown.StatusColour = colours.Red; + break; + + case UserAction.AppearOffline: + api.LocalUser.Value.Status.Value = new UserStatusOffline(); + dropdown.StatusColour = colours.Gray7; + break; + + case UserAction.SignOut: + api.Logout(); + break; + } + }, true); + break; + } + + if (form != null) GetContainingInputManager()?.ChangeFocus(form); + }); + + public override bool AcceptsFocus => true; + + protected override bool OnClick(ClickEvent e) => true; + + protected override void OnFocus(FocusEvent e) + { + if (form != null) GetContainingInputManager().ChangeFocus(form); + base.OnFocus(e); + } + } +} diff --git a/osu.Game/Overlays/Login/UserAction.cs b/osu.Game/Overlays/Login/UserAction.cs new file mode 100644 index 0000000000..440d6ea456 --- /dev/null +++ b/osu.Game/Overlays/Login/UserAction.cs @@ -0,0 +1,18 @@ +using System.ComponentModel; + +namespace osu.Game.Overlays.Login +{ + public enum UserAction + { + Online, + + [Description(@"Do not disturb")] + DoNotDisturb, + + [Description(@"Appear offline")] + AppearOffline, + + [Description(@"Sign out")] + SignOut, + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/Login/UserDropdown.cs b/osu.Game/Overlays/Login/UserDropdown.cs new file mode 100644 index 0000000000..80f6c7113b --- /dev/null +++ b/osu.Game/Overlays/Login/UserDropdown.cs @@ -0,0 +1,121 @@ +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Login +{ + public class UserDropdown : OsuEnumDropdown + { + protected override DropdownHeader CreateHeader() => new UserDropdownHeader(); + + protected override DropdownMenu CreateMenu() => new UserDropdownMenu(); + + public Color4 StatusColour + { + set + { + if (Header is UserDropdownHeader h) + h.StatusColour = value; + } + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AccentColour = colours.Gray5; + } + + protected class UserDropdownMenu : OsuDropdownMenu + { + public UserDropdownMenu() + { + Masking = true; + CornerRadius = 5; + + Margin = new MarginPadding { Bottom = 5 }; + + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.25f), + Radius = 4, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundColour = colours.Gray3; + } + + protected override DrawableDropdownMenuItem CreateDrawableDropdownMenuItem(MenuItem item) => new DrawableUserDropdownMenuItem(item); + + private class DrawableUserDropdownMenuItem : DrawableOsuDropdownMenuItem + { + public DrawableUserDropdownMenuItem(MenuItem item) + : base(item) + { + Foreground.Padding = new MarginPadding { Top = 5, Bottom = 5, Left = 10, Right = 5 }; + CornerRadius = 5; + } + + protected override Drawable CreateContent() => new Content + { + Label = { Margin = new MarginPadding { Left = UserDropdownHeader.LABEL_LEFT_MARGIN - 11 } } + }; + } + } + + private class UserDropdownHeader : OsuDropdownHeader + { + public const float LABEL_LEFT_MARGIN = 20; + + private readonly SpriteIcon statusIcon; + + public Color4 StatusColour + { + set => statusIcon.FadeColour(value, 500, Easing.OutQuint); + } + + public UserDropdownHeader() + { + Foreground.Padding = new MarginPadding { Left = 10, Right = 10 }; + Margin = new MarginPadding { Bottom = 5 }; + Masking = true; + CornerRadius = 5; + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.25f), + Radius = 4, + }; + + Icon.Size = new Vector2(14); + Icon.Margin = new MarginPadding(0); + + Foreground.Add(statusIcon = new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Icon = FontAwesome.Regular.Circle, + Size = new Vector2(14), + }); + + Text.Margin = new MarginPadding { Left = LABEL_LEFT_MARGIN }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundColour = colours.Gray3; + } + } + } +} diff --git a/osu.Game/Overlays/LoginOverlay.cs b/osu.Game/Overlays/LoginOverlay.cs index e7caaa3aca..f3562aa6d9 100644 --- a/osu.Game/Overlays/LoginOverlay.cs +++ b/osu.Game/Overlays/LoginOverlay.cs @@ -5,17 +5,17 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; -using osu.Game.Overlays.Settings.Sections.General; using osuTK.Graphics; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; +using osu.Game.Overlays.Login; namespace osu.Game.Overlays { public class LoginOverlay : OsuFocusedOverlayContainer { - private LoginSettings settingsSection; + private LoginPanel panel; private const float transition_time = 400; @@ -50,7 +50,7 @@ namespace osu.Game.Overlays AutoSizeEasing = Easing.OutQuint, Children = new Drawable[] { - settingsSection = new LoginSettings + panel = new LoginPanel { Padding = new MarginPadding(10), RequestHide = Hide, @@ -75,17 +75,17 @@ namespace osu.Game.Overlays { base.PopIn(); - settingsSection.Bounding = true; + panel.Bounding = true; this.FadeIn(transition_time, Easing.OutQuint); - GetContainingInputManager().ChangeFocus(settingsSection); + GetContainingInputManager().ChangeFocus(panel); } protected override void PopOut() { base.PopOut(); - settingsSection.Bounding = false; + panel.Bounding = false; this.FadeOut(transition_time); } } diff --git a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs deleted file mode 100644 index 8f757f7a36..0000000000 --- a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs +++ /dev/null @@ -1,421 +0,0 @@ -// 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 osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.UserInterface; -using osu.Game.Configuration; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API; -using osuTK; -using osu.Game.Users; -using System.ComponentModel; -using osu.Framework.Bindables; -using osu.Game.Graphics; -using osuTK.Graphics; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics.Effects; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.Events; -using osu.Game.Graphics.Containers; -using RectangleF = osu.Framework.Graphics.Primitives.RectangleF; -using Container = osu.Framework.Graphics.Containers.Container; - -namespace osu.Game.Overlays.Settings.Sections.General -{ - public class LoginSettings : FillFlowContainer - { - private bool bounding = true; - private LoginForm form; - - [Resolved] - private OsuColour colours { get; set; } - - private UserGridPanel panel; - private UserDropdown dropdown; - - /// - /// Called to request a hide of a parent displaying this container. - /// - public Action RequestHide; - - private readonly IBindable apiState = new Bindable(); - - [Resolved] - private IAPIProvider api { get; set; } - - public override RectangleF BoundingBox => bounding ? base.BoundingBox : RectangleF.Empty; - - public bool Bounding - { - get => bounding; - set - { - bounding = value; - Invalidate(Invalidation.MiscGeometry); - } - } - - public LoginSettings() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Direction = FillDirection.Vertical; - Spacing = new Vector2(0f, 5f); - } - - [BackgroundDependencyLoader] - private void load() - { - apiState.BindTo(api.State); - apiState.BindValueChanged(onlineStateChanged, true); - } - - private void onlineStateChanged(ValueChangedEvent state) => Schedule(() => - { - form = null; - - switch (state.NewValue) - { - case APIState.Offline: - Children = new Drawable[] - { - new OsuSpriteText - { - Text = "ACCOUNT", - Margin = new MarginPadding { Bottom = 5 }, - Font = OsuFont.GetFont(weight: FontWeight.Bold), - }, - form = new LoginForm - { - RequestHide = RequestHide - } - }; - break; - - case APIState.Failing: - case APIState.Connecting: - LinkFlowContainer linkFlow; - - Children = new Drawable[] - { - new LoadingSpinner - { - State = { Value = Visibility.Visible }, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - }, - linkFlow = new LinkFlowContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - TextAnchor = Anchor.TopCentre, - AutoSizeAxes = Axes.Both, - Text = state.NewValue == APIState.Failing ? "Connection is failing, will attempt to reconnect... " : "Attempting to connect... ", - Margin = new MarginPadding { Top = 10, Bottom = 10 }, - }, - }; - - linkFlow.AddLink("cancel", api.Logout, string.Empty); - break; - - case APIState.Online: - Children = new Drawable[] - { - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Left = 20, Right = 20 }, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0f, 10f), - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new[] - { - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = "Signed in", - Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold), - Margin = new MarginPadding { Top = 5, Bottom = 5 }, - }, - }, - }, - panel = new UserGridPanel(api.LocalUser.Value) - { - RelativeSizeAxes = Axes.X, - Action = RequestHide - }, - dropdown = new UserDropdown { RelativeSizeAxes = Axes.X }, - }, - }, - }; - - panel.Status.BindTo(api.LocalUser.Value.Status); - panel.Activity.BindTo(api.LocalUser.Value.Activity); - - dropdown.Current.BindValueChanged(action => - { - switch (action.NewValue) - { - case UserAction.Online: - api.LocalUser.Value.Status.Value = new UserStatusOnline(); - dropdown.StatusColour = colours.Green; - break; - - case UserAction.DoNotDisturb: - api.LocalUser.Value.Status.Value = new UserStatusDoNotDisturb(); - dropdown.StatusColour = colours.Red; - break; - - case UserAction.AppearOffline: - api.LocalUser.Value.Status.Value = new UserStatusOffline(); - dropdown.StatusColour = colours.Gray7; - break; - - case UserAction.SignOut: - api.Logout(); - break; - } - }, true); - break; - } - - if (form != null) GetContainingInputManager()?.ChangeFocus(form); - }); - - public override bool AcceptsFocus => true; - - protected override bool OnClick(ClickEvent e) => true; - - protected override void OnFocus(FocusEvent e) - { - if (form != null) GetContainingInputManager().ChangeFocus(form); - base.OnFocus(e); - } - - private class LoginForm : FillFlowContainer - { - private TextBox username; - private TextBox password; - private ShakeContainer shakeSignIn; - - [Resolved(CanBeNull = true)] - private IAPIProvider api { get; set; } - - public Action RequestHide; - - private void performLogin() - { - if (!string.IsNullOrEmpty(username.Text) && !string.IsNullOrEmpty(password.Text)) - api?.Login(username.Text, password.Text); - else - shakeSignIn.Shake(); - } - - [BackgroundDependencyLoader(permitNulls: true)] - private void load(OsuConfigManager config, AccountCreationOverlay accountCreation) - { - Direction = FillDirection.Vertical; - Spacing = new Vector2(0, 5); - AutoSizeAxes = Axes.Y; - RelativeSizeAxes = Axes.X; - Children = new Drawable[] - { - username = new OsuTextBox - { - PlaceholderText = "username", - RelativeSizeAxes = Axes.X, - Text = api?.ProvidedUsername ?? string.Empty, - TabbableContentContainer = this - }, - password = new OsuPasswordTextBox - { - PlaceholderText = "password", - RelativeSizeAxes = Axes.X, - TabbableContentContainer = this, - }, - new SettingsCheckbox - { - LabelText = "Remember username", - Current = config.GetBindable(OsuSetting.SaveUsername), - }, - new SettingsCheckbox - { - LabelText = "Stay signed in", - Current = config.GetBindable(OsuSetting.SavePassword), - }, - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - shakeSignIn = new ShakeContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Child = new SettingsButton - { - Text = "Sign in", - Action = performLogin - }, - } - } - }, - new SettingsButton - { - Text = "Register", - Action = () => - { - RequestHide(); - accountCreation.Show(); - } - } - }; - - password.OnCommit += (sender, newText) => performLogin(); - } - - public override bool AcceptsFocus => true; - - protected override bool OnClick(ClickEvent e) => true; - - protected override void OnFocus(FocusEvent e) - { - Schedule(() => { GetContainingInputManager().ChangeFocus(string.IsNullOrEmpty(username.Text) ? username : password); }); - } - } - - private class UserDropdown : OsuEnumDropdown - { - protected override DropdownHeader CreateHeader() => new UserDropdownHeader(); - - protected override DropdownMenu CreateMenu() => new UserDropdownMenu(); - - public Color4 StatusColour - { - set - { - if (Header is UserDropdownHeader h) - h.StatusColour = value; - } - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - AccentColour = colours.Gray5; - } - - private class UserDropdownMenu : OsuDropdownMenu - { - public UserDropdownMenu() - { - Masking = true; - CornerRadius = 5; - - Margin = new MarginPadding { Bottom = 5 }; - - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(0.25f), - Radius = 4, - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - BackgroundColour = colours.Gray3; - } - - protected override DrawableDropdownMenuItem CreateDrawableDropdownMenuItem(MenuItem item) => new DrawableUserDropdownMenuItem(item); - - private class DrawableUserDropdownMenuItem : DrawableOsuDropdownMenuItem - { - public DrawableUserDropdownMenuItem(MenuItem item) - : base(item) - { - Foreground.Padding = new MarginPadding { Top = 5, Bottom = 5, Left = 10, Right = 5 }; - CornerRadius = 5; - } - - protected override Drawable CreateContent() => new Content - { - Label = { Margin = new MarginPadding { Left = UserDropdownHeader.LABEL_LEFT_MARGIN - 11 } } - }; - } - } - - private class UserDropdownHeader : OsuDropdownHeader - { - public const float LABEL_LEFT_MARGIN = 20; - - private readonly SpriteIcon statusIcon; - - public Color4 StatusColour - { - set => statusIcon.FadeColour(value, 500, Easing.OutQuint); - } - - public UserDropdownHeader() - { - Foreground.Padding = new MarginPadding { Left = 10, Right = 10 }; - Margin = new MarginPadding { Bottom = 5 }; - Masking = true; - CornerRadius = 5; - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(0.25f), - Radius = 4, - }; - - Icon.Size = new Vector2(14); - Icon.Margin = new MarginPadding(0); - - Foreground.Add(statusIcon = new SpriteIcon - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Icon = FontAwesome.Regular.Circle, - Size = new Vector2(14), - }); - - Text.Margin = new MarginPadding { Left = LABEL_LEFT_MARGIN }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - BackgroundColour = colours.Gray3; - } - } - } - - private enum UserAction - { - Online, - - [Description(@"Do not disturb")] - DoNotDisturb, - - [Description(@"Appear offline")] - AppearOffline, - - [Description(@"Sign out")] - SignOut, - } - } -} From e49d8d0878c9efc5faebad745171cf02e43a6152 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Sep 2021 17:36:17 +0900 Subject: [PATCH 1998/2442] Add test coverage of login dialog --- .../Visual/Menus/TestSceneLoginPanel.cs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 osu.Game.Tests/Visual/Menus/TestSceneLoginPanel.cs diff --git a/osu.Game.Tests/Visual/Menus/TestSceneLoginPanel.cs b/osu.Game.Tests/Visual/Menus/TestSceneLoginPanel.cs new file mode 100644 index 0000000000..5fdadfc2fb --- /dev/null +++ b/osu.Game.Tests/Visual/Menus/TestSceneLoginPanel.cs @@ -0,0 +1,41 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Testing; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Login; + +namespace osu.Game.Tests.Visual.Menus +{ + [TestFixture] + public class TestSceneLoginPanel : OsuManualInputManagerTestScene + { + private LoginPanel loginPanel; + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("create login dialog", () => + { + Add(loginPanel = new LoginPanel + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Width = 0.5f, + }); + }); + } + + [Test] + public void TestBasicLogin() + { + AddStep("logout", () => API.Logout()); + + AddStep("enter password", () => loginPanel.ChildrenOfType().First().Text = "password"); + AddStep("submit", () => loginPanel.ChildrenOfType().First(b => b.Text.ToString() == "Sign in").TriggerClick()); + } + } +} From 2d3913120259fdb34311c423bc691828d2d99c58 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Sep 2021 18:41:03 +0900 Subject: [PATCH 1999/2442] Refactor taiko flashlight a bit --- .../Mods/TaikoModFlashlight.cs | 17 +++++++++-------- osu.Game/Rulesets/Mods/ModFlashlight.cs | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs index 3578e3a734..19ba99586b 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs @@ -36,24 +36,25 @@ namespace osu.Game.Rulesets.Taiko.Mods public TaikoFlashlight(TaikoPlayfield taikoPlayfield) { this.taikoPlayfield = taikoPlayfield; - FlashlightSize = new Vector2(0, getSizeFor(0)); + FlashlightSize = getSizeFor(0); AddLayout(flashlightProperties); } - private float getSizeFor(int combo) + private Vector2 getSizeFor(int combo) { if (combo > 200) - return default_flashlight_size * 0.8f; - else if (combo > 100) - return default_flashlight_size * 0.9f; - else - return default_flashlight_size; + return new Vector2(0, default_flashlight_size * 0.8f); + + if (combo > 100) + return new Vector2(0, default_flashlight_size * 0.9f); + + return new Vector2(0, default_flashlight_size); } protected override void OnComboChange(ValueChangedEvent e) { - this.TransformTo(nameof(FlashlightSize), new Vector2(0, getSizeFor(e.NewValue)), FLASHLIGHT_FADE_DURATION); + this.TransformTo(nameof(FlashlightSize), getSizeFor(e.NewValue), FLASHLIGHT_FADE_DURATION); } protected override string FragmentShader => "CircularFlashlight"; diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index 7abae71273..f96f8a3c1b 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Mods public abstract class Flashlight : Drawable { - internal BindableInt Combo; + protected internal BindableInt Combo { get; internal set; } private IShader shader; protected override DrawNode CreateDrawNode() => new FlashlightDrawNode(this); From 35c3d75cb8d720583e7b010ff026fdd7885b773c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Sep 2021 18:51:43 +0900 Subject: [PATCH 2000/2442] Preserve flashlight size through aspect adjustment --- .../Mods/TaikoModFlashlight.cs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs index 19ba99586b..12f19a0086 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs @@ -43,13 +43,17 @@ namespace osu.Game.Rulesets.Taiko.Mods private Vector2 getSizeFor(int combo) { + Vector2 size; + if (combo > 200) - return new Vector2(0, default_flashlight_size * 0.8f); + size = new Vector2(0, default_flashlight_size * 0.8f); + else if (combo > 100) + size = new Vector2(0, default_flashlight_size * 0.9f); + else + size = new Vector2(0, default_flashlight_size); - if (combo > 100) - return new Vector2(0, default_flashlight_size * 0.9f); - - return new Vector2(0, default_flashlight_size); + // Preserve flashlight size through the playfield's aspect adjustment. + return size * taikoPlayfield.DrawHeight / TaikoPlayfield.DEFAULT_HEIGHT; } protected override void OnComboChange(ValueChangedEvent e) @@ -66,6 +70,10 @@ namespace osu.Game.Rulesets.Taiko.Mods if (!flashlightProperties.IsValid) { FlashlightPosition = ToLocalSpace(taikoPlayfield.HitTarget.ScreenSpaceDrawQuad.Centre); + + ClearTransforms(targetMember: nameof(FlashlightSize)); + FlashlightSize = getSizeFor(Combo.Value); + flashlightProperties.Validate(); } } From a743a3f3067e375516a0d1bd7c8a37b93400c7cf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Sep 2021 19:15:14 +0900 Subject: [PATCH 2001/2442] Change combo bind logic to be non-weird --- osu.Game/Rulesets/Mods/ModFlashlight.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index f96f8a3c1b..a77a83b36c 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -69,9 +69,11 @@ namespace osu.Game.Rulesets.Mods public virtual void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { var flashlight = CreateFlashlight(); - flashlight.Combo = Combo; + flashlight.RelativeSizeAxes = Axes.Both; flashlight.Colour = Color4.Black; + + flashlight.Combo.BindTo(Combo); drawableRuleset.KeyBindingInputManager.Add(flashlight); flashlight.Breaks = drawableRuleset.Beatmap.Breaks; @@ -81,7 +83,8 @@ namespace osu.Game.Rulesets.Mods public abstract class Flashlight : Drawable { - protected internal BindableInt Combo { get; internal set; } + public readonly BindableInt Combo = new BindableInt(); + private IShader shader; protected override DrawNode CreateDrawNode() => new FlashlightDrawNode(this); From be7346d0b76febd7e741e0fe6f97518d3140d2c0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Sep 2021 19:18:37 +0900 Subject: [PATCH 2002/2442] Refactor `getSizeFor` to read a touch better --- osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs index 12f19a0086..0a325f174e 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs @@ -43,17 +43,15 @@ namespace osu.Game.Rulesets.Taiko.Mods private Vector2 getSizeFor(int combo) { - Vector2 size; + float size = default_flashlight_size; if (combo > 200) - size = new Vector2(0, default_flashlight_size * 0.8f); + size *= 0.8f; else if (combo > 100) - size = new Vector2(0, default_flashlight_size * 0.9f); - else - size = new Vector2(0, default_flashlight_size); + size *= 0.9f; // Preserve flashlight size through the playfield's aspect adjustment. - return size * taikoPlayfield.DrawHeight / TaikoPlayfield.DEFAULT_HEIGHT; + return new Vector2(0, size * taikoPlayfield.DrawHeight / TaikoPlayfield.DEFAULT_HEIGHT); } protected override void OnComboChange(ValueChangedEvent e) From 9485323a13755b5b9736525bb3a0d90a74f8d2c3 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 17 Sep 2021 20:52:13 +0900 Subject: [PATCH 2003/2442] Add audio feedback for incorrect MP room password --- osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index a823db282c..70dc14951c 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -189,9 +189,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private OsuPasswordTextBox passwordTextbox; private TriangleButton joinButton; private OsuSpriteText errorText; + private Sample sampleJoinFail; [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, AudioManager audio) { Child = new FillFlowContainer { @@ -227,6 +228,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge } }; + sampleJoinFail = audio.Samples.Get(@"UI/password-fail"); + joinButton.Action = () => lounge?.Join(room, passwordTextbox.Text, null, joinFailed); } @@ -244,6 +247,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge .FadeOutFromOne(1000, Easing.In); Body.Shake(); + + Schedule(() => { sampleJoinFail?.Play(); }); } protected override void LoadComplete() From f868feae44044af55106fc9789fb4de637b427f8 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 17 Sep 2021 21:12:39 +0900 Subject: [PATCH 2004/2442] Remove unnecessary Schedule --- osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index 70dc14951c..fe7c7cc364 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -248,7 +248,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge Body.Shake(); - Schedule(() => { sampleJoinFail?.Play(); }); + sampleJoinFail?.Play(); } protected override void LoadComplete() From cc11532d9b48421b5dbbd6915134ef75115f597e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 18 Sep 2021 03:08:42 +0900 Subject: [PATCH 2005/2442] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 0e60a5a99e..4859510e6c 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 390b026497..0460de6d72 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 2158772b83..51ca381b63 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From b2b3108afa84f7c156c00876ee75390f8933a2b2 Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Fri, 17 Sep 2021 16:19:41 -0400 Subject: [PATCH 2006/2442] Resolve addressed issues + Stopped using testing methods in non-testing classes + Resolve Player and add OnSeek event + Take bindings away from BarHitErrorMeter + Add support for ColourHitErrorMeter --- .idea/.idea.osu.Desktop/.idea/discord.xml | 7 +++++ .../HUD/HitErrorMeters/BarHitErrorMeter.cs | 31 ++----------------- .../HUD/HitErrorMeters/ColourHitErrorMeter.cs | 2 ++ .../Play/HUD/HitErrorMeters/HitErrorMeter.cs | 8 +++++ osu.Game/Screens/Play/Player.cs | 8 ++++- 5 files changed, 26 insertions(+), 30 deletions(-) create mode 100644 .idea/.idea.osu.Desktop/.idea/discord.xml diff --git a/.idea/.idea.osu.Desktop/.idea/discord.xml b/.idea/.idea.osu.Desktop/.idea/discord.xml new file mode 100644 index 0000000000..30bab2abb1 --- /dev/null +++ b/.idea/.idea.osu.Desktop/.idea/discord.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 2854fabbae..2b5228cab0 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -10,10 +10,7 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.Bindings; -using osu.Framework.Testing; using osu.Game.Graphics; -using osu.Game.Input.Bindings; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using osuTK; @@ -21,7 +18,7 @@ using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD.HitErrorMeters { - public class BarHitErrorMeter : HitErrorMeter, IKeyBindingHandler + public class BarHitErrorMeter : HitErrorMeter { private const int arrow_move_duration = 400; @@ -144,10 +141,6 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters arrow.Alpha = 0; arrow.Delay(200).FadeInFromZero(600); - - var progressBar = Parent.ChildrenOfType().FirstOrDefault(); - if (progressBar != null) - progressBar.Bar.OnSeek += _ => handleSeek(); } private void createColourBars(OsuColour colours) @@ -287,26 +280,6 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters } } - private void handleSeek() - { - judgementsContainer.Clear(true); - } - - public bool OnPressed(GlobalAction action) - { - switch (action) - { - case GlobalAction.SeekReplayBackward: - case GlobalAction.SeekReplayForward: - handleSeek(); - return false; - } - - return false; - } - - public void OnReleased(GlobalAction action) - { - } + public override void Clear() => judgementsContainer.Clear(true); } } diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs index dda2a6da95..ea64d1f4d9 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs @@ -33,6 +33,8 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters judgementsFlow.Push(GetColourForHitResult(judgement.Type)); } + public override void Clear() => judgementsFlow.Clear(true); + private class JudgementFlow : FillFlowContainer { private const int max_available_judgements = 20; diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs index 788ba5be8c..22ae3900d6 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs @@ -22,6 +22,9 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters [Resolved] private OsuColour colours { get; set; } + [Resolved(canBeNull: true)] + private Player player { get; set; } + public bool UsesFixedAnchor { get; set; } [BackgroundDependencyLoader(true)] @@ -34,6 +37,9 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters { base.LoadComplete(); + if (player != null) + player.OnSeek += Clear; + processor.NewJudgement += OnNewJudgement; } @@ -67,6 +73,8 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters } } + public abstract void Clear(); + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index e8a2790c94..f0544d5fcd 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -69,6 +69,8 @@ namespace osu.Game.Screens.Play public Action RestartRequested; + public Action OnSeek; + public bool HasFailed { get; private set; } private Bindable mouseWheelDisabled; @@ -584,7 +586,11 @@ namespace osu.Game.Screens.Play /// Seek to a specific time in gameplay. /// /// The destination time to seek to. - public void Seek(double time) => GameplayClockContainer.Seek(time); + public void Seek(double time) + { + GameplayClockContainer.Seek(time); + OnSeek.Invoke(); + } private ScheduledDelegate frameStablePlaybackResetDelegate; From 79438c19a45eecb5c0983e6a14efb98539e3686c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 18 Sep 2021 16:27:30 +0200 Subject: [PATCH 2007/2442] Fix slider parts not reproxying after first hitobject freed --- .../Objects/Drawables/DrawableSlider.cs | 2 +- .../Skinning/Legacy/LegacyReverseArrow.cs | 29 +++++++++++++++++-- .../Legacy/LegacySliderHeadHitCircle.cs | 27 +++++++++++++++-- 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 0e1d1043e3..3acec4498d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -187,7 +187,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables repeatContainer.Clear(false); tickContainer.Clear(false); - OverlayElementContainer.Clear(); + OverlayElementContainer.Clear(false); } protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs index b6956693b6..9e4bd258bd 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -17,12 +18,14 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy [Resolved(canBeNull: true)] private DrawableHitObject drawableHitObject { get; set; } + private Drawable proxy; + public LegacyReverseArrow(ISkin skin) { this.skin = skin; } - [BackgroundDependencyLoader] + [BackgroundDependencyLoader(true)] private void load() { AutoSizeAxes = Axes.Both; @@ -36,9 +39,29 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { base.LoadComplete(); + proxy = CreateProxy(); + + if (drawableHitObject != null) + { + drawableHitObject.HitObjectApplied += onHitObjectApplied; + onHitObjectApplied(drawableHitObject); + } + } + + private void onHitObjectApplied(DrawableHitObject drawableObject) + { + Debug.Assert(proxy.Parent == null); + // see logic in LegacySliderHeadHitCircle. - (drawableHitObject as DrawableSliderRepeat)?.DrawableSlider - .OverlayElementContainer.Add(CreateProxy()); + (drawableObject as DrawableSliderRepeat)?.DrawableSlider + .OverlayElementContainer.Add(proxy); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + if (drawableHitObject != null) + drawableHitObject.HitObjectApplied -= onHitObjectApplied; } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderHeadHitCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderHeadHitCircle.cs index 83ebdafa50..13ba42ba50 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderHeadHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderHeadHitCircle.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Rulesets.Objects.Drawables; @@ -13,6 +14,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy [Resolved(canBeNull: true)] private DrawableHitObject drawableHitObject { get; set; } + private Drawable proxiedHitCircleOverlay; + public LegacySliderHeadHitCircle() : base("sliderstartcircle") { @@ -21,10 +24,30 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy protected override void LoadComplete() { base.LoadComplete(); + proxiedHitCircleOverlay = HitCircleOverlay.CreateProxy(); + + if (drawableHitObject != null) + { + drawableHitObject.HitObjectApplied += onHitObjectApplied; + onHitObjectApplied(drawableHitObject); + } + } + + private void onHitObjectApplied(DrawableHitObject drawableObject) + { + Debug.Assert(proxiedHitCircleOverlay.Parent == null); // see logic in LegacyReverseArrow. - (drawableHitObject as DrawableSliderHead)?.DrawableSlider - .OverlayElementContainer.Add(HitCircleOverlay.CreateProxy().With(d => d.Depth = float.MinValue)); + (drawableObject as DrawableSliderHead)?.DrawableSlider + .OverlayElementContainer.Add(proxiedHitCircleOverlay.With(d => d.Depth = float.MinValue)); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (drawableHitObject != null) + drawableHitObject.HitObjectApplied -= onHitObjectApplied; } } } From 59657aca9a5e42bef22d93ceb8053557f8bbf8cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 18 Sep 2021 16:28:25 +0200 Subject: [PATCH 2008/2442] Remove redundant qualifier --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs index 9e4bd258bd..298079fa6c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy string lookupName = new OsuSkinComponent(OsuSkinComponents.ReverseArrow).LookupName; - InternalChild = skin.GetAnimation(lookupName, true, true) ?? Drawable.Empty(); + InternalChild = skin.GetAnimation(lookupName, true, true) ?? Empty(); } protected override void LoadComplete() From c23354bb67fc63d54e490d195436d5adfa1441a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 18 Sep 2021 16:28:44 +0200 Subject: [PATCH 2009/2442] Remove unused setter --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs index 298079fa6c..ab774a2b67 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { public class LegacyReverseArrow : CompositeDrawable { - private ISkin skin { get; set; } + private ISkin skin { get; } [Resolved(canBeNull: true)] private DrawableHitObject drawableHitObject { get; set; } From 680484bb165361ef1b6ea81562266d190937268d Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Sat, 18 Sep 2021 12:04:25 -0400 Subject: [PATCH 2010/2442] Remove discord.xml Not sure how that slipped in there, but it's gone now! --- .idea/.idea.osu.Desktop/.idea/discord.xml | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 .idea/.idea.osu.Desktop/.idea/discord.xml diff --git a/.idea/.idea.osu.Desktop/.idea/discord.xml b/.idea/.idea.osu.Desktop/.idea/discord.xml deleted file mode 100644 index 30bab2abb1..0000000000 --- a/.idea/.idea.osu.Desktop/.idea/discord.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - \ No newline at end of file From 12cc16c598f4bab59c0ab0e805ada327c4ca4c18 Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Sat, 18 Sep 2021 12:05:06 -0400 Subject: [PATCH 2011/2442] Remove unused property in `SongProgress` --- osu.Game/Screens/Play/SongProgress.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index dff2dcc86b..b27a9c5f5d 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -35,8 +35,6 @@ namespace osu.Game.Screens.Play private readonly SongProgressGraph graph; private readonly SongProgressInfo info; - public SongProgressBar Bar => bar; - public Action RequestSeek; /// From f6e279baa1764a876884341a66f3af6ab05afd5d Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Sat, 18 Sep 2021 12:18:11 -0400 Subject: [PATCH 2012/2442] Add xmldoc to HitErrorMeter.Clear Explains how the method is called and what inheritors should do when implementing it. --- osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs index 22ae3900d6..1871519ab5 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs @@ -73,6 +73,10 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters } } + /// + /// Invoked by when the active seeks through the current beatmap. + /// Any inheritors of should have this method clear their container that displays the hit error results. + /// public abstract void Clear(); protected override void Dispose(bool isDisposing) From 04715a54719766a519287f1bf58538f3d0414d0c Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Sat, 18 Sep 2021 12:20:36 -0400 Subject: [PATCH 2013/2442] Add null-check when invoking OnSeek --- osu.Game/Screens/Play/Player.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index f0544d5fcd..e982d02baf 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -589,7 +589,7 @@ namespace osu.Game.Screens.Play public void Seek(double time) { GameplayClockContainer.Seek(time); - OnSeek.Invoke(); + OnSeek?.Invoke(); } private ScheduledDelegate frameStablePlaybackResetDelegate; From 36237398fa307f6939306df065e897b4683cf740 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 18 Sep 2021 18:24:36 +0200 Subject: [PATCH 2014/2442] Remove accidental leftover nullable BDL spec --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs index ab774a2b67..fd7bfe7e60 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy this.skin = skin; } - [BackgroundDependencyLoader(true)] + [BackgroundDependencyLoader] private void load() { AutoSizeAxes = Axes.Both; From 846cde53b3dd7c2dd79034cb0ef2e214228c976a Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sat, 18 Sep 2021 22:54:12 +0200 Subject: [PATCH 2015/2442] Add `RelativePositionAxes` support --- .../Gameplay/TestSceneParticleSpewer.cs | 25 +++++++++----- osu.Game/Graphics/ParticleSpewer.cs | 33 ++++++++++++++----- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs index c259718a7a..31cc505a0d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs @@ -28,8 +28,12 @@ namespace osu.Game.Tests.Visual.Gameplay Child = spewer = createSpewer(); AddToggleStep("toggle spawning", value => spewer.Active.Value = value); - AddSliderStep("particle gravity", 0f, 250f, 0f, value => spewer.Gravity = value); - AddSliderStep("particle velocity", 0f, 500f, 250f, value => spewer.MaxVelocity = value); + AddSliderStep("particle gravity", 0f, 1f, 0f, value => spewer.Gravity = value); + AddSliderStep("particle velocity", 0f, 1f, 0.25f, value => spewer.MaxVelocity = value); + AddStep("move to new location", () => + { + spewer.TransformTo(nameof(spewer.SpawnPosition), new Vector2(RNG.NextSingle(), RNG.NextSingle()), 1000, Easing.Out); + }); } [SetUpSteps] @@ -51,14 +55,15 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("is not present", () => !spewer.IsPresent); } - private TestParticleSpewer createSpewer() - { - return new TestParticleSpewer(skinManager.DefaultLegacySkin.GetTexture("star2")) + private TestParticleSpewer createSpewer() => + new TestParticleSpewer(skinManager.DefaultLegacySkin.GetTexture("star2")) { - Anchor = Anchor.Centre, Origin = Anchor.Centre, + RelativePositionAxes = Axes.Both, + RelativeSizeAxes = Axes.Both, + Position = new Vector2(0.5f), + Size = new Vector2(0.5f), }; - } private class TestParticleSpewer : ParticleSpewer { @@ -66,7 +71,10 @@ namespace osu.Game.Tests.Visual.Gameplay private const int rate = 250; public float Gravity; - public float MaxVelocity = 250; + + public float MaxVelocity = 0.25f; + + public Vector2 SpawnPosition { get; set; } = new Vector2(0.5f); protected override float ParticleGravity => Gravity; @@ -82,6 +90,7 @@ namespace osu.Game.Tests.Visual.Gameplay RNG.NextSingle(-MaxVelocity, MaxVelocity), RNG.NextSingle(-MaxVelocity, MaxVelocity) ), + StartPosition = SpawnPosition, Duration = RNG.NextSingle(lifetime), StartAngle = RNG.NextSingle(MathF.PI * 2), EndAngle = RNG.NextSingle(MathF.PI * 2), diff --git a/osu.Game/Graphics/ParticleSpewer.cs b/osu.Game/Graphics/ParticleSpewer.cs index c022fd4598..6bf9bff05a 100644 --- a/osu.Game/Graphics/ParticleSpewer.cs +++ b/osu.Game/Graphics/ParticleSpewer.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Bindables; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Vertices; using osu.Framework.Graphics.Primitives; @@ -83,6 +84,8 @@ namespace osu.Game.Graphics private float currentTime; private float gravity; + private Axes relativePositionAxes; + private Vector2 sourceSize; public ParticleSpewerDrawNode(Sprite source) : base(source) @@ -98,6 +101,8 @@ namespace osu.Game.Graphics currentTime = (float)Source.Time.Current; gravity = Source.ParticleGravity; + relativePositionAxes = Source.RelativePositionAxes; + sourceSize = Source.DrawSize; } protected override void Blit(Action vertexAction) @@ -116,18 +121,11 @@ namespace osu.Game.Graphics var alpha = p.AlphaAtTime(timeSinceStart); if (alpha <= 0) continue; - var scale = p.ScaleAtTime(timeSinceStart); var pos = p.PositionAtTime(timeSinceStart, gravity); + var scale = p.ScaleAtTime(timeSinceStart); var angle = p.AngleAtTime(timeSinceStart); - var width = Texture.DisplayWidth * scale; - var height = Texture.DisplayHeight * scale; - - var rect = new RectangleF( - pos.X - width / 2, - pos.Y - height / 2, - width, - height); + var rect = createDrawRect(pos, scale); var quad = new Quad( transformPosition(rect.TopLeft, rect.Centre, angle), @@ -142,6 +140,23 @@ namespace osu.Game.Graphics } } + private RectangleF createDrawRect(Vector2 position, float scale) + { + var width = Texture.DisplayWidth * scale; + var height = Texture.DisplayHeight * scale; + + if (relativePositionAxes.HasFlagFast(Axes.X)) + position.X *= sourceSize.X; + if (relativePositionAxes.HasFlagFast(Axes.Y)) + position.Y *= sourceSize.Y; + + return new RectangleF( + position.X - width / 2, + position.Y - height / 2, + width, + height); + } + private Vector2 transformPosition(Vector2 pos, Vector2 centre, float angle) { float cos = MathF.Cos(angle); From ef530ed87cc984d2d0b23e8f78ae3c464d04c984 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sat, 18 Sep 2021 23:45:58 +0200 Subject: [PATCH 2016/2442] Normalize particle velocity based on max duration --- .../Visual/Gameplay/TestSceneParticleSpewer.cs | 2 +- osu.Game/Graphics/ParticleSpewer.cs | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs index 31cc505a0d..086b1c2ac2 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs @@ -29,7 +29,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddToggleStep("toggle spawning", value => spewer.Active.Value = value); AddSliderStep("particle gravity", 0f, 1f, 0f, value => spewer.Gravity = value); - AddSliderStep("particle velocity", 0f, 1f, 0.25f, value => spewer.MaxVelocity = value); + AddSliderStep("particle velocity", 0f, 1f, 0.5f, value => spewer.MaxVelocity = value); AddStep("move to new location", () => { spewer.TransformTo(nameof(spewer.SpawnPosition), new Vector2(RNG.NextSingle(), RNG.NextSingle()), 1000, Easing.Out); diff --git a/osu.Game/Graphics/ParticleSpewer.cs b/osu.Game/Graphics/ParticleSpewer.cs index 6bf9bff05a..1ad4672238 100644 --- a/osu.Game/Graphics/ParticleSpewer.cs +++ b/osu.Game/Graphics/ParticleSpewer.cs @@ -82,6 +82,8 @@ namespace osu.Game.Graphics protected new ParticleSpewer Source => (ParticleSpewer)base.Source; + private readonly float maxLifetime; + private float currentTime; private float gravity; private Axes relativePositionAxes; @@ -91,6 +93,7 @@ namespace osu.Game.Graphics : base(source) { particles = new FallingParticle[Source.particles.Length]; + maxLifetime = (float)Source.maxLifetime; } public override void ApplyState() @@ -121,7 +124,7 @@ namespace osu.Game.Graphics var alpha = p.AlphaAtTime(timeSinceStart); if (alpha <= 0) continue; - var pos = p.PositionAtTime(timeSinceStart, gravity); + var pos = p.PositionAtTime(timeSinceStart, gravity, maxLifetime); var scale = p.ScaleAtTime(timeSinceStart); var angle = p.AngleAtTime(timeSinceStart); @@ -187,12 +190,12 @@ namespace osu.Game.Graphics public float AngleAtTime(float timeSinceStart) => StartAngle + (EndAngle - StartAngle) * progressAtTime(timeSinceStart); - public Vector2 PositionAtTime(float timeSinceStart, float gravity) + public Vector2 PositionAtTime(float timeSinceStart, float gravity, float maxDuration) { var progress = progressAtTime(timeSinceStart); - var currentGravity = new Vector2(0, gravity * Duration / 1000 * progress); + var currentGravity = new Vector2(0, gravity * Duration / maxDuration * progress); - return StartPosition + (Velocity + currentGravity) * timeSinceStart / 1000; + return StartPosition + (Velocity + currentGravity) * timeSinceStart / maxDuration; } private float progressAtTime(float timeSinceStart) => Math.Clamp(timeSinceStart / Duration, 0, 1); From 3f8454cb76cc5f2d0c52f8270a27a843bab366eb Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 19 Sep 2021 03:19:16 +0200 Subject: [PATCH 2017/2442] Remove abstract from `ParticleSpewer` --- .../Skinning/Legacy/LegacyCursorParticles.cs | 218 ++++++++---------- .../Gameplay/TestSceneParticleSpewer.cs | 62 ++--- osu.Game/Graphics/ParticleSpewer.cs | 48 ++-- 3 files changed, 144 insertions(+), 184 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index 858ba98b86..d4e5bdd46f 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -1,11 +1,11 @@ // 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.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Textures; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; @@ -20,12 +20,17 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Skinning.Legacy { - public class LegacyCursorParticles : CompositeDrawable, IKeyBindingHandler + public class LegacyCursorParticles : CompositeDrawable, IKeyBindingHandler, IRequireHighFrequencyMousePosition { + private const int particle_lifetime_min = 300; + private const int particle_lifetime_max = 1000; + private const float particle_gravity = 240; + public bool Active => breakSpewer?.Active.Value == true || kiaiSpewer?.Active.Value == true; - private LegacyCursorParticleSpewer breakSpewer; - private LegacyCursorParticleSpewer kiaiSpewer; + private Vector2 cursorVelocity; + private ParticleSpewer breakSpewer; + private ParticleSpewer kiaiSpewer; [Resolved(canBeNull: true)] private Player player { get; set; } @@ -45,21 +50,25 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy texture.ScaleAdjust *= 1.6f; } + RelativeSizeAxes = Axes.Both; + Anchor = Anchor.Centre; InternalChildren = new[] { - breakSpewer = new LegacyCursorParticleSpewer(texture, 20) + breakSpewer = new ParticleSpewer(texture, 20, particle_lifetime_max) { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = Vector2.One, Colour = starBreakAdditive, - Direction = SpewDirection.None, + ParticleGravity = particle_gravity, + CreateParticle = createBreakParticle, }, - kiaiSpewer = new LegacyCursorParticleSpewer(texture, 60) + kiaiSpewer = new ParticleSpewer(texture, 60, particle_lifetime_max) { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = Vector2.One, Colour = starBreakAdditive, - Direction = SpewDirection.None, + ParticleGravity = particle_gravity, + CreateParticle = createParticle, }, }; @@ -85,6 +94,39 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy kiaiSpewer.Active.Value = kiaiHitObject != null; } + private Vector2? cursorScreenPosition; + + private const double max_velocity_frame_length = 15; + private double velocityFrameLength; + private Vector2 totalPosDifference; + + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; + + protected override bool OnMouseMove(MouseMoveEvent e) + { + if (cursorScreenPosition == null) + { + cursorScreenPosition = e.ScreenSpaceMousePosition; + return base.OnMouseMove(e); + } + + // calculate cursor velocity. + totalPosDifference += e.ScreenSpaceMousePosition - cursorScreenPosition.Value; + cursorScreenPosition = e.ScreenSpaceMousePosition; + + velocityFrameLength += Math.Abs(Clock.ElapsedFrameTime); + + if (velocityFrameLength > max_velocity_frame_length) + { + cursorVelocity = totalPosDifference / (float)velocityFrameLength; + + totalPosDifference = Vector2.Zero; + velocityFrameLength = 0; + } + + return base.OnMouseMove(e); + } + public bool OnPressed(OsuAction action) { handleInput(action, true); @@ -111,125 +153,53 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy rightPressed = pressed; break; } + } + + private ParticleSpewer.FallingParticle? createParticle() + { + if (!cursorScreenPosition.HasValue) return null; + + return new ParticleSpewer.FallingParticle + { + StartPosition = ToLocalSpace(cursorScreenPosition.Value), + Duration = RNG.NextSingle(particle_lifetime_min, particle_lifetime_max), + StartAngle = (float)(RNG.NextDouble() * 4 - 2), + EndAngle = RNG.NextSingle(-2f, 2f), + EndScale = RNG.NextSingle(2f), + Velocity = cursorVelocity * 40, + }; + } + + private ParticleSpewer.FallingParticle? createBreakParticle() + { + var baseParticle = createParticle(); + if (!baseParticle.HasValue) return null; + + var p = baseParticle.Value; if (leftPressed && rightPressed) - breakSpewer.Direction = SpewDirection.Omni; + { + p.Velocity += new Vector2( + RNG.NextSingle(-460f, 460f), + RNG.NextSingle(-160f, 160f) + ); + } else if (leftPressed) - breakSpewer.Direction = SpewDirection.Left; + { + p.Velocity += new Vector2( + RNG.NextSingle(-460f, 0), + RNG.NextSingle(-40f, 40f) + ); + } else if (rightPressed) - breakSpewer.Direction = SpewDirection.Right; - else - breakSpewer.Direction = SpewDirection.None; - } - - private class LegacyCursorParticleSpewer : ParticleSpewer, IRequireHighFrequencyMousePosition - { - private const int particle_lifetime_min = 300; - private const int particle_lifetime_max = 1000; - - public SpewDirection Direction { get; set; } - - protected override bool CanSpawnParticles => base.CanSpawnParticles && cursorScreenPosition.HasValue; - protected override float ParticleGravity => 240; - - public LegacyCursorParticleSpewer(Texture texture, int perSecond) - : base(texture, perSecond, particle_lifetime_max) { - Active.BindValueChanged(_ => resetVelocityCalculation()); + p.Velocity += new Vector2( + RNG.NextSingle(0, 460f), + RNG.NextSingle(-40f, 40f) + ); } - private Vector2? cursorScreenPosition; - private Vector2 cursorVelocity; - - private const double max_velocity_frame_length = 15; - private double velocityFrameLength; - private Vector2 totalPosDifference; - - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; - - protected override bool OnMouseMove(MouseMoveEvent e) - { - if (cursorScreenPosition == null) - { - cursorScreenPosition = e.ScreenSpaceMousePosition; - return base.OnMouseMove(e); - } - - // calculate cursor velocity. - totalPosDifference += e.ScreenSpaceMousePosition - cursorScreenPosition.Value; - cursorScreenPosition = e.ScreenSpaceMousePosition; - - velocityFrameLength += Clock.ElapsedFrameTime; - - if (velocityFrameLength > max_velocity_frame_length) - { - cursorVelocity = totalPosDifference / (float)velocityFrameLength; - - totalPosDifference = Vector2.Zero; - velocityFrameLength = 0; - } - - return base.OnMouseMove(e); - } - - private void resetVelocityCalculation() - { - cursorScreenPosition = null; - totalPosDifference = Vector2.Zero; - velocityFrameLength = 0; - } - - protected override FallingParticle CreateParticle() => - new FallingParticle - { - StartPosition = ToLocalSpace(cursorScreenPosition ?? Vector2.Zero), - Duration = RNG.NextSingle(particle_lifetime_min, particle_lifetime_max), - StartAngle = (float)(RNG.NextDouble() * 4 - 2), - EndAngle = RNG.NextSingle(-2f, 2f), - EndScale = RNG.NextSingle(2f), - Velocity = getVelocity(), - }; - - private Vector2 getVelocity() - { - Vector2 velocity = Vector2.Zero; - - switch (Direction) - { - case SpewDirection.Left: - velocity = new Vector2( - RNG.NextSingle(-460f, 0), - RNG.NextSingle(-40f, 40f) - ); - break; - - case SpewDirection.Right: - velocity = new Vector2( - RNG.NextSingle(0, 460f), - RNG.NextSingle(-40f, 40f) - ); - break; - - case SpewDirection.Omni: - velocity = new Vector2( - RNG.NextSingle(-460f, 460f), - RNG.NextSingle(-160f, 160f) - ); - break; - } - - velocity += cursorVelocity * 40; - - return velocity; - } - } - - private enum SpewDirection - { - None, - Left, - Right, - Omni, + return p; } } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs index 086b1c2ac2..390534745d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs @@ -5,7 +5,6 @@ using System; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Textures; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Graphics; @@ -17,7 +16,12 @@ namespace osu.Game.Tests.Visual.Gameplay [TestFixture] public class TestSceneParticleSpewer : OsuTestScene { - private TestParticleSpewer spewer; + private const int max_particle_duration = 1500; + + private float particleMaxVelocity = 0.5f; + private Vector2 particleSpawnPosition = new Vector2(0.5f); + + private ParticleSpewer spewer; [Resolved] private SkinManager skinManager { get; set; } @@ -28,11 +32,11 @@ namespace osu.Game.Tests.Visual.Gameplay Child = spewer = createSpewer(); AddToggleStep("toggle spawning", value => spewer.Active.Value = value); - AddSliderStep("particle gravity", 0f, 1f, 0f, value => spewer.Gravity = value); - AddSliderStep("particle velocity", 0f, 1f, 0.5f, value => spewer.MaxVelocity = value); + AddSliderStep("particle velocity", 0f, 1f, 0.5f, value => particleMaxVelocity = value); + AddSliderStep("particle gravity", 0f, 1f, 0f, value => spewer.ParticleGravity = value); AddStep("move to new location", () => { - spewer.TransformTo(nameof(spewer.SpawnPosition), new Vector2(RNG.NextSingle(), RNG.NextSingle()), 1000, Easing.Out); + this.TransformTo(nameof(particleSpawnPosition), new Vector2(RNG.NextSingle(), RNG.NextSingle()), 1000, Easing.Out); }); } @@ -55,47 +59,29 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("is not present", () => !spewer.IsPresent); } - private TestParticleSpewer createSpewer() => - new TestParticleSpewer(skinManager.DefaultLegacySkin.GetTexture("star2")) + private ParticleSpewer createSpewer() => + new ParticleSpewer(skinManager.DefaultLegacySkin.GetTexture("star2"), 1500, max_particle_duration) { Origin = Anchor.Centre, RelativePositionAxes = Axes.Both, RelativeSizeAxes = Axes.Both, Position = new Vector2(0.5f), Size = new Vector2(0.5f), + CreateParticle = createParticle, }; - private class TestParticleSpewer : ParticleSpewer - { - private const int lifetime = 1500; - private const int rate = 250; - - public float Gravity; - - public float MaxVelocity = 0.25f; - - public Vector2 SpawnPosition { get; set; } = new Vector2(0.5f); - - protected override float ParticleGravity => Gravity; - - public TestParticleSpewer(Texture texture) - : base(texture, rate, lifetime) + private ParticleSpewer.FallingParticle? createParticle() => + new ParticleSpewer.FallingParticle { - } - - protected override FallingParticle CreateParticle() => - new FallingParticle - { - Velocity = new Vector2( - RNG.NextSingle(-MaxVelocity, MaxVelocity), - RNG.NextSingle(-MaxVelocity, MaxVelocity) - ), - StartPosition = SpawnPosition, - Duration = RNG.NextSingle(lifetime), - StartAngle = RNG.NextSingle(MathF.PI * 2), - EndAngle = RNG.NextSingle(MathF.PI * 2), - EndScale = RNG.NextSingle(0.5f, 1.5f) - }; - } + Velocity = new Vector2( + RNG.NextSingle(-particleMaxVelocity, particleMaxVelocity), + RNG.NextSingle(-particleMaxVelocity, particleMaxVelocity) + ), + StartPosition = particleSpawnPosition, + Duration = RNG.NextSingle(max_particle_duration), + StartAngle = RNG.NextSingle(MathF.PI * 2), + EndAngle = RNG.NextSingle(MathF.PI * 2), + EndScale = RNG.NextSingle(0.5f, 1.5f) + }; } } diff --git a/osu.Game/Graphics/ParticleSpewer.cs b/osu.Game/Graphics/ParticleSpewer.cs index 1ad4672238..911f5894e7 100644 --- a/osu.Game/Graphics/ParticleSpewer.cs +++ b/osu.Game/Graphics/ParticleSpewer.cs @@ -13,14 +13,14 @@ using osuTK; namespace osu.Game.Graphics { - public abstract class ParticleSpewer : Sprite + public class ParticleSpewer : Sprite { private readonly FallingParticle[] particles; private int currentIndex; private double lastParticleAdded; private readonly double cooldown; - private readonly double maxLifetime; + private readonly double maxDuration; /// /// Determines whether particles are being spawned. @@ -29,20 +29,24 @@ namespace osu.Game.Graphics public override bool IsPresent => base.IsPresent && hasActiveParticles; - protected virtual bool CanSpawnParticles => true; - protected virtual float ParticleGravity => 0; + /// + /// Called each time a new particle should be spawned. + /// + public Func CreateParticle = () => new FallingParticle(); - private bool hasActiveParticles => Active.Value || (lastParticleAdded + maxLifetime) > Time.Current; + public float ParticleGravity; - protected ParticleSpewer(Texture texture, int perSecond, double maxLifetime) + private bool hasActiveParticles => Active.Value || (lastParticleAdded + maxDuration) > Time.Current; + + public ParticleSpewer(Texture texture, int perSecond, double maxDuration) { Texture = texture; Blending = BlendingParameters.Additive; - particles = new FallingParticle[perSecond * (int)Math.Ceiling(maxLifetime / 1000)]; + particles = new FallingParticle[perSecond * (int)Math.Ceiling(maxDuration / 1000)]; cooldown = 1000f / perSecond; - this.maxLifetime = maxLifetime; + this.maxDuration = maxDuration; } protected override void Update() @@ -53,25 +57,25 @@ namespace osu.Game.Graphics // this can happen when seeking in replays. if (lastParticleAdded > Time.Current) lastParticleAdded = 0; - if (Active.Value && CanSpawnParticles && Time.Current > lastParticleAdded + cooldown) + if (Active.Value && Time.Current > lastParticleAdded + cooldown) { var newParticle = CreateParticle(); - newParticle.StartTime = (float)Time.Current; - particles[currentIndex] = newParticle; + if (newParticle.HasValue) + { + var particle = newParticle.Value; + particle.StartTime = (float)Time.Current; - currentIndex = (currentIndex + 1) % particles.Length; - lastParticleAdded = Time.Current; + particles[currentIndex] = particle; + + currentIndex = (currentIndex + 1) % particles.Length; + lastParticleAdded = Time.Current; + } } Invalidate(Invalidation.DrawNode); } - /// - /// Called each time a new particle should be spawned. - /// - protected virtual FallingParticle CreateParticle() => new FallingParticle(); - protected override DrawNode CreateDrawNode() => new ParticleSpewerDrawNode(this); # region DrawNode @@ -82,7 +86,7 @@ namespace osu.Game.Graphics protected new ParticleSpewer Source => (ParticleSpewer)base.Source; - private readonly float maxLifetime; + private readonly float maxDuration; private float currentTime; private float gravity; @@ -93,7 +97,7 @@ namespace osu.Game.Graphics : base(source) { particles = new FallingParticle[Source.particles.Length]; - maxLifetime = (float)Source.maxLifetime; + maxDuration = (float)Source.maxDuration; } public override void ApplyState() @@ -124,7 +128,7 @@ namespace osu.Game.Graphics var alpha = p.AlphaAtTime(timeSinceStart); if (alpha <= 0) continue; - var pos = p.PositionAtTime(timeSinceStart, gravity, maxLifetime); + var pos = p.PositionAtTime(timeSinceStart, gravity, maxDuration); var scale = p.ScaleAtTime(timeSinceStart); var angle = p.AngleAtTime(timeSinceStart); @@ -174,7 +178,7 @@ namespace osu.Game.Graphics #endregion - protected struct FallingParticle + public struct FallingParticle { public float StartTime; public Vector2 StartPosition; From af4c3727d77a16e2534df9bbf452336b5c544342 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 19 Sep 2021 04:39:35 +0200 Subject: [PATCH 2018/2442] Fix build errors --- .../Skinning/Legacy/LegacyCursorParticles.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index d4e5bdd46f..ba5a03c1f1 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -127,15 +127,15 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy return base.OnMouseMove(e); } - public bool OnPressed(OsuAction action) + public bool OnPressed(KeyBindingPressEvent e) { - handleInput(action, true); + handleInput(e.Action, true); return false; } - public void OnReleased(OsuAction action) + public void OnReleased(KeyBindingReleaseEvent e) { - handleInput(action, false); + handleInput(e.Action, false); } private bool leftPressed; From 761da45f6a41b30d90931819e168ea1204571d99 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 19 Sep 2021 14:00:56 +0200 Subject: [PATCH 2019/2442] Revert `af4c3727d77a16e2534df9bbf452336b5c544342` --- .idea/.idea.osu/.idea/indexLayout.xml | 2 +- .../Skinning/Legacy/LegacyCursorParticles.cs | 217 ++++++++++-------- .../Gameplay/TestSceneParticleSpewer.cs | 62 +++-- osu.Game/Graphics/ParticleSpewer.cs | 34 ++- 4 files changed, 178 insertions(+), 137 deletions(-) diff --git a/.idea/.idea.osu/.idea/indexLayout.xml b/.idea/.idea.osu/.idea/indexLayout.xml index 27ba142e96..7b08163ceb 100644 --- a/.idea/.idea.osu/.idea/indexLayout.xml +++ b/.idea/.idea.osu/.idea/indexLayout.xml @@ -1,6 +1,6 @@ - + diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index ba5a03c1f1..e1b7dbc3e3 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Textures; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; @@ -20,17 +21,12 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Skinning.Legacy { - public class LegacyCursorParticles : CompositeDrawable, IKeyBindingHandler, IRequireHighFrequencyMousePosition + public class LegacyCursorParticles : CompositeDrawable, IKeyBindingHandler { - private const int particle_lifetime_min = 300; - private const int particle_lifetime_max = 1000; - private const float particle_gravity = 240; - public bool Active => breakSpewer?.Active.Value == true || kiaiSpewer?.Active.Value == true; - private Vector2 cursorVelocity; - private ParticleSpewer breakSpewer; - private ParticleSpewer kiaiSpewer; + private LegacyCursorParticleSpewer breakSpewer; + private LegacyCursorParticleSpewer kiaiSpewer; [Resolved(canBeNull: true)] private Player player { get; set; } @@ -50,25 +46,21 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy texture.ScaleAdjust *= 1.6f; } - RelativeSizeAxes = Axes.Both; - Anchor = Anchor.Centre; InternalChildren = new[] { - breakSpewer = new ParticleSpewer(texture, 20, particle_lifetime_max) + breakSpewer = new LegacyCursorParticleSpewer(texture, 20) { - RelativeSizeAxes = Axes.Both, - Size = Vector2.One, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Colour = starBreakAdditive, - ParticleGravity = particle_gravity, - CreateParticle = createBreakParticle, + Direction = SpewDirection.None, }, - kiaiSpewer = new ParticleSpewer(texture, 60, particle_lifetime_max) + kiaiSpewer = new LegacyCursorParticleSpewer(texture, 60) { - RelativeSizeAxes = Axes.Both, - Size = Vector2.One, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Colour = starBreakAdditive, - ParticleGravity = particle_gravity, - CreateParticle = createParticle, + Direction = SpewDirection.None, }, }; @@ -94,39 +86,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy kiaiSpewer.Active.Value = kiaiHitObject != null; } - private Vector2? cursorScreenPosition; - - private const double max_velocity_frame_length = 15; - private double velocityFrameLength; - private Vector2 totalPosDifference; - - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; - - protected override bool OnMouseMove(MouseMoveEvent e) - { - if (cursorScreenPosition == null) - { - cursorScreenPosition = e.ScreenSpaceMousePosition; - return base.OnMouseMove(e); - } - - // calculate cursor velocity. - totalPosDifference += e.ScreenSpaceMousePosition - cursorScreenPosition.Value; - cursorScreenPosition = e.ScreenSpaceMousePosition; - - velocityFrameLength += Math.Abs(Clock.ElapsedFrameTime); - - if (velocityFrameLength > max_velocity_frame_length) - { - cursorVelocity = totalPosDifference / (float)velocityFrameLength; - - totalPosDifference = Vector2.Zero; - velocityFrameLength = 0; - } - - return base.OnMouseMove(e); - } - public bool OnPressed(KeyBindingPressEvent e) { handleInput(e.Action, true); @@ -153,53 +112,125 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy rightPressed = pressed; break; } - } - - private ParticleSpewer.FallingParticle? createParticle() - { - if (!cursorScreenPosition.HasValue) return null; - - return new ParticleSpewer.FallingParticle - { - StartPosition = ToLocalSpace(cursorScreenPosition.Value), - Duration = RNG.NextSingle(particle_lifetime_min, particle_lifetime_max), - StartAngle = (float)(RNG.NextDouble() * 4 - 2), - EndAngle = RNG.NextSingle(-2f, 2f), - EndScale = RNG.NextSingle(2f), - Velocity = cursorVelocity * 40, - }; - } - - private ParticleSpewer.FallingParticle? createBreakParticle() - { - var baseParticle = createParticle(); - if (!baseParticle.HasValue) return null; - - var p = baseParticle.Value; if (leftPressed && rightPressed) - { - p.Velocity += new Vector2( - RNG.NextSingle(-460f, 460f), - RNG.NextSingle(-160f, 160f) - ); - } + breakSpewer.Direction = SpewDirection.Omni; else if (leftPressed) - { - p.Velocity += new Vector2( - RNG.NextSingle(-460f, 0), - RNG.NextSingle(-40f, 40f) - ); - } + breakSpewer.Direction = SpewDirection.Left; else if (rightPressed) + breakSpewer.Direction = SpewDirection.Right; + else + breakSpewer.Direction = SpewDirection.None; + } + + private class LegacyCursorParticleSpewer : ParticleSpewer, IRequireHighFrequencyMousePosition + { + private const int particle_duration_min = 300; + private const int particle_duration_max = 1000; + + public SpewDirection Direction { get; set; } + + protected override bool CanSpawnParticles => base.CanSpawnParticles && cursorScreenPosition.HasValue; + protected override float ParticleGravity => 240; + + public LegacyCursorParticleSpewer(Texture texture, int perSecond) + : base(texture, perSecond, particle_duration_max) { - p.Velocity += new Vector2( - RNG.NextSingle(0, 460f), - RNG.NextSingle(-40f, 40f) - ); + Active.BindValueChanged(_ => resetVelocityCalculation()); } - return p; + private Vector2? cursorScreenPosition; + private Vector2 cursorVelocity; + + private const double max_velocity_frame_length = 15; + private double velocityFrameLength; + private Vector2 totalPosDifference; + + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; + + protected override bool OnMouseMove(MouseMoveEvent e) + { + if (cursorScreenPosition == null) + { + cursorScreenPosition = e.ScreenSpaceMousePosition; + return base.OnMouseMove(e); + } + + // calculate cursor velocity. + totalPosDifference += e.ScreenSpaceMousePosition - cursorScreenPosition.Value; + cursorScreenPosition = e.ScreenSpaceMousePosition; + + velocityFrameLength += Math.Abs(Clock.ElapsedFrameTime); + + if (velocityFrameLength > max_velocity_frame_length) + { + cursorVelocity = totalPosDifference / (float)velocityFrameLength; + + totalPosDifference = Vector2.Zero; + velocityFrameLength = 0; + } + + return base.OnMouseMove(e); + } + + private void resetVelocityCalculation() + { + cursorScreenPosition = null; + totalPosDifference = Vector2.Zero; + velocityFrameLength = 0; + } + + protected override FallingParticle CreateParticle() => + new FallingParticle + { + StartPosition = ToLocalSpace(cursorScreenPosition ?? Vector2.Zero), + Duration = RNG.NextSingle(particle_duration_min, particle_duration_max), + StartAngle = (float)(RNG.NextDouble() * 4 - 2), + EndAngle = RNG.NextSingle(-2f, 2f), + EndScale = RNG.NextSingle(2f), + Velocity = getVelocity(), + }; + + private Vector2 getVelocity() + { + Vector2 velocity = Vector2.Zero; + + switch (Direction) + { + case SpewDirection.Left: + velocity = new Vector2( + RNG.NextSingle(-460f, 0), + RNG.NextSingle(-40f, 40f) + ); + break; + + case SpewDirection.Right: + velocity = new Vector2( + RNG.NextSingle(0, 460f), + RNG.NextSingle(-40f, 40f) + ); + break; + + case SpewDirection.Omni: + velocity = new Vector2( + RNG.NextSingle(-460f, 460f), + RNG.NextSingle(-160f, 160f) + ); + break; + } + + velocity += cursorVelocity * 40; + + return velocity; + } + } + + private enum SpewDirection + { + None, + Left, + Right, + Omni, } } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs index 390534745d..2f107c8300 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs @@ -5,6 +5,7 @@ using System; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Textures; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Graphics; @@ -16,12 +17,7 @@ namespace osu.Game.Tests.Visual.Gameplay [TestFixture] public class TestSceneParticleSpewer : OsuTestScene { - private const int max_particle_duration = 1500; - - private float particleMaxVelocity = 0.5f; - private Vector2 particleSpawnPosition = new Vector2(0.5f); - - private ParticleSpewer spewer; + private TestParticleSpewer spewer; [Resolved] private SkinManager skinManager { get; set; } @@ -32,11 +28,11 @@ namespace osu.Game.Tests.Visual.Gameplay Child = spewer = createSpewer(); AddToggleStep("toggle spawning", value => spewer.Active.Value = value); - AddSliderStep("particle velocity", 0f, 1f, 0.5f, value => particleMaxVelocity = value); - AddSliderStep("particle gravity", 0f, 1f, 0f, value => spewer.ParticleGravity = value); + AddSliderStep("particle gravity", 0f, 1f, 0f, value => spewer.Gravity = value); + AddSliderStep("particle velocity", 0f, 1f, 0.5f, value => spewer.MaxVelocity = value); AddStep("move to new location", () => { - this.TransformTo(nameof(particleSpawnPosition), new Vector2(RNG.NextSingle(), RNG.NextSingle()), 1000, Easing.Out); + spewer.TransformTo(nameof(spewer.SpawnPosition), new Vector2(RNG.NextSingle(), RNG.NextSingle()), 1000, Easing.Out); }); } @@ -59,29 +55,47 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("is not present", () => !spewer.IsPresent); } - private ParticleSpewer createSpewer() => - new ParticleSpewer(skinManager.DefaultLegacySkin.GetTexture("star2"), 1500, max_particle_duration) + private TestParticleSpewer createSpewer() => + new TestParticleSpewer(skinManager.DefaultLegacySkin.GetTexture("star2")) { Origin = Anchor.Centre, RelativePositionAxes = Axes.Both, RelativeSizeAxes = Axes.Both, Position = new Vector2(0.5f), Size = new Vector2(0.5f), - CreateParticle = createParticle, }; - private ParticleSpewer.FallingParticle? createParticle() => - new ParticleSpewer.FallingParticle + private class TestParticleSpewer : ParticleSpewer + { + private const int max_duration = 1500; + private const int rate = 250; + + public float Gravity; + + public float MaxVelocity = 0.25f; + + public Vector2 SpawnPosition { get; set; } = new Vector2(0.5f); + + protected override float ParticleGravity => Gravity; + + public TestParticleSpewer(Texture texture) + : base(texture, rate, max_duration) { - Velocity = new Vector2( - RNG.NextSingle(-particleMaxVelocity, particleMaxVelocity), - RNG.NextSingle(-particleMaxVelocity, particleMaxVelocity) - ), - StartPosition = particleSpawnPosition, - Duration = RNG.NextSingle(max_particle_duration), - StartAngle = RNG.NextSingle(MathF.PI * 2), - EndAngle = RNG.NextSingle(MathF.PI * 2), - EndScale = RNG.NextSingle(0.5f, 1.5f) - }; + } + + protected override FallingParticle CreateParticle() => + new FallingParticle + { + Velocity = new Vector2( + RNG.NextSingle(-MaxVelocity, MaxVelocity), + RNG.NextSingle(-MaxVelocity, MaxVelocity) + ), + StartPosition = SpawnPosition, + Duration = RNG.NextSingle(max_duration), + StartAngle = RNG.NextSingle(MathF.PI * 2), + EndAngle = RNG.NextSingle(MathF.PI * 2), + EndScale = RNG.NextSingle(0.5f, 1.5f) + }; + } } } diff --git a/osu.Game/Graphics/ParticleSpewer.cs b/osu.Game/Graphics/ParticleSpewer.cs index 911f5894e7..492e439352 100644 --- a/osu.Game/Graphics/ParticleSpewer.cs +++ b/osu.Game/Graphics/ParticleSpewer.cs @@ -13,7 +13,7 @@ using osuTK; namespace osu.Game.Graphics { - public class ParticleSpewer : Sprite + public abstract class ParticleSpewer : Sprite { private readonly FallingParticle[] particles; private int currentIndex; @@ -29,16 +29,12 @@ namespace osu.Game.Graphics public override bool IsPresent => base.IsPresent && hasActiveParticles; - /// - /// Called each time a new particle should be spawned. - /// - public Func CreateParticle = () => new FallingParticle(); - - public float ParticleGravity; + protected virtual bool CanSpawnParticles => true; + protected virtual float ParticleGravity => 0; private bool hasActiveParticles => Active.Value || (lastParticleAdded + maxDuration) > Time.Current; - public ParticleSpewer(Texture texture, int perSecond, double maxDuration) + protected ParticleSpewer(Texture texture, int perSecond, double maxDuration) { Texture = texture; Blending = BlendingParameters.Additive; @@ -57,25 +53,25 @@ namespace osu.Game.Graphics // this can happen when seeking in replays. if (lastParticleAdded > Time.Current) lastParticleAdded = 0; - if (Active.Value && Time.Current > lastParticleAdded + cooldown) + if (Active.Value && CanSpawnParticles && Time.Current > lastParticleAdded + cooldown) { var newParticle = CreateParticle(); + newParticle.StartTime = (float)Time.Current; - if (newParticle.HasValue) - { - var particle = newParticle.Value; - particle.StartTime = (float)Time.Current; + particles[currentIndex] = newParticle; - particles[currentIndex] = particle; - - currentIndex = (currentIndex + 1) % particles.Length; - lastParticleAdded = Time.Current; - } + currentIndex = (currentIndex + 1) % particles.Length; + lastParticleAdded = Time.Current; } Invalidate(Invalidation.DrawNode); } + /// + /// Called each time a new particle should be spawned. + /// + protected virtual FallingParticle CreateParticle() => new FallingParticle(); + protected override DrawNode CreateDrawNode() => new ParticleSpewerDrawNode(this); # region DrawNode @@ -178,7 +174,7 @@ namespace osu.Game.Graphics #endregion - public struct FallingParticle + protected struct FallingParticle { public float StartTime; public Vector2 StartPosition; From d5a10e922177f9cf7c7bc7f465f7c3e1d9a9807e Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 19 Sep 2021 14:47:20 +0200 Subject: [PATCH 2020/2442] Fix particles not spawning if `Time.Current` is negative --- osu.Game/Graphics/ParticleSpewer.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/osu.Game/Graphics/ParticleSpewer.cs b/osu.Game/Graphics/ParticleSpewer.cs index 492e439352..a52f749f4a 100644 --- a/osu.Game/Graphics/ParticleSpewer.cs +++ b/osu.Game/Graphics/ParticleSpewer.cs @@ -49,11 +49,7 @@ namespace osu.Game.Graphics { base.Update(); - // reset cooldown if the clock was rewound. - // this can happen when seeking in replays. - if (lastParticleAdded > Time.Current) lastParticleAdded = 0; - - if (Active.Value && CanSpawnParticles && Time.Current > lastParticleAdded + cooldown) + if (Active.Value && CanSpawnParticles && Math.Abs(Time.Current - lastParticleAdded) > cooldown) { var newParticle = CreateParticle(); newParticle.StartTime = (float)Time.Current; @@ -112,9 +108,6 @@ namespace osu.Game.Graphics { foreach (var p in particles) { - // ignore particles that weren't initialized. - if (p.StartTime <= 0) continue; - var timeSinceStart = currentTime - p.StartTime; // ignore particles from the future. From 0b593fac5c243438d2f0c9200f74eb7d2abdddf3 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 19 Sep 2021 14:49:09 +0200 Subject: [PATCH 2021/2442] Scope down DrawNode's `source` parameter --- osu.Game/Graphics/ParticleSpewer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/ParticleSpewer.cs b/osu.Game/Graphics/ParticleSpewer.cs index a52f749f4a..90216da85a 100644 --- a/osu.Game/Graphics/ParticleSpewer.cs +++ b/osu.Game/Graphics/ParticleSpewer.cs @@ -85,7 +85,7 @@ namespace osu.Game.Graphics private Axes relativePositionAxes; private Vector2 sourceSize; - public ParticleSpewerDrawNode(Sprite source) + public ParticleSpewerDrawNode(ParticleSpewer source) : base(source) { particles = new FallingParticle[Source.particles.Length]; From 9c90dd539f3e6842cc03027cad368da5a9bb5d57 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 19 Sep 2021 15:06:15 +0200 Subject: [PATCH 2022/2442] Use `Interpolation.Lerp` --- osu.Game/Graphics/ParticleSpewer.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/ParticleSpewer.cs b/osu.Game/Graphics/ParticleSpewer.cs index 90216da85a..466bf04369 100644 --- a/osu.Game/Graphics/ParticleSpewer.cs +++ b/osu.Game/Graphics/ParticleSpewer.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.OpenGL.Vertices; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Framework.Utils; using osuTK; namespace osu.Game.Graphics @@ -179,9 +180,9 @@ namespace osu.Game.Graphics public float AlphaAtTime(float timeSinceStart) => 1 - progressAtTime(timeSinceStart); - public float ScaleAtTime(float timeSinceStart) => 1 + (EndScale - 1) * progressAtTime(timeSinceStart); + public float ScaleAtTime(float timeSinceStart) => (float)Interpolation.Lerp(1, EndScale, progressAtTime(timeSinceStart)); - public float AngleAtTime(float timeSinceStart) => StartAngle + (EndAngle - StartAngle) * progressAtTime(timeSinceStart); + public float AngleAtTime(float timeSinceStart) => (float)Interpolation.Lerp(StartAngle, EndAngle, progressAtTime(timeSinceStart)); public Vector2 PositionAtTime(float timeSinceStart, float gravity, float maxDuration) { From 366dbf5c3dbae9993b78f1f55c2ac73dd7806944 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 19 Sep 2021 15:35:03 +0200 Subject: [PATCH 2023/2442] Add test for time jumps --- .../Gameplay/TestSceneParticleSpewer.cs | 37 ++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs index 2f107c8300..ce5cd629e0 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs @@ -7,6 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Framework.Testing; +using osu.Framework.Timing; using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Skinning; @@ -55,6 +56,26 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("is not present", () => !spewer.IsPresent); } + [Test] + public void TestTimeJumps() + { + ManualClock testClock = new ManualClock(); + + AddStep("prepare clock", () => + { + testClock.CurrentTime = TestParticleSpewer.MAX_DURATION * -3; + spewer.Clock = new FramedClock(testClock); + }); + AddStep("start spewer", () => spewer.Active.Value = true); + AddAssert("spawned first particle", () => spewer.TotalCreatedParticles == 1); + + AddStep("move clock forward", () => testClock.CurrentTime = TestParticleSpewer.MAX_DURATION * 3); + AddAssert("spawned second particle", () => spewer.TotalCreatedParticles == 2); + + AddStep("move clock backwards", () => testClock.CurrentTime = TestParticleSpewer.MAX_DURATION * -1); + AddAssert("spawned third particle", () => spewer.TotalCreatedParticles == 3); + } + private TestParticleSpewer createSpewer() => new TestParticleSpewer(skinManager.DefaultLegacySkin.GetTexture("star2")) { @@ -67,9 +88,11 @@ namespace osu.Game.Tests.Visual.Gameplay private class TestParticleSpewer : ParticleSpewer { - private const int max_duration = 1500; + public const int MAX_DURATION = 1500; private const int rate = 250; + public int TotalCreatedParticles { get; private set; } + public float Gravity; public float MaxVelocity = 0.25f; @@ -79,23 +102,27 @@ namespace osu.Game.Tests.Visual.Gameplay protected override float ParticleGravity => Gravity; public TestParticleSpewer(Texture texture) - : base(texture, rate, max_duration) + : base(texture, rate, MAX_DURATION) { } - protected override FallingParticle CreateParticle() => - new FallingParticle + protected override FallingParticle CreateParticle() + { + TotalCreatedParticles++; + + return new FallingParticle { Velocity = new Vector2( RNG.NextSingle(-MaxVelocity, MaxVelocity), RNG.NextSingle(-MaxVelocity, MaxVelocity) ), StartPosition = SpawnPosition, - Duration = RNG.NextSingle(max_duration), + Duration = RNG.NextSingle(MAX_DURATION), StartAngle = RNG.NextSingle(MathF.PI * 2), EndAngle = RNG.NextSingle(MathF.PI * 2), EndScale = RNG.NextSingle(0.5f, 1.5f) }; + } } } } From 56e80a07060e959948e109433f93aab43e501fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 19 Sep 2021 16:41:30 +0200 Subject: [PATCH 2024/2442] Add rectangular position snap grid --- .../TestSceneRectangularPositionSnapGrid.cs | 103 ++++++++++++++++++ .../Components/RectangularPositionSnapGrid.cs | 101 +++++++++++++++++ 2 files changed, 204 insertions(+) create mode 100644 osu.Game.Tests/Visual/Editing/TestSceneRectangularPositionSnapGrid.cs create mode 100644 osu.Game/Screens/Edit/Compose/Components/RectangularPositionSnapGrid.cs diff --git a/osu.Game.Tests/Visual/Editing/TestSceneRectangularPositionSnapGrid.cs b/osu.Game.Tests/Visual/Editing/TestSceneRectangularPositionSnapGrid.cs new file mode 100644 index 0000000000..ec267bf752 --- /dev/null +++ b/osu.Game.Tests/Visual/Editing/TestSceneRectangularPositionSnapGrid.cs @@ -0,0 +1,103 @@ +// 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 NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osu.Game.Screens.Edit.Compose.Components; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Tests.Visual.Editing +{ + public class TestSceneRectangularPositionSnapGrid : OsuManualInputManagerTestScene + { + private Container content; + protected override Container Content => content; + + [BackgroundDependencyLoader] + private void load() + { + base.Content.AddRange(new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Colour4.Gray + }, + content = new Container + { + RelativeSizeAxes = Axes.Both + } + }); + } + + private static readonly object[][] test_cases = + { + new object[] { new Vector2(0, 0), new Vector2(10, 10) }, + new object[] { new Vector2(240, 180), new Vector2(10, 15) }, + new object[] { new Vector2(160, 120), new Vector2(30, 20) }, + new object[] { new Vector2(480, 360), new Vector2(100, 100) }, + }; + + [TestCaseSource(nameof(test_cases))] + public void TestRectangularGrid(Vector2 position, Vector2 spacing) + { + RectangularPositionSnapGrid grid = null; + + AddStep("create grid", () => Child = grid = new RectangularPositionSnapGrid(position, spacing) + { + RelativeSizeAxes = Axes.Both + }); + + AddStep("add snapping cursor", () => Add(new SnappingCursorContainer + { + RelativeSizeAxes = Axes.Both, + GetSnapPosition = pos => grid.GetSnappedPosition(grid.ToLocalSpace(pos)) + })); + } + + private class SnappingCursorContainer : CompositeDrawable + { + public Func GetSnapPosition; + + private readonly Drawable cursor; + + public SnappingCursorContainer() + { + RelativeSizeAxes = Axes.Both; + + InternalChild = cursor = new Circle + { + Origin = Anchor.Centre, + Size = new Vector2(50), + Colour = Color4.Red + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + updatePosition(GetContainingInputManager().CurrentState.Mouse.Position); + } + + protected override bool OnMouseMove(MouseMoveEvent e) + { + base.OnMouseMove(e); + + updatePosition(e.ScreenSpaceMousePosition); + return true; + } + + private void updatePosition(Vector2 screenSpacePosition) + { + cursor.Position = GetSnapPosition.Invoke(screenSpacePosition); + } + } + } +} diff --git a/osu.Game/Screens/Edit/Compose/Components/RectangularPositionSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/RectangularPositionSnapGrid.cs new file mode 100644 index 0000000000..f243c027e3 --- /dev/null +++ b/osu.Game/Screens/Edit/Compose/Components/RectangularPositionSnapGrid.cs @@ -0,0 +1,101 @@ +// 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 osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Layout; +using osuTK; + +namespace osu.Game.Screens.Edit.Compose.Components +{ + public class RectangularPositionSnapGrid : CompositeDrawable + { + /// + /// The position of the origin of this in local coordinates. + /// + public Vector2 StartPosition { get; } + + /// + /// The spacing between grid lines of this . + /// + public Vector2 Spacing { get; } + + private readonly LayoutValue gridCache = new LayoutValue(Invalidation.RequiredParentSizeToFit); + + public RectangularPositionSnapGrid(Vector2 startPosition, Vector2 spacing) + { + StartPosition = startPosition; + Spacing = spacing; + + AddLayout(gridCache); + } + + protected override void Update() + { + base.Update(); + + if (!gridCache.IsValid) + { + ClearInternal(); + createContent(); + gridCache.Validate(); + } + } + + private void createContent() + { + var drawSize = DrawSize; + + generateGridLines(Direction.Horizontal, StartPosition.Y, 0, -Spacing.Y); + generateGridLines(Direction.Horizontal, StartPosition.Y, drawSize.Y, Spacing.Y); + + generateGridLines(Direction.Vertical, StartPosition.X, 0, -Spacing.X); + generateGridLines(Direction.Vertical, StartPosition.X, drawSize.X, Spacing.X); + } + + private void generateGridLines(Direction direction, float startPosition, float endPosition, float step) + { + int index = 0; + float currentPosition = startPosition; + + while ((endPosition - currentPosition) * Math.Sign(step) > 0) + { + var gridLine = new Box + { + Colour = Colour4.White, + Alpha = index == 0 ? 0.3f : 0.1f, + EdgeSmoothness = new Vector2(0.2f) + }; + + if (direction == Direction.Horizontal) + { + gridLine.RelativeSizeAxes = Axes.X; + gridLine.Height = 1; + gridLine.Y = currentPosition; + } + else + { + gridLine.RelativeSizeAxes = Axes.Y; + gridLine.Width = 1; + gridLine.X = currentPosition; + } + + AddInternal(gridLine); + + index += 1; + currentPosition = startPosition + index * step; + } + } + + public Vector2 GetSnappedPosition(Vector2 original) + { + Vector2 relativeToStart = original - StartPosition; + Vector2 offset = Vector2.Divide(relativeToStart, Spacing); + Vector2 roundedOffset = new Vector2(MathF.Round(offset.X), MathF.Round(offset.Y)); + + return StartPosition + Vector2.Multiply(roundedOffset, Spacing); + } + } +} From e1738025d439733586b88ea2b08d12a976f6fbd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 19 Sep 2021 17:48:29 +0200 Subject: [PATCH 2025/2442] Add basic integration of rectangular grid to osu! composer --- .../Edit/OsuHitObjectComposer.cs | 57 ++++++++++++++++--- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 806b7e6051..491ad655fa 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -17,6 +17,7 @@ using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.UI; using osu.Game.Screens.Edit.Components.TernaryButtons; using osu.Game.Screens.Edit.Compose.Components; @@ -42,10 +43,12 @@ namespace osu.Game.Rulesets.Osu.Edit }; private readonly Bindable distanceSnapToggle = new Bindable(); + private readonly Bindable gridSnapToggle = new Bindable(); protected override IEnumerable CreateTernaryButtons() => base.CreateTernaryButtons().Concat(new[] { - new TernaryButton(distanceSnapToggle, "Distance Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Ruler }) + new TernaryButton(distanceSnapToggle, "Distance Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Ruler }), + new TernaryButton(gridSnapToggle, "Grid Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Th }) }); private BindableList selectedHitObjects; @@ -63,6 +66,10 @@ namespace osu.Game.Rulesets.Osu.Edit PlayfieldBorderStyle = { Value = PlayfieldBorderStyle.Corners } }, distanceSnapGridContainer = new Container + { + RelativeSizeAxes = Axes.Both + }, + rectangularPositionSnapGridContainer = new Container { RelativeSizeAxes = Axes.Both } @@ -73,7 +80,21 @@ namespace osu.Game.Rulesets.Osu.Edit placementObject = EditorBeatmap.PlacementObject.GetBoundCopy(); placementObject.ValueChanged += _ => updateDistanceSnapGrid(); - distanceSnapToggle.ValueChanged += _ => updateDistanceSnapGrid(); + distanceSnapToggle.ValueChanged += _ => + { + updateDistanceSnapGrid(); + + if (distanceSnapToggle.Value == TernaryState.True) + gridSnapToggle.Value = TernaryState.False; + }; + + gridSnapToggle.ValueChanged += _ => + { + updateRectangularPositionSnapGrid(); + + if (gridSnapToggle.Value == TernaryState.True) + distanceSnapToggle.Value = TernaryState.False; + }; // we may be entering the screen with a selection already active updateDistanceSnapGrid(); @@ -122,13 +143,19 @@ namespace osu.Game.Rulesets.Osu.Edit if (positionSnap.ScreenSpacePosition != screenSpacePosition) return positionSnap; - // will be null if distance snap is disabled or not feasible for the current time value. - if (distanceSnapGrid == null) - return base.SnapScreenSpacePositionToValidTime(screenSpacePosition); + if (distanceSnapGrid != null) + { + (Vector2 pos, double time) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(screenSpacePosition)); + return new SnapResult(distanceSnapGrid.ToScreenSpace(pos), time, PlayfieldAtScreenSpacePosition(screenSpacePosition)); + } - (Vector2 pos, double time) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(screenSpacePosition)); + if (rectangularPositionSnapGrid != null) + { + Vector2 pos = rectangularPositionSnapGrid.GetSnappedPosition(rectangularPositionSnapGrid.ToLocalSpace(screenSpacePosition)); + return new SnapResult(rectangularPositionSnapGrid.ToScreenSpace(pos), null, PlayfieldAtScreenSpacePosition(screenSpacePosition)); + } - return new SnapResult(distanceSnapGrid.ToScreenSpace(pos), time, PlayfieldAtScreenSpacePosition(screenSpacePosition)); + return base.SnapScreenSpacePositionToValidTime(screenSpacePosition); } private bool snapToVisibleBlueprints(Vector2 screenSpacePosition, out SnapResult snapResult) @@ -272,5 +299,21 @@ namespace osu.Game.Rulesets.Osu.Edit return new OsuDistanceSnapGrid((OsuHitObject)sourceObject, (OsuHitObject)targetObject); } + + private Container rectangularPositionSnapGridContainer; + private RectangularPositionSnapGrid rectangularPositionSnapGrid; + + private void updateRectangularPositionSnapGrid() + { + rectangularPositionSnapGridContainer.Clear(); + + if (gridSnapToggle.Value == TernaryState.True) + { + rectangularPositionSnapGridContainer.Add(rectangularPositionSnapGrid = new RectangularPositionSnapGrid(OsuPlayfield.BASE_SIZE / 2, new Vector2(16)) + { + RelativeSizeAxes = Axes.Both + }); + } + } } } From c403e628ddae77c9c3173604e8c9832dd09b8a6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 19 Sep 2021 17:58:32 +0200 Subject: [PATCH 2026/2442] Add test coverage for distance/rectangular grid exclusivity --- .../Editor/TestSceneOsuEditorGrids.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs new file mode 100644 index 0000000000..007b27b2e7 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs @@ -0,0 +1,34 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Testing; +using osu.Game.Rulesets.Osu.Edit; +using osu.Game.Screens.Edit.Compose.Components; +using osu.Game.Tests.Visual; +using osuTK.Input; + +namespace osu.Game.Rulesets.Osu.Tests.Editor +{ + public class TestSceneOsuEditorGrids : EditorTestScene + { + protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); + + [Test] + public void TestGridExclusivity() + { + AddStep("enable distance snap grid", () => InputManager.Key(Key.T)); + AddStep("select second object", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.ElementAt(1))); + AddUntilStep("distance snap grid visible", () => this.ChildrenOfType().Any()); + + AddStep("enable rectangular grid", () => InputManager.Key(Key.Y)); + AddUntilStep("distance snap grid hidden", () => !this.ChildrenOfType().Any()); + AddUntilStep("rectangular grid visible", () => this.ChildrenOfType().Any()); + + AddStep("enable distance snap grid", () => InputManager.Key(Key.T)); + AddUntilStep("distance snap grid visible", () => this.ChildrenOfType().Any()); + AddUntilStep("rectangular grid hidden", () => !this.ChildrenOfType().Any()); + } + } +} From 93ca615c022f4d00a972d97ae947426447b14333 Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Sun, 19 Sep 2021 14:15:22 -0400 Subject: [PATCH 2027/2442] Add tests for clearing `HitErrorMeter` Works with both `BarHitErrorMeter` and `ColourHitErrorMeter` --- .../Visual/Gameplay/TestSceneHitErrorMeter.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs index 7accaef818..1ba0965ceb 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs @@ -7,6 +7,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; @@ -137,6 +138,23 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("no circle added", () => !this.ChildrenOfType().Any()); } + [Test] + public void TestClear() + { + AddStep("OD 1", () => recreateDisplay(new OsuHitWindows(), 1)); + + AddStep("hit", () => newJudgement(0.2D)); + AddAssert("bar added", () => this.ChildrenOfType().All( + meter => meter.ChildrenOfType().Count() == 1)); + AddAssert("circle added", () => this.ChildrenOfType().All( + meter => meter.ChildrenOfType().Count() == 1)); + + AddStep("clear", () => this.ChildrenOfType().ForEach(meter => meter.Clear())); + + AddAssert("bar cleared", () => !this.ChildrenOfType().Any()); + AddAssert("colour cleared", () => !this.ChildrenOfType().Any()); + } + private void recreateDisplay(HitWindows hitWindows, float overallDifficulty) { hitWindows?.SetDifficulty(overallDifficulty); From 4e094b2127d614fc3d1943351bad9de1a239b83c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 19 Sep 2021 18:45:22 +0200 Subject: [PATCH 2028/2442] Implement grid size toggling matching stable --- .../Editor/TestSceneOsuEditorGrids.cs | 28 +++++++++-- .../Edit/OsuHitObjectComposer.cs | 3 +- .../Edit/OsuRectangularPositionSnapGrid.cs | 50 +++++++++++++++++++ .../TestSceneRectangularPositionSnapGrid.cs | 5 +- .../Components/RectangularPositionSnapGrid.cs | 18 +++++-- 5 files changed, 94 insertions(+), 10 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs index 007b27b2e7..81e44a57db 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs @@ -5,8 +5,8 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Testing; using osu.Game.Rulesets.Osu.Edit; -using osu.Game.Screens.Edit.Compose.Components; using osu.Game.Tests.Visual; +using osuTK; using osuTK.Input; namespace osu.Game.Rulesets.Osu.Tests.Editor @@ -24,11 +24,33 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("enable rectangular grid", () => InputManager.Key(Key.Y)); AddUntilStep("distance snap grid hidden", () => !this.ChildrenOfType().Any()); - AddUntilStep("rectangular grid visible", () => this.ChildrenOfType().Any()); + AddUntilStep("rectangular grid visible", () => this.ChildrenOfType().Any()); AddStep("enable distance snap grid", () => InputManager.Key(Key.T)); AddUntilStep("distance snap grid visible", () => this.ChildrenOfType().Any()); - AddUntilStep("rectangular grid hidden", () => !this.ChildrenOfType().Any()); + AddUntilStep("rectangular grid hidden", () => !this.ChildrenOfType().Any()); } + + [Test] + public void TestGridSizeToggling() + { + AddStep("enable rectangular grid", () => InputManager.Key(Key.Y)); + AddUntilStep("rectangular grid visible", () => this.ChildrenOfType().Any()); + gridSizeIs(4); + + nextGridSizeIs(8); + nextGridSizeIs(16); + nextGridSizeIs(32); + nextGridSizeIs(4); + } + + private void nextGridSizeIs(int size) + { + AddStep("toggle to next grid size", () => InputManager.Key(Key.G)); + gridSizeIs(size); + } + + private void gridSizeIs(int size) + => AddAssert($"grid size is {size}", () => this.ChildrenOfType().Single().Spacing == new Vector2(size)); } } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 491ad655fa..a2ee646341 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -17,7 +17,6 @@ using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.UI; using osu.Game.Screens.Edit.Components.TernaryButtons; using osu.Game.Screens.Edit.Compose.Components; @@ -309,7 +308,7 @@ namespace osu.Game.Rulesets.Osu.Edit if (gridSnapToggle.Value == TernaryState.True) { - rectangularPositionSnapGridContainer.Add(rectangularPositionSnapGrid = new RectangularPositionSnapGrid(OsuPlayfield.BASE_SIZE / 2, new Vector2(16)) + rectangularPositionSnapGridContainer.Add(rectangularPositionSnapGrid = new OsuRectangularPositionSnapGrid(EditorBeatmap.BeatmapInfo.GridSize) { RelativeSizeAxes = Axes.Both }); diff --git a/osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs b/osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs new file mode 100644 index 0000000000..2faaab75e4 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs @@ -0,0 +1,50 @@ +// 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 osu.Framework.Input.Events; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Screens.Edit.Compose.Components; +using osuTK; +using osuTK.Input; + +namespace osu.Game.Rulesets.Osu.Edit +{ + public class OsuRectangularPositionSnapGrid : RectangularPositionSnapGrid + { + private static readonly int[] grid_sizes = { 4, 8, 16, 32 }; + + private int currentGridSizeIndex; + + public OsuRectangularPositionSnapGrid(int gridSize) + : base(OsuPlayfield.BASE_SIZE / 2) + { + var gridSizeIndex = Array.IndexOf(grid_sizes, gridSize); + if (gridSizeIndex > 0) + currentGridSizeIndex = gridSizeIndex; + updateSpacing(); + } + + protected override bool OnKeyDown(KeyDownEvent e) + { + if (e.Key == Key.G) + { + nextGridSize(); + return true; + } + + return false; + } + + private void nextGridSize() + { + currentGridSizeIndex = (currentGridSizeIndex + 1) % grid_sizes.Length; + updateSpacing(); + } + + private void updateSpacing() + { + Spacing = new Vector2(grid_sizes[currentGridSizeIndex]); + } + } +} diff --git a/osu.Game.Tests/Visual/Editing/TestSceneRectangularPositionSnapGrid.cs b/osu.Game.Tests/Visual/Editing/TestSceneRectangularPositionSnapGrid.cs index ec267bf752..85a98eca47 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneRectangularPositionSnapGrid.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneRectangularPositionSnapGrid.cs @@ -49,9 +49,10 @@ namespace osu.Game.Tests.Visual.Editing { RectangularPositionSnapGrid grid = null; - AddStep("create grid", () => Child = grid = new RectangularPositionSnapGrid(position, spacing) + AddStep("create grid", () => Child = grid = new RectangularPositionSnapGrid(position) { - RelativeSizeAxes = Axes.Both + RelativeSizeAxes = Axes.Both, + Spacing = spacing }); AddStep("add snapping cursor", () => Add(new SnappingCursorContainer diff --git a/osu.Game/Screens/Edit/Compose/Components/RectangularPositionSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/RectangularPositionSnapGrid.cs index f243c027e3..95b4b2fe53 100644 --- a/osu.Game/Screens/Edit/Compose/Components/RectangularPositionSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/RectangularPositionSnapGrid.cs @@ -17,17 +17,29 @@ namespace osu.Game.Screens.Edit.Compose.Components /// public Vector2 StartPosition { get; } + private Vector2 spacing = Vector2.One; + /// /// The spacing between grid lines of this . /// - public Vector2 Spacing { get; } + public Vector2 Spacing + { + get => spacing; + set + { + if (spacing.X <= 0 || spacing.Y <= 0) + throw new ArgumentException("Grid spacing must be positive."); + + spacing = value; + gridCache.Invalidate(); + } + } private readonly LayoutValue gridCache = new LayoutValue(Invalidation.RequiredParentSizeToFit); - public RectangularPositionSnapGrid(Vector2 startPosition, Vector2 spacing) + public RectangularPositionSnapGrid(Vector2 startPosition) { StartPosition = startPosition; - Spacing = spacing; AddLayout(gridCache); } From ab213e20107da27a5b92f92f49dbd65d371ed97c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 19 Sep 2021 21:09:03 +0200 Subject: [PATCH 2029/2442] Add missing licence headers --- osu.Game/Overlays/Login/LoginForm.cs | 3 +++ osu.Game/Overlays/Login/UserAction.cs | 3 +++ osu.Game/Overlays/Login/UserDropdown.cs | 3 +++ 3 files changed, 9 insertions(+) diff --git a/osu.Game/Overlays/Login/LoginForm.cs b/osu.Game/Overlays/Login/LoginForm.cs index 9d229c2b3e..e43b84d52a 100644 --- a/osu.Game/Overlays/Login/LoginForm.cs +++ b/osu.Game/Overlays/Login/LoginForm.cs @@ -1,3 +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 osu.Framework.Allocation; using osu.Framework.Graphics; diff --git a/osu.Game/Overlays/Login/UserAction.cs b/osu.Game/Overlays/Login/UserAction.cs index 440d6ea456..07b6b4bf7e 100644 --- a/osu.Game/Overlays/Login/UserAction.cs +++ b/osu.Game/Overlays/Login/UserAction.cs @@ -1,3 +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.ComponentModel; namespace osu.Game.Overlays.Login diff --git a/osu.Game/Overlays/Login/UserDropdown.cs b/osu.Game/Overlays/Login/UserDropdown.cs index 80f6c7113b..ac4e7f8eda 100644 --- a/osu.Game/Overlays/Login/UserDropdown.cs +++ b/osu.Game/Overlays/Login/UserDropdown.cs @@ -1,3 +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 osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; From 0d58530dbea69e72ef12a8bc1c0fac6fe60f5397 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Sep 2021 01:48:14 +0900 Subject: [PATCH 2030/2442] Reduce overhead of `ColumnHasObject` calls by storing column usage separately --- .../Beatmaps/Patterns/Pattern.cs | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Pattern.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Pattern.cs index f095a0ffce..817d2b60b6 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Pattern.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Pattern.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.Linq; using osu.Game.Rulesets.Mania.Objects; namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns @@ -14,6 +13,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns { private readonly List hitObjects = new List(); + private readonly HashSet containedColumns = new HashSet(); + /// /// All the hit objects contained in this pattern. /// @@ -24,34 +25,42 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns /// /// The column index. /// Whether the column with index contains a hit object. - public bool ColumnHasObject(int column) => hitObjects.Exists(h => h.Column == column); + public bool ColumnHasObject(int column) => containedColumns.Contains(column); /// /// Amount of columns taken up by hit objects in this pattern. /// - public int ColumnWithObjects => HitObjects.GroupBy(h => h.Column).Count(); + public int ColumnWithObjects => containedColumns.Count; /// /// Adds a hit object to this pattern. /// /// The hit object to add. - public void Add(ManiaHitObject hitObject) => hitObjects.Add(hitObject); + public void Add(ManiaHitObject hitObject) + { + hitObjects.Add(hitObject); + containedColumns.Add(hitObject.Column); + } /// /// Copies hit object from another pattern to this one. /// /// The other pattern. - public void Add(Pattern other) => hitObjects.AddRange(other.HitObjects); + public void Add(Pattern other) + { + hitObjects.AddRange(other.HitObjects); + + foreach (var h in other.hitObjects) + containedColumns.Add(h.Column); + } /// /// Clears this pattern, removing all hit objects. /// - public void Clear() => hitObjects.Clear(); - - /// - /// Removes a hit object from this pattern. - /// - /// The hit object to remove. - public bool Remove(ManiaHitObject hitObject) => hitObjects.Remove(hitObject); + public void Clear() + { + hitObjects.Clear(); + containedColumns.Clear(); + } } } From 16e60eed56d8586ef69df2eeeb1e5b2fd6b2836f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Sep 2021 01:48:33 +0900 Subject: [PATCH 2031/2442] Reduce `NestedHitObject` enumerator overhead This was especially bad due to it allocating on any and every start time change, even the first (see usage in `HitObject.ctor`). --- .../Difficulty/CatchDifficultyCalculator.cs | 2 +- osu.Game/Rulesets/Objects/HitObject.cs | 18 +++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 9feaa55051..82d76252d2 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty // In 2B beatmaps, it is possible that a normal Fruit is placed in the middle of a JuiceStream. foreach (var hitObject in beatmap.HitObjects - .SelectMany(obj => obj is JuiceStream stream ? stream.NestedHitObjects : new[] { obj }) + .SelectMany(obj => obj is JuiceStream stream ? stream.NestedHitObjects.AsEnumerable() : new[] { obj }) .Cast() .OrderBy(x => x.StartTime)) { diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index c4b9e6e1ad..ae0cb895bc 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -3,11 +3,12 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Threading; using JetBrains.Annotations; using Newtonsoft.Json; using osu.Framework.Bindables; +using osu.Framework.Extensions.ListExtensions; +using osu.Framework.Lists; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -83,7 +84,7 @@ namespace osu.Game.Rulesets.Objects private readonly List nestedHitObjects = new List(); [JsonIgnore] - public IReadOnlyList NestedHitObjects => nestedHitObjects; + public SlimReadOnlyListWrapper NestedHitObjects => nestedHitObjects.AsSlimReadOnly(); public HitObject() { @@ -91,7 +92,7 @@ namespace osu.Game.Rulesets.Objects { double offset = time.NewValue - time.OldValue; - foreach (var nested in NestedHitObjects) + foreach (var nested in nestedHitObjects) nested.StartTime += offset; }; } @@ -122,11 +123,14 @@ namespace osu.Game.Rulesets.Objects if (this is IHasComboInformation hasCombo) { - foreach (var n in NestedHitObjects.OfType()) + foreach (HitObject hitObject in nestedHitObjects) { - n.ComboIndexBindable.BindTo(hasCombo.ComboIndexBindable); - n.ComboIndexWithOffsetsBindable.BindTo(hasCombo.ComboIndexWithOffsetsBindable); - n.IndexInCurrentComboBindable.BindTo(hasCombo.IndexInCurrentComboBindable); + if (hitObject is IHasComboInformation n) + { + n.ComboIndexBindable.BindTo(hasCombo.ComboIndexBindable); + n.ComboIndexWithOffsetsBindable.BindTo(hasCombo.ComboIndexWithOffsetsBindable); + n.IndexInCurrentComboBindable.BindTo(hasCombo.IndexInCurrentComboBindable); + } } } From 03291e389776777c85db8899d5b64328a548b689 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Sep 2021 02:14:20 +0900 Subject: [PATCH 2032/2442] Avoid LINQ overhead in `PatternGenerator.isValid` --- .../Beatmaps/Patterns/Legacy/PatternGenerator.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs index fb58d805a9..e643b82271 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs @@ -149,7 +149,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { lowerBound ??= RandomStart; upperBound ??= TotalColumns; - nextColumn ??= (_ => GetRandomColumn(lowerBound, upperBound)); + nextColumn ??= _ => GetRandomColumn(lowerBound, upperBound); // Check for the initial column if (isValid(initialColumn)) @@ -176,7 +176,19 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy return initialColumn; - bool isValid(int column) => validation?.Invoke(column) != false && !patterns.Any(p => p.ColumnHasObject(column)); + bool isValid(int column) + { + if (validation?.Invoke(column) == false) + return false; + + foreach (var p in patterns) + { + if (p.ColumnHasObject(column)) + return false; + } + + return true; + } } /// From d96d1b3e47fb57691b5bb72c60e30ea6b847a310 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Sep 2021 14:47:47 +0900 Subject: [PATCH 2033/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 4859510e6c..7a739e6f2a 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 0460de6d72..2bed1b1564 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 51ca381b63..b547e98419 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 98f1c1cc292d5103523b8e1a87b284ae01f5bbfc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Sep 2021 16:02:02 +0900 Subject: [PATCH 2034/2442] Avoid allocating list storage in `Pattern` until first usage Patterns can often be constructed only to never be used. --- .../Beatmaps/Patterns/Pattern.cs | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Pattern.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Pattern.cs index 817d2b60b6..8c42e47dbc 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Pattern.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Pattern.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; using osu.Game.Rulesets.Mania.Objects; namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns @@ -11,26 +12,25 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns /// internal class Pattern { - private readonly List hitObjects = new List(); - - private readonly HashSet containedColumns = new HashSet(); + private List hitObjects; + private HashSet containedColumns; /// /// All the hit objects contained in this pattern. /// - public IEnumerable HitObjects => hitObjects; + public IEnumerable HitObjects => hitObjects ?? Enumerable.Empty(); /// /// Check whether a column of this patterns contains a hit object. /// /// The column index. /// Whether the column with index contains a hit object. - public bool ColumnHasObject(int column) => containedColumns.Contains(column); + public bool ColumnHasObject(int column) => containedColumns?.Contains(column) ?? false; /// /// Amount of columns taken up by hit objects in this pattern. /// - public int ColumnWithObjects => containedColumns.Count; + public int ColumnWithObjects => containedColumns?.Count ?? 0; /// /// Adds a hit object to this pattern. @@ -38,6 +38,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns /// The hit object to add. public void Add(ManiaHitObject hitObject) { + prepareStorage(); + hitObjects.Add(hitObject); containedColumns.Add(hitObject.Column); } @@ -48,10 +50,15 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns /// The other pattern. public void Add(Pattern other) { - hitObjects.AddRange(other.HitObjects); + prepareStorage(); - foreach (var h in other.hitObjects) - containedColumns.Add(h.Column); + if (other.hitObjects != null) + { + hitObjects.AddRange(other.hitObjects); + + foreach (var h in other.hitObjects) + containedColumns.Add(h.Column); + } } /// @@ -59,8 +66,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns /// public void Clear() { - hitObjects.Clear(); - containedColumns.Clear(); + hitObjects?.Clear(); + containedColumns?.Clear(); + } + + private void prepareStorage() + { + hitObjects ??= new List(); + containedColumns ??= new HashSet(); } } } From cdef6d0cf56a633c77d9b28ca2166d19eeb52a51 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Sep 2021 16:43:15 +0900 Subject: [PATCH 2035/2442] Add key binding support for grid mode cycle --- .../Edit/OsuRectangularPositionSnapGrid.cs | 32 +++++++++++-------- .../Input/Bindings/GlobalActionContainer.cs | 6 +++- .../GlobalActionKeyBindingStrings.cs | 5 +++ 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs b/osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs index 2faaab75e4..da18d3175e 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs @@ -2,15 +2,16 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Game.Input.Bindings; using osu.Game.Rulesets.Osu.UI; using osu.Game.Screens.Edit.Compose.Components; using osuTK; -using osuTK.Input; namespace osu.Game.Rulesets.Osu.Edit { - public class OsuRectangularPositionSnapGrid : RectangularPositionSnapGrid + public class OsuRectangularPositionSnapGrid : RectangularPositionSnapGrid, IKeyBindingHandler { private static readonly int[] grid_sizes = { 4, 8, 16, 32 }; @@ -25,17 +26,6 @@ namespace osu.Game.Rulesets.Osu.Edit updateSpacing(); } - protected override bool OnKeyDown(KeyDownEvent e) - { - if (e.Key == Key.G) - { - nextGridSize(); - return true; - } - - return false; - } - private void nextGridSize() { currentGridSizeIndex = (currentGridSizeIndex + 1) % grid_sizes.Length; @@ -46,5 +36,21 @@ namespace osu.Game.Rulesets.Osu.Edit { Spacing = new Vector2(grid_sizes[currentGridSizeIndex]); } + + public bool OnPressed(KeyBindingPressEvent e) + { + switch (e.Action) + { + case GlobalAction.EditorCycleGridDisplayMode: + nextGridSize(); + return true; + } + + return false; + } + + public void OnReleased(KeyBindingReleaseEvent e) + { + } } } diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index f62131e2d7..9fd7caadd0 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -75,6 +75,7 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.A }, GlobalAction.EditorVerifyMode), new KeyBinding(new[] { InputKey.J }, GlobalAction.EditorNudgeLeft), new KeyBinding(new[] { InputKey.K }, GlobalAction.EditorNudgeRight), + new KeyBinding(new[] { InputKey.G }, GlobalAction.EditorCycleGridDisplayMode), }; public IEnumerable InGameKeyBindings => new[] @@ -284,6 +285,9 @@ namespace osu.Game.Input.Bindings SeekReplayBackward, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleChatFocus))] - ToggleChatFocus + ToggleChatFocus, + + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorCycleGridDisplayMode))] + EditorCycleGridDisplayMode } } diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index 14159f0d34..06f1b094bf 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -164,6 +164,11 @@ namespace osu.Game.Localisation /// public static LocalisableString EditorTimingMode => new TranslatableString(getKey(@"editor_timing_mode"), @"Timing mode"); + /// + /// "Cycle grid display mode" + /// + public static LocalisableString EditorCycleGridDisplayMode => new TranslatableString(getKey(@"editor_cycle_grid_display_mode"), @"Cycle grid display mode"); + /// /// "Hold for HUD" /// From 20eeb36567c2c2afb232b7580729c4c52f4d2ec6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Sep 2021 18:35:47 +0900 Subject: [PATCH 2036/2442] Avoid `AliveObject` enumeration when not in kiai section --- .../Skinning/Legacy/LegacyCursorParticles.cs | 46 +++++++++++++------ 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index e1b7dbc3e3..2b0dfba1dd 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Textures; @@ -12,6 +13,7 @@ using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Utils; using osu.Game.Graphics; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.UI; using osu.Game.Screens.Play; @@ -32,7 +34,13 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private Player player { get; set; } [Resolved(canBeNull: true)] - private OsuPlayfield osuPlayfield { get; set; } + private OsuPlayfield playfield { get; set; } + + [Resolved(canBeNull: true)] + private GameplayBeatmap gameplayBeatmap { get; set; } + + [Resolved(canBeNull: true)] + private GameplayClock gameplayClock { get; set; } [BackgroundDependencyLoader] private void load(ISkinSource skin, OsuColour colours) @@ -65,27 +73,39 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy }; if (player != null) - { - breakSpewer.Active.BindTarget = player.IsBreakTime; - } + ((IBindable)breakSpewer.Active).BindTo(player.IsBreakTime); } protected override void Update() { - if (osuPlayfield == null) return; + if (playfield == null || gameplayBeatmap == null) return; - // find active kiai slider or spinner. - var kiaiHitObject = osuPlayfield.HitObjectContainer.AliveObjects.FirstOrDefault(h => - h.HitObject.Kiai && - ( - (h is DrawableSlider slider && slider.Tracking.Value) || - (h is DrawableSpinner spinner && spinner.RotationTracker.Tracking) - ) - ); + DrawableHitObject kiaiHitObject = null; + + // Check whether currently in a kiai section first. This is only done as an optimisation to avoid enumerating AliveObjects when not necessary. + if (gameplayBeatmap.ControlPointInfo.EffectPointAt(gameplayBeatmap.Time.Current).KiaiMode) + kiaiHitObject = playfield.HitObjectContainer.AliveObjects.FirstOrDefault(isTracking); kiaiSpewer.Active.Value = kiaiHitObject != null; } + private bool isTracking(DrawableHitObject h) + { + if (!h.HitObject.Kiai) + return false; + + switch (h) + { + case DrawableSlider slider: + return slider.Tracking.Value; + + case DrawableSpinner spinner: + return spinner.RotationTracker.Tracking; + } + + return false; + } + public bool OnPressed(KeyBindingPressEvent e) { handleInput(e.Action, true); From 10fe2382b08abf113d0ca020f11307284041cb9e Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Mon, 20 Sep 2021 10:07:42 -0400 Subject: [PATCH 2037/2442] Address most issues --- osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs | 2 +- .../Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs | 2 +- osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs | 5 ++++- osu.Game/Screens/Play/Player.cs | 5 ++++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 2b5228cab0..604df0b774 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -280,6 +280,6 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters } } - public override void Clear() => judgementsContainer.Clear(true); + protected override void Clear() => judgementsContainer.Clear(); } } diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs index ea64d1f4d9..19ba1910e6 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs @@ -33,7 +33,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters judgementsFlow.Push(GetColourForHitResult(judgement.Type)); } - public override void Clear() => judgementsFlow.Clear(true); + protected override void Clear() => judgementsFlow.Clear(); private class JudgementFlow : FillFlowContainer { diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs index 1871519ab5..f9d4d89d1b 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs @@ -77,7 +77,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters /// Invoked by when the active seeks through the current beatmap. /// Any inheritors of should have this method clear their container that displays the hit error results. /// - public abstract void Clear(); + protected abstract void Clear(); protected override void Dispose(bool isDisposing) { @@ -85,6 +85,9 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters if (processor != null) processor.NewJudgement -= OnNewJudgement; + + if (player != null) + player.OnSeek -= Clear; } } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index e982d02baf..cde3cda369 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -69,7 +69,10 @@ namespace osu.Game.Screens.Play public Action RestartRequested; - public Action OnSeek; + /// + /// Invoked when a seek has been performed via + /// + public event Action OnSeek; public bool HasFailed { get; private set; } From 36a20ab0b365d77878a76c0fbf4444bd6b67380f Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Mon, 20 Sep 2021 10:22:36 -0400 Subject: [PATCH 2038/2442] Resolve failed test compilation --- osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs | 2 +- .../Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs | 2 +- osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 604df0b774..39dafaffad 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -280,6 +280,6 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters } } - protected override void Clear() => judgementsContainer.Clear(); + public override void Clear() => judgementsContainer.Clear(); } } diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs index 19ba1910e6..5012be7249 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs @@ -33,7 +33,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters judgementsFlow.Push(GetColourForHitResult(judgement.Type)); } - protected override void Clear() => judgementsFlow.Clear(); + public override void Clear() => judgementsFlow.Clear(); private class JudgementFlow : FillFlowContainer { diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs index f9d4d89d1b..a864753b0c 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; @@ -77,7 +77,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters /// Invoked by when the active seeks through the current beatmap. /// Any inheritors of should have this method clear their container that displays the hit error results. /// - protected abstract void Clear(); + public abstract void Clear(); protected override void Dispose(bool isDisposing) { From 9a1db04920ea9034980d70bd9f0d26ad45862ca2 Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Mon, 20 Sep 2021 10:28:58 -0400 Subject: [PATCH 2039/2442] Resolve `GameplayClockContainer` instead of `Player` --- osu.Game/Screens/Play/GameplayClockContainer.cs | 8 ++++++++ .../Play/HUD/HitErrorMeters/HitErrorMeter.cs | 14 +++++++------- osu.Game/Screens/Play/Player.cs | 6 ------ 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index f791da80c8..0c9b827a41 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -13,6 +13,7 @@ namespace osu.Game.Screens.Play /// /// Encapsulates gameplay timing logic and provides a via DI for gameplay components to use. /// + [Cached] public abstract class GameplayClockContainer : Container, IAdjustableClock { /// @@ -35,6 +36,11 @@ namespace osu.Game.Screens.Play /// protected IClock SourceClock { get; private set; } + /// + /// Invoked when a seek has been performed via + /// + public event Action OnSeek; + /// /// Creates a new . /// @@ -88,6 +94,8 @@ namespace osu.Game.Screens.Play // Manually process to make sure the gameplay clock is correctly updated after a seek. GameplayClock.UnderlyingClock.ProcessFrame(); + + OnSeek?.Invoke(); } /// diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs index a864753b0c..c7b06a3a2c 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters private OsuColour colours { get; set; } [Resolved(canBeNull: true)] - private Player player { get; set; } + private GameplayClockContainer gameplayClockContainer { get; set; } public bool UsesFixedAnchor { get; set; } @@ -37,8 +37,8 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters { base.LoadComplete(); - if (player != null) - player.OnSeek += Clear; + if (gameplayClockContainer != null) + gameplayClockContainer.OnSeek += Clear; processor.NewJudgement += OnNewJudgement; } @@ -74,7 +74,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters } /// - /// Invoked by when the active seeks through the current beatmap. + /// Invoked by . /// Any inheritors of should have this method clear their container that displays the hit error results. /// public abstract void Clear(); @@ -86,8 +86,8 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters if (processor != null) processor.NewJudgement -= OnNewJudgement; - if (player != null) - player.OnSeek -= Clear; + if (gameplayClockContainer != null) + gameplayClockContainer.OnSeek -= Clear; } } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index cde3cda369..a9a74d30d4 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -69,11 +69,6 @@ namespace osu.Game.Screens.Play public Action RestartRequested; - /// - /// Invoked when a seek has been performed via - /// - public event Action OnSeek; - public bool HasFailed { get; private set; } private Bindable mouseWheelDisabled; @@ -592,7 +587,6 @@ namespace osu.Game.Screens.Play public void Seek(double time) { GameplayClockContainer.Seek(time); - OnSeek?.Invoke(); } private ScheduledDelegate frameStablePlaybackResetDelegate; From a3464c98a7b11826694eddf3587399a9ec7ce648 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Sep 2021 23:51:58 +0900 Subject: [PATCH 2040/2442] Fix `KeyCounterDisplay` potentially getting stuck invisible due to autosize masking Closes #14793. --- osu.Game/Screens/Play/KeyCounterDisplay.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/KeyCounterDisplay.cs b/osu.Game/Screens/Play/KeyCounterDisplay.cs index 2ed4afafd3..66a44e5314 100644 --- a/osu.Game/Screens/Play/KeyCounterDisplay.cs +++ b/osu.Game/Screens/Play/KeyCounterDisplay.cs @@ -33,8 +33,6 @@ namespace osu.Game.Screens.Play public KeyCounterDisplay() { - AutoSizeAxes = Axes.Both; - InternalChild = KeyFlow = new FillFlowContainer { Direction = FillDirection.Horizontal, @@ -42,6 +40,15 @@ namespace osu.Game.Screens.Play }; } + protected override void Update() + { + base.Update(); + + // Don't use autosize as it will shrink to zero when KeyFlow is hidden. + // In turn this can cause the display to be masked off screen and never become visible again. + Size = KeyFlow.Size; + } + public override void Add(KeyCounter key) { if (key == null) throw new ArgumentNullException(nameof(key)); From b5af01f4561757857e5863b1b306efb11f1049f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Sep 2021 20:13:06 +0200 Subject: [PATCH 2041/2442] Always show rectangular grid in osu! composer --- .../Edit/OsuHitObjectComposer.cs | 22 +++---------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index a2ee646341..3e4711db58 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.Edit { RelativeSizeAxes = Axes.Both }, - rectangularPositionSnapGridContainer = new Container + rectangularPositionSnapGrid = new OsuRectangularPositionSnapGrid(EditorBeatmap.BeatmapInfo.GridSize) { RelativeSizeAxes = Axes.Both } @@ -89,8 +89,6 @@ namespace osu.Game.Rulesets.Osu.Edit gridSnapToggle.ValueChanged += _ => { - updateRectangularPositionSnapGrid(); - if (gridSnapToggle.Value == TernaryState.True) distanceSnapToggle.Value = TernaryState.False; }; @@ -111,6 +109,8 @@ namespace osu.Game.Rulesets.Osu.Edit private readonly Cached distanceSnapGridCache = new Cached(); private double? lastDistanceSnapGridTime; + private RectangularPositionSnapGrid rectangularPositionSnapGrid; + protected override void Update() { base.Update(); @@ -298,21 +298,5 @@ namespace osu.Game.Rulesets.Osu.Edit return new OsuDistanceSnapGrid((OsuHitObject)sourceObject, (OsuHitObject)targetObject); } - - private Container rectangularPositionSnapGridContainer; - private RectangularPositionSnapGrid rectangularPositionSnapGrid; - - private void updateRectangularPositionSnapGrid() - { - rectangularPositionSnapGridContainer.Clear(); - - if (gridSnapToggle.Value == TernaryState.True) - { - rectangularPositionSnapGridContainer.Add(rectangularPositionSnapGrid = new OsuRectangularPositionSnapGrid(EditorBeatmap.BeatmapInfo.GridSize) - { - RelativeSizeAxes = Axes.Both - }); - } - } } } From 52542374e8bb07b5e2e76a408adb1139313b9b62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Sep 2021 20:14:28 +0200 Subject: [PATCH 2042/2442] Fix rectangular grid snap being always active --- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 3e4711db58..027334ba8b 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -42,12 +42,12 @@ namespace osu.Game.Rulesets.Osu.Edit }; private readonly Bindable distanceSnapToggle = new Bindable(); - private readonly Bindable gridSnapToggle = new Bindable(); + private readonly Bindable rectangularGridSnapToggle = new Bindable(); protected override IEnumerable CreateTernaryButtons() => base.CreateTernaryButtons().Concat(new[] { new TernaryButton(distanceSnapToggle, "Distance Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Ruler }), - new TernaryButton(gridSnapToggle, "Grid Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Th }) + new TernaryButton(rectangularGridSnapToggle, "Grid Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Th }) }); private BindableList selectedHitObjects; @@ -84,12 +84,12 @@ namespace osu.Game.Rulesets.Osu.Edit updateDistanceSnapGrid(); if (distanceSnapToggle.Value == TernaryState.True) - gridSnapToggle.Value = TernaryState.False; + rectangularGridSnapToggle.Value = TernaryState.False; }; - gridSnapToggle.ValueChanged += _ => + rectangularGridSnapToggle.ValueChanged += _ => { - if (gridSnapToggle.Value == TernaryState.True) + if (rectangularGridSnapToggle.Value == TernaryState.True) distanceSnapToggle.Value = TernaryState.False; }; @@ -142,13 +142,13 @@ namespace osu.Game.Rulesets.Osu.Edit if (positionSnap.ScreenSpacePosition != screenSpacePosition) return positionSnap; - if (distanceSnapGrid != null) + if (distanceSnapToggle.Value == TernaryState.True && distanceSnapGrid != null) { (Vector2 pos, double time) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(screenSpacePosition)); return new SnapResult(distanceSnapGrid.ToScreenSpace(pos), time, PlayfieldAtScreenSpacePosition(screenSpacePosition)); } - if (rectangularPositionSnapGrid != null) + if (rectangularGridSnapToggle.Value == TernaryState.True) { Vector2 pos = rectangularPositionSnapGrid.GetSnappedPosition(rectangularPositionSnapGrid.ToLocalSpace(screenSpacePosition)); return new SnapResult(rectangularPositionSnapGrid.ToScreenSpace(pos), null, PlayfieldAtScreenSpacePosition(screenSpacePosition)); From fe21577f113d89d2d648442c53f19dfcf5fb657a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Sep 2021 20:32:34 +0200 Subject: [PATCH 2043/2442] Adjust grid snap in line with new logic --- .../Editor/TestSceneOsuEditorGrids.cs | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs index 81e44a57db..77aac54929 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs @@ -4,7 +4,9 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Testing; +using osu.Framework.Utils; using osu.Game.Rulesets.Osu.Edit; +using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles; using osu.Game.Tests.Visual; using osuTK; using osuTK.Input; @@ -21,14 +23,33 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("enable distance snap grid", () => InputManager.Key(Key.T)); AddStep("select second object", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.ElementAt(1))); AddUntilStep("distance snap grid visible", () => this.ChildrenOfType().Any()); + rectangularGridActive(false); AddStep("enable rectangular grid", () => InputManager.Key(Key.Y)); AddUntilStep("distance snap grid hidden", () => !this.ChildrenOfType().Any()); - AddUntilStep("rectangular grid visible", () => this.ChildrenOfType().Any()); + rectangularGridActive(true); AddStep("enable distance snap grid", () => InputManager.Key(Key.T)); + AddStep("select second object", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.ElementAt(1))); AddUntilStep("distance snap grid visible", () => this.ChildrenOfType().Any()); - AddUntilStep("rectangular grid hidden", () => !this.ChildrenOfType().Any()); + rectangularGridActive(false); + } + + private void rectangularGridActive(bool active) + { + AddStep("choose placement tool", () => InputManager.Key(Key.Number2)); + AddStep("move cursor to (1, 1)", () => + { + var composer = Editor.ChildrenOfType().Single(); + InputManager.MoveMouseTo(composer.ToScreenSpace(new Vector2(1, 1))); + }); + + if (active) + AddAssert("placement blueprint at (0, 0)", () => Precision.AlmostEquals(Editor.ChildrenOfType().Single().HitObject.Position, new Vector2(0, 0))); + else + AddAssert("placement blueprint at (1, 1)", () => Precision.AlmostEquals(Editor.ChildrenOfType().Single().HitObject.Position, new Vector2(1, 1))); + + AddStep("choose selection tool", () => InputManager.Key(Key.Number1)); } [Test] From 0d7dac03f439c1333ef2572ae8590728bfa1934b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Sep 2021 20:34:22 +0200 Subject: [PATCH 2044/2442] Start with largest grid size --- osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs | 4 ++-- osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs index 77aac54929..00813dee3a 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs @@ -57,12 +57,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { AddStep("enable rectangular grid", () => InputManager.Key(Key.Y)); AddUntilStep("rectangular grid visible", () => this.ChildrenOfType().Any()); - gridSizeIs(4); + gridSizeIs(32); + nextGridSizeIs(4); nextGridSizeIs(8); nextGridSizeIs(16); nextGridSizeIs(32); - nextGridSizeIs(4); } private void nextGridSizeIs(int size) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs b/osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs index da18d3175e..74cb49bedc 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Osu.Edit { private static readonly int[] grid_sizes = { 4, 8, 16, 32 }; - private int currentGridSizeIndex; + private int currentGridSizeIndex = grid_sizes.Length - 1; public OsuRectangularPositionSnapGrid(int gridSize) : base(OsuPlayfield.BASE_SIZE / 2) From d15bd5a15e7c293dfa5a58a0049c0b74de024a37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Sep 2021 20:39:39 +0200 Subject: [PATCH 2045/2442] Store grid size back to beatmap on change --- .../Editor/TestSceneOsuEditorGrids.cs | 3 ++- .../Edit/OsuHitObjectComposer.cs | 2 +- .../Edit/OsuRectangularPositionSnapGrid.cs | 19 ++++++++++++++++--- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs index 00813dee3a..a24cb6526d 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs @@ -72,6 +72,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor } private void gridSizeIs(int size) - => AddAssert($"grid size is {size}", () => this.ChildrenOfType().Single().Spacing == new Vector2(size)); + => AddAssert($"grid size is {size}", () => this.ChildrenOfType().Single().Spacing == new Vector2(size) + && EditorBeatmap.BeatmapInfo.GridSize == size); } } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 027334ba8b..1e84ec80e1 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.Edit { RelativeSizeAxes = Axes.Both }, - rectangularPositionSnapGrid = new OsuRectangularPositionSnapGrid(EditorBeatmap.BeatmapInfo.GridSize) + rectangularPositionSnapGrid = new OsuRectangularPositionSnapGrid { RelativeSizeAxes = Axes.Both } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs b/osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs index 74cb49bedc..b93585af09 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs @@ -2,10 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Allocation; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Input.Bindings; using osu.Game.Rulesets.Osu.UI; +using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Compose.Components; using osuTK; @@ -17,10 +19,18 @@ namespace osu.Game.Rulesets.Osu.Edit private int currentGridSizeIndex = grid_sizes.Length - 1; - public OsuRectangularPositionSnapGrid(int gridSize) + [Resolved] + private EditorBeatmap editorBeatmap { get; set; } + + public OsuRectangularPositionSnapGrid() : base(OsuPlayfield.BASE_SIZE / 2) { - var gridSizeIndex = Array.IndexOf(grid_sizes, gridSize); + } + + [BackgroundDependencyLoader] + private void load() + { + var gridSizeIndex = Array.IndexOf(grid_sizes, editorBeatmap.BeatmapInfo.GridSize); if (gridSizeIndex > 0) currentGridSizeIndex = gridSizeIndex; updateSpacing(); @@ -34,7 +44,10 @@ namespace osu.Game.Rulesets.Osu.Edit private void updateSpacing() { - Spacing = new Vector2(grid_sizes[currentGridSizeIndex]); + int gridSize = grid_sizes[currentGridSizeIndex]; + + editorBeatmap.BeatmapInfo.GridSize = gridSize; + Spacing = new Vector2(gridSize); } public bool OnPressed(KeyBindingPressEvent e) From fb416c79e906c7fd95c031f3f6de00778ac36f7f Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Mon, 20 Sep 2021 15:01:03 -0400 Subject: [PATCH 2046/2442] Fully revert `Player` --- osu.Game/Screens/Play/Player.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a9a74d30d4..e8a2790c94 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -584,10 +584,7 @@ namespace osu.Game.Screens.Play /// Seek to a specific time in gameplay. /// /// The destination time to seek to. - public void Seek(double time) - { - GameplayClockContainer.Seek(time); - } + public void Seek(double time) => GameplayClockContainer.Seek(time); private ScheduledDelegate frameStablePlaybackResetDelegate; From b715b89edc3c2c4ea82db029effca7cda912f6d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Sep 2021 19:54:05 +0000 Subject: [PATCH 2047/2442] Bump SharpCompress from 0.28.3 to 0.29.0 in /osu.Game Bumps [SharpCompress](https://github.com/adamhathcock/sharpcompress) from 0.28.3 to 0.29.0. - [Release notes](https://github.com/adamhathcock/sharpcompress/releases) - [Commits](https://github.com/adamhathcock/sharpcompress/compare/0.28.3...0.29) --- updated-dependencies: - dependency-name: SharpCompress dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- 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 0460de6d72..e6afbe383a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -39,7 +39,7 @@ - + From 9ea9fa5f20db5b48dcf453aa92854285929324e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 11:38:04 +0900 Subject: [PATCH 2048/2442] Remove issue template to avoid the average use submitting issues --- .github/ISSUE_TEMPLATE/01-bug-issues.md | 30 ------------------------- .github/ISSUE_TEMPLATE/config.yml | 8 +++---- 2 files changed, 4 insertions(+), 34 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/01-bug-issues.md diff --git a/.github/ISSUE_TEMPLATE/01-bug-issues.md b/.github/ISSUE_TEMPLATE/01-bug-issues.md deleted file mode 100644 index 7026179259..0000000000 --- a/.github/ISSUE_TEMPLATE/01-bug-issues.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -name: Bug Report -about: Report a bug or crash to desktop ---- - - - - -**Describe the bug:** - -**Screenshots or videos showing encountered issue:** - -**osu!lazer version:** - -**Logs:** - - diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index c62231e8e0..47a6a4c3d3 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,12 +1,12 @@ blank_issues_enabled: false contact_links: - - name: Suggestions or feature request - url: https://github.com/ppy/osu/discussions/categories/ideas - about: Got something you think should change or be added? Search for or start a new discussion! - name: Help url: https://github.com/ppy/osu/discussions/categories/q-a about: osu! not working as you'd expect? Not sure it's a bug? Check the Q&A section! + - name: Suggestions or feature request + url: https://github.com/ppy/osu/discussions/categories/ideas + about: Got something you think should change or be added? Search for or start a new discussion! - name: osu!stable issues url: https://github.com/ppy/osu-stable-issues - about: For osu!stable bugs (not osu!lazer), check out the dedicated repository. Note that we only accept serious bug reports. + about: For osu!(stable) - ie. the current "live" game version, check out the dedicated repository. Note that this is for serious bug reports only, not tech support. From e3542878045a16a72d0cc0a091c96c42eef8df9c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 9 Sep 2021 14:36:49 +0900 Subject: [PATCH 2049/2442] Fix incorrect ruleset name --- .github/workflows/test-diffcalc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 4274d01bab..92ca1e1a8b 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -33,7 +33,7 @@ jobs: ruleset: - { name: osu, id: 0 } - { name: taiko, id: 1 } - - { name: catch, id: 2 } + - { name: fruits, id: 2 } - { name: mania, id: 3 } services: From fa374e67e7cc001e77d7c2ff6fd49d1f25167b17 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 9 Sep 2021 14:42:39 +0900 Subject: [PATCH 2050/2442] Single line condition --- .github/workflows/test-diffcalc.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 92ca1e1a8b..bfa4a3a8a0 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -22,10 +22,7 @@ jobs: runs-on: ubuntu-latest continue-on-error: true - if: | - github.event.issue.pull_request && - contains(github.event.comment.body, '!pp check') && - (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') + if: github.event.issue.pull_request && contains(github.event.comment.body, '!pp check') && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') strategy: fail-fast: false From 0f8e570b845f9266e03defc486c6fa2557ac3dd0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Sep 2021 12:18:19 +0900 Subject: [PATCH 2051/2442] Use top 1000 data --- .github/workflows/test-diffcalc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index bfa4a3a8a0..0e53bd2c88 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -102,7 +102,7 @@ jobs: # Initial data imports - name: Download + import data run: | - PERFORMANCE_DATA_NAME=$(curl https://data.ppy.sh/ | grep performance_${{ matrix.ruleset.name }}_top | tail -1 | awk -F "\"" '{print $2}' | sed 's/\.tar\.bz2//g') + PERFORMANCE_DATA_NAME=$(curl https://data.ppy.sh/ | grep performance_${{ matrix.ruleset.name }}_top_1000 | tail -1 | awk -F "\"" '{print $2}' | sed 's/\.tar\.bz2//g') BEATMAPS_DATA_NAME=$(curl https://data.ppy.sh/ | grep osu_files | tail -1 | awk -F "\"" '{print $2}' | sed 's/\.tar\.bz2//g') # Set env variable for further steps. From fc5fd203d60c75bcc1ec0df3296a23d35e5d6aa3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Sep 2021 12:46:27 +0900 Subject: [PATCH 2052/2442] Output sql import process --- .github/workflows/test-diffcalc.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 0e53bd2c88..6a8ecbbae5 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -120,8 +120,9 @@ jobs: mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} -e "CREATE DATABASE osu_master" mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} -e "CREATE DATABASE osu_pr" - cat *.sql | mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} --database=osu_master - cat *.sql | mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} --database=osu_pr + echo "Importing SQL..." + { pv -f *.sql | mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} --database=osu_master; } 2>&1 | stdbuf -oL tr '\r' '\n' + { pv -f *.sql | mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} --database=osu_pr; } 2>&1 | stdbuf -oL tr '\r' '\n' # Run diffcalc - name: Run diffcalc (master) From 6bfb31a63559358013447a6ec54609ebb6418112 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Sep 2021 12:55:33 +0900 Subject: [PATCH 2053/2442] Install pv --- .github/workflows/test-diffcalc.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 6a8ecbbae5..54365ed5f6 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -49,6 +49,9 @@ jobs: echo "${{ github.event.comment.body }} doesn't contain ${{ matrix.ruleset.id }}" exit 1 + - name: Install dependencies + run: sudo apt-get install -y pv + - name: Verify MySQL connection from host run: | sudo apt-get install -y mysql-client From 9d4c5e9cb6bbf12efcc3a866621bbdfe778df1d3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 16 Sep 2021 14:24:30 +0900 Subject: [PATCH 2054/2442] Add filename to output rows --- .github/workflows/test-diffcalc.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 54365ed5f6..79c1497dca 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -148,6 +148,7 @@ jobs: SELECT m.beatmap_id, m.mods, + b.filename, m.diff_unified as 'sr_master', p.diff_unified as 'sr_pr', (p.diff_unified - m.diff_unified) as 'diff' @@ -156,6 +157,8 @@ jobs: ON m.beatmap_id = p.beatmap_id AND m.mode = p.mode AND m.mods = p.mods + JOIN osu_pr.osu_beatmaps b + ON b.beatmap_id = p.beatmap_id WHERE abs(m.diff_unified - p.diff_unified) > 0.1 ORDER BY abs(m.diff_unified - p.diff_unified) DESC From 06e11484721f9cfa5c58003e9198008bab7387d0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Sep 2021 19:47:41 +0900 Subject: [PATCH 2055/2442] Run on self-hosted runner --- .github/workflows/test-diffcalc.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 79c1497dca..9c633a597d 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -12,6 +12,7 @@ on: env: DB_USER: root DB_HOST: 127.0.0.1 + DB_PORT: 33306:3306 CONCURRENCY: 4 ALLOW_DOWNLOAD: 1 SAVE_DOWNLOADED: 1 @@ -19,7 +20,7 @@ env: jobs: diffcalc: name: Diffcalc - runs-on: ubuntu-latest + runs-on: self-hosted continue-on-error: true if: github.event.issue.pull_request && contains(github.event.comment.body, '!pp check') && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') @@ -39,7 +40,7 @@ jobs: env: MYSQL_ALLOW_EMPTY_PASSWORD: yes ports: - - 3306:3306 + - 33306:3306 options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 steps: @@ -54,8 +55,7 @@ jobs: - name: Verify MySQL connection from host run: | - sudo apt-get install -y mysql-client - mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} -e "SHOW DATABASES" + mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e "SHOW DATABASES" - name: Create directory structure run: | @@ -120,12 +120,12 @@ jobs: cd $GITHUB_WORKSPACE/$PERFORMANCE_DATA_NAME - mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} -e "CREATE DATABASE osu_master" - mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} -e "CREATE DATABASE osu_pr" + mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e "CREATE DATABASE osu_master" + mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e "CREATE DATABASE osu_pr" echo "Importing SQL..." - { pv -f *.sql | mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} --database=osu_master; } 2>&1 | stdbuf -oL tr '\r' '\n' - { pv -f *.sql | mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} --database=osu_pr; } 2>&1 | stdbuf -oL tr '\r' '\n' + { pv -f *.sql | mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} --database=osu_master; } 2>&1 | stdbuf -oL tr '\r' '\n' + { pv -f *.sql | mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} --database=osu_pr; } 2>&1 | stdbuf -oL tr '\r' '\n' # Run diffcalc - name: Run diffcalc (master) @@ -144,7 +144,7 @@ jobs: # Print diffs - name: Print diffs run: | - mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} -e " + mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e " SELECT m.beatmap_id, m.mods, From 6b3a37e262b1da27a58570178946609bbced85c6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 18 Sep 2021 03:49:30 +0900 Subject: [PATCH 2056/2442] Remove deps step --- .github/workflows/test-diffcalc.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 9c633a597d..565af6ad3c 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -50,9 +50,6 @@ jobs: echo "${{ github.event.comment.body }} doesn't contain ${{ matrix.ruleset.id }}" exit 1 - - name: Install dependencies - run: sudo apt-get install -y pv - - name: Verify MySQL connection from host run: | mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e "SHOW DATABASES" @@ -164,4 +161,4 @@ jobs: DESC LIMIT 10000;" - # Todo: Run ppcalc \ No newline at end of file + # Todo: Run ppcalc From 1d560ed1226e648d05bc003c6939273f21112b67 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Sep 2021 23:18:31 +0900 Subject: [PATCH 2057/2442] Use optimisation branch for now --- .github/workflows/test-diffcalc.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 565af6ad3c..db1fa7a088 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -63,7 +63,8 @@ jobs: - name: Checkout osu (master) uses: actions/checkout@v2 with: - repository: ppy/osu + repository: peppy/osu + ref: 'diffcalc-optimisations' path: 'master/osu' - name: Checkout osu (pr) uses: actions/checkout@v2 From 7d5e4ae0a2386e82748219bf57d767610d0403de Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 18 Sep 2021 03:57:25 +0900 Subject: [PATCH 2058/2442] Drop service based mysql usage (use host instead) --- .github/workflows/test-diffcalc.yml | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index db1fa7a088..188e074e07 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -12,7 +12,7 @@ on: env: DB_USER: root DB_HOST: 127.0.0.1 - DB_PORT: 33306:3306 + DB_PORT: 3306 CONCURRENCY: 4 ALLOW_DOWNLOAD: 1 SAVE_DOWNLOADED: 1 @@ -34,15 +34,6 @@ jobs: - { name: fruits, id: 2 } - { name: mania, id: 3 } - services: - mysql: - image: mysql:8.0 - env: - MYSQL_ALLOW_EMPTY_PASSWORD: yes - ports: - - 33306:3306 - options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 - steps: - name: Verify ruleset if: contains(github.event.comment.body, matrix.ruleset.id) == false @@ -54,6 +45,11 @@ jobs: run: | mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e "SHOW DATABASES" + - name: Drop previous databases + run: | + mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e "DROP DATABASE IF EXISTS osu_master" + mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e "DROP DATABASE IF EXISTS osu_pr" + - name: Create directory structure run: | mkdir -p $GITHUB_WORKSPACE/master/ From 7c2b8fc650f02c25509d8cdf2ebf3db42b6777f8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 00:37:12 +0900 Subject: [PATCH 2059/2442] Apply new skip insert rule --- .github/workflows/test-diffcalc.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 188e074e07..23b5c8bd2c 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -16,6 +16,7 @@ env: CONCURRENCY: 4 ALLOW_DOWNLOAD: 1 SAVE_DOWNLOADED: 1 + SKIP_INSERT_ATTRIBUTES: 1 jobs: diffcalc: @@ -71,12 +72,14 @@ jobs: - name: Checkout osu-difficulty-calculator (master) uses: actions/checkout@v2 with: - repository: ppy/osu-difficulty-calculator + repository: peppy/osu-difficulty-calculator + ref: 'bypass-attrib-row-insert' path: 'master/osu-difficulty-calculator' - name: Checkout osu-difficulty-calculator (pr) uses: actions/checkout@v2 with: - repository: ppy/osu-difficulty-calculator + repository: peppy/osu-difficulty-calculator + ref: 'bypass-attrib-row-insert' path: 'pr/osu-difficulty-calculator' - name: Install .NET 5.0.x From a46fe5da75793867394fe1072268dc44b4a4a49c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 01:14:33 +0900 Subject: [PATCH 2060/2442] Simplify action --- .github/workflows/test-diffcalc.yml | 44 ++++++++++++++++++----------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 23b5c8bd2c..b5937c1378 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -10,9 +10,6 @@ on: types: [ created ] env: - DB_USER: root - DB_HOST: 127.0.0.1 - DB_PORT: 3306 CONCURRENCY: 4 ALLOW_DOWNLOAD: 1 SAVE_DOWNLOADED: 1 @@ -44,12 +41,14 @@ jobs: - name: Verify MySQL connection from host run: | - mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e "SHOW DATABASES" + mysql -e "SHOW DATABASES" - name: Drop previous databases run: | - mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e "DROP DATABASE IF EXISTS osu_master" - mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e "DROP DATABASE IF EXISTS osu_pr" + for db in osu_master osu_pr + do + mysql -e "DROP DATABASE IF EXISTS $db" + done - name: Create directory structure run: | @@ -68,7 +67,6 @@ jobs: with: path: 'pr/osu' - # Checkout osu-difficulty-calculator - name: Checkout osu-difficulty-calculator (master) uses: actions/checkout@v2 with: @@ -99,7 +97,6 @@ jobs: ./UseLocalOsu.sh dotnet build - # Initial data imports - name: Download + import data run: | PERFORMANCE_DATA_NAME=$(curl https://data.ppy.sh/ | grep performance_${{ matrix.ruleset.name }}_top_1000 | tail -1 | awk -F "\"" '{print $2}' | sed 's/\.tar\.bz2//g') @@ -112,19 +109,33 @@ jobs: wget https://data.ppy.sh/$PERFORMANCE_DATA_NAME.tar.bz2 wget https://data.ppy.sh/$BEATMAPS_DATA_NAME.tar.bz2 + tar -xf $PERFORMANCE_DATA_NAME.tar.bz2 tar -xf $BEATMAPS_DATA_NAME.tar.bz2 - cd $GITHUB_WORKSPACE/$PERFORMANCE_DATA_NAME + cd $PERFORMANCE_DATA_NAME - mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e "CREATE DATABASE osu_master" - mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e "CREATE DATABASE osu_pr" + for db in osu_master osu_pr + do + echo "Setting up database $db.." - echo "Importing SQL..." - { pv -f *.sql | mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} --database=osu_master; } 2>&1 | stdbuf -oL tr '\r' '\n' - { pv -f *.sql | mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} --database=osu_pr; } 2>&1 | stdbuf -oL tr '\r' '\n' + mysql -e "CREATE DATABASE $db" + + echo "Importing beatmaps..." + cat osu_beatmaps.sql | mysql $db + cat osu_beatmapsets.sql | mysql $db + + mysql $db -e "CREATE TABLE `osu_beatmap_difficulty` ( + `beatmap_id` int unsigned NOT NULL, + `mode` tinyint NOT NULL DEFAULT '0', + `mods` int unsigned NOT NULL, + `diff_unified` float NOT NULL, + `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`beatmap_id`,`mode`,`mods`), + KEY `diff_sort` (`mode`,`mods`,`diff_unified`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;" + done - # Run diffcalc - name: Run diffcalc (master) env: DB_NAME: osu_master @@ -138,10 +149,9 @@ jobs: cd $GITHUB_WORKSPACE/pr/osu-difficulty-calculator/osu.Server.DifficultyCalculator dotnet run -c:Release -- all -m ${{ matrix.ruleset.id }} -ac -c ${{ env.CONCURRENCY }} - # Print diffs - name: Print diffs run: | - mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e " + mysql -e " SELECT m.beatmap_id, m.mods, From 5b9cab8b1fca1054a4eebea5af8613853b831cd5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 01:44:07 +0900 Subject: [PATCH 2061/2442] Add more log output and quiet `wget` --- .github/workflows/test-diffcalc.yml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index b5937c1378..e9fd624168 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -107,10 +107,14 @@ jobs: cd $GITHUB_WORKSPACE - wget https://data.ppy.sh/$PERFORMANCE_DATA_NAME.tar.bz2 - wget https://data.ppy.sh/$BEATMAPS_DATA_NAME.tar.bz2 - + echo "Downloading database dump $PERFORMANCE_DATA_NAME.." + wget -q -nc https://data.ppy.sh/$PERFORMANCE_DATA_NAME.tar.bz2 + echo "Extracting.." tar -xf $PERFORMANCE_DATA_NAME.tar.bz2 + + echo "Downloading beatmap dump $BEATMAPS_DATA_NAME.." + wget -q https://data.ppy.sh/$BEATMAPS_DATA_NAME.tar.bz2 + echo "Extracting.." tar -xf $BEATMAPS_DATA_NAME.tar.bz2 cd $PERFORMANCE_DATA_NAME @@ -121,19 +125,21 @@ jobs: mysql -e "CREATE DATABASE $db" - echo "Importing beatmaps..." + echo "Importing beatmaps.." cat osu_beatmaps.sql | mysql $db + echo "Importing beatmapsets.." cat osu_beatmapsets.sql | mysql $db - mysql $db -e "CREATE TABLE `osu_beatmap_difficulty` ( + echo "Creating table structure.." + mysql $db -e 'CREATE TABLE `osu_beatmap_difficulty` ( `beatmap_id` int unsigned NOT NULL, - `mode` tinyint NOT NULL DEFAULT '0', + `mode` tinyint NOT NULL DEFAULT 0, `mods` int unsigned NOT NULL, `diff_unified` float NOT NULL, `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`beatmap_id`,`mode`,`mods`), KEY `diff_sort` (`mode`,`mods`,`diff_unified`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;" + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;' done - name: Run diffcalc (master) From f73ebfcea188b19de4b9f7f804229d15b6de4bbc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 01:44:54 +0900 Subject: [PATCH 2062/2442] Don't redownload if file already exists --- .github/workflows/test-diffcalc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index e9fd624168..dd623f59ce 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -113,7 +113,7 @@ jobs: tar -xf $PERFORMANCE_DATA_NAME.tar.bz2 echo "Downloading beatmap dump $BEATMAPS_DATA_NAME.." - wget -q https://data.ppy.sh/$BEATMAPS_DATA_NAME.tar.bz2 + wget -q -nc https://data.ppy.sh/$BEATMAPS_DATA_NAME.tar.bz2 echo "Extracting.." tar -xf $BEATMAPS_DATA_NAME.tar.bz2 From afcf3edec99d76c7937683abd249d9efcc5b0a39 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 02:27:21 +0900 Subject: [PATCH 2063/2442] Build build matrix dynamically --- .github/workflows/test-diffcalc.yml | 51 ++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index dd623f59ce..75742f2bde 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -16,29 +16,50 @@ env: SKIP_INSERT_ATTRIBUTES: 1 jobs: + metadata: + runs-on: self-hosted + if: github.event.issue.pull_request && contains(github.event.comment.body, '!pp check') && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') + outputs: + matrix: ${{ steps.generate-matrix.outputs.matrix }} + continue: ${{ steps.generate-matrix.outputs.continue }} + steps: + - name: generate matrix + id: generate-matrix + run: | + if [[ "${{ github.event.comment.body }}" =~ "1" ]] ; then + MATRIX_PROJECTS_JSON+='{ "name": "osu", "id": 0 },' + fi + if [[ "${{ github.event.comment.body }}" =~ "2" ]] ; then + MATRIX_PROJECTS_JSON+='{ "name": "taiko", "id": 1 },' + fi + if [[ "${{ github.event.comment.body }}" =~ "3" ]] ; then + MATRIX_PROJECTS_JSON+='{ "name": "catch", "id": 2 },' + fi + if [[ "${{ github.event.comment.body }}" =~ "4" ]] ; then + MATRIX_PROJECTS_JSON+='{ "name": "mania", "id": 3 },' + fi + + if [[ "${MATRIX_PROJECTS_JSON}" != "" ]]; then + MATRIX_JSON="{ \"ruleset\": [ ${MATRIX_PROJECTS_JSON} ] }" + echo "${MATRIX_JSON}" + CONTINUE="yes" + else + CONTINUE="no" + fi + + echo "::set-output name=continue::${CONTINUE}" + echo "::set-output name=matrix::${MATRIX_JSON}" diffcalc: name: Diffcalc runs-on: self-hosted continue-on-error: true - - if: github.event.issue.pull_request && contains(github.event.comment.body, '!pp check') && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') + if: needs.metadata.outputs.continue == 'yes' + needs: metadata strategy: - fail-fast: false - matrix: - ruleset: - - { name: osu, id: 0 } - - { name: taiko, id: 1 } - - { name: fruits, id: 2 } - - { name: mania, id: 3 } + matrix: ${{ fromJson(needs.metadata.outputs.matrix) }} steps: - - name: Verify ruleset - if: contains(github.event.comment.body, matrix.ruleset.id) == false - run: | - echo "${{ github.event.comment.body }} doesn't contain ${{ matrix.ruleset.id }}" - exit 1 - - name: Verify MySQL connection from host run: | mysql -e "SHOW DATABASES" From f282bd6f42c758be1e111755ee33839e362f0638 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 02:47:27 +0900 Subject: [PATCH 2064/2442] Tidy up naming --- .github/workflows/test-diffcalc.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 75742f2bde..c83cbf5d61 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -4,7 +4,7 @@ # !pp check 0 2 | Runs only the osu! and catch rulesets. # -name: Diffcalc Consistency Checks +name: Difficulty Calculation on: issue_comment: types: [ created ] @@ -17,13 +17,14 @@ env: jobs: metadata: + name: Check for requests runs-on: self-hosted if: github.event.issue.pull_request && contains(github.event.comment.body, '!pp check') && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') outputs: matrix: ${{ steps.generate-matrix.outputs.matrix }} continue: ${{ steps.generate-matrix.outputs.continue }} steps: - - name: generate matrix + - name: Construct build matrix id: generate-matrix run: | if [[ "${{ github.event.comment.body }}" =~ "1" ]] ; then @@ -50,15 +51,13 @@ jobs: echo "::set-output name=continue::${CONTINUE}" echo "::set-output name=matrix::${MATRIX_JSON}" diffcalc: - name: Diffcalc + name: Run runs-on: self-hosted continue-on-error: true if: needs.metadata.outputs.continue == 'yes' needs: metadata - strategy: matrix: ${{ fromJson(needs.metadata.outputs.matrix) }} - steps: - name: Verify MySQL connection from host run: | From 192089db61398096d61494b3f77d66687de0d43d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 02:54:37 +0900 Subject: [PATCH 2065/2442] Use keywords instead of IDs --- .github/workflows/test-diffcalc.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index c83cbf5d61..edb9fbd323 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -27,16 +27,16 @@ jobs: - name: Construct build matrix id: generate-matrix run: | - if [[ "${{ github.event.comment.body }}" =~ "1" ]] ; then + if [[ "${{ github.event.comment.body }}" =~ "osu" ]] ; then MATRIX_PROJECTS_JSON+='{ "name": "osu", "id": 0 },' fi - if [[ "${{ github.event.comment.body }}" =~ "2" ]] ; then + if [[ "${{ github.event.comment.body }}" =~ "taiko" ]] ; then MATRIX_PROJECTS_JSON+='{ "name": "taiko", "id": 1 },' fi - if [[ "${{ github.event.comment.body }}" =~ "3" ]] ; then + if [[ "${{ github.event.comment.body }}" =~ "catch" ]] ; then MATRIX_PROJECTS_JSON+='{ "name": "catch", "id": 2 },' fi - if [[ "${{ github.event.comment.body }}" =~ "4" ]] ; then + if [[ "${{ github.event.comment.body }}" =~ "mania" ]] ; then MATRIX_PROJECTS_JSON+='{ "name": "mania", "id": 3 },' fi From a694d482ed1c2fee5d0a153f4b2009910a95e6cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 12:32:26 +0900 Subject: [PATCH 2066/2442] Rename file --- .github/workflows/{test-diffcalc.yml => diffcalc.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{test-diffcalc.yml => diffcalc.yml} (100%) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/diffcalc.yml similarity index 100% rename from .github/workflows/test-diffcalc.yml rename to .github/workflows/diffcalc.yml From b9c91111d2a855e6935b443a5871c6b2d990885e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Sep 2021 12:43:29 +0900 Subject: [PATCH 2067/2442] Add some whitespace --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 529f8b9672..cb3338126c 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -39,14 +39,17 @@ namespace osu.Game.Rulesets.Osu.Difficulty double baseAimPerformance = Math.Pow(5 * Math.Max(1, aimRating / 0.0675) - 4, 3) / 100000; double baseSpeedPerformance = Math.Pow(5 * Math.Max(1, speedRating / 0.0675) - 4, 3) / 100000; double baseFlashlightPerformance = 0.0; + if (mods.Any(h => h is OsuModFlashlight)) baseFlashlightPerformance = Math.Pow(flashlightRating, 2.0) * 25.0; + double basePerformance = Math.Pow( Math.Pow(baseAimPerformance, 1.1) + Math.Pow(baseSpeedPerformance, 1.1) + Math.Pow(baseFlashlightPerformance, 1.1), 1.0 / 1.1 ); + double starRating = basePerformance > 0.00001 ? Math.Cbrt(1.12) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0; HitWindows hitWindows = new OsuHitWindows(); From ea624489ca809327e37a9952b0d90cf96c168ec1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 12:48:10 +0900 Subject: [PATCH 2068/2442] Remove continue-on-error --- .github/workflows/diffcalc.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/diffcalc.yml b/.github/workflows/diffcalc.yml index edb9fbd323..842522ae87 100644 --- a/.github/workflows/diffcalc.yml +++ b/.github/workflows/diffcalc.yml @@ -53,7 +53,6 @@ jobs: diffcalc: name: Run runs-on: self-hosted - continue-on-error: true if: needs.metadata.outputs.continue == 'yes' needs: metadata strategy: From 93e33fa94d666130e9628980acfcd98c6c5536e4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 14:08:54 +0900 Subject: [PATCH 2069/2442] Use `true` comparison rather than null coalesce fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game.Rulesets.Mania/Beatmaps/Patterns/Pattern.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Pattern.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Pattern.cs index 8c42e47dbc..828f2ec393 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Pattern.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Pattern.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns /// /// The column index. /// Whether the column with index contains a hit object. - public bool ColumnHasObject(int column) => containedColumns?.Contains(column) ?? false; + public bool ColumnHasObject(int column) => containedColumns?.Contains(column) == true; /// /// Amount of columns taken up by hit objects in this pattern. From aa0d32b3b109bdc62bc8f7596baef3bce774cfa8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 14:23:44 +0900 Subject: [PATCH 2070/2442] Retarget `master` repos in diffcalc CI runs --- .github/workflows/diffcalc.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/diffcalc.yml b/.github/workflows/diffcalc.yml index 842522ae87..3c57d971ae 100644 --- a/.github/workflows/diffcalc.yml +++ b/.github/workflows/diffcalc.yml @@ -78,8 +78,7 @@ jobs: - name: Checkout osu (master) uses: actions/checkout@v2 with: - repository: peppy/osu - ref: 'diffcalc-optimisations' + repository: ppy/osu path: 'master/osu' - name: Checkout osu (pr) uses: actions/checkout@v2 @@ -89,14 +88,12 @@ jobs: - name: Checkout osu-difficulty-calculator (master) uses: actions/checkout@v2 with: - repository: peppy/osu-difficulty-calculator - ref: 'bypass-attrib-row-insert' + repository: ppy/osu-difficulty-calculator path: 'master/osu-difficulty-calculator' - name: Checkout osu-difficulty-calculator (pr) uses: actions/checkout@v2 with: - repository: peppy/osu-difficulty-calculator - ref: 'bypass-attrib-row-insert' + repository: ppy/osu-difficulty-calculator path: 'pr/osu-difficulty-calculator' - name: Install .NET 5.0.x From bad3f0b1e9f0af33a45e4e3624605b51ab995f4d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Sep 2021 14:25:44 +0900 Subject: [PATCH 2071/2442] Disable FtB pass for particle spewer --- osu.Game/Graphics/ParticleSpewer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Graphics/ParticleSpewer.cs b/osu.Game/Graphics/ParticleSpewer.cs index 466bf04369..54a2b1e890 100644 --- a/osu.Game/Graphics/ParticleSpewer.cs +++ b/osu.Game/Graphics/ParticleSpewer.cs @@ -164,6 +164,8 @@ namespace osu.Game.Graphics return Vector2Extensions.Transform(new Vector2(x, y), DrawInfo.Matrix); } + + protected override bool CanDrawOpaqueInterior => false; } #endregion From 6246bbc262eb789099b515a3dd2d20cf54607e14 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Sep 2021 14:37:09 +0900 Subject: [PATCH 2072/2442] Also add to ParticleExplosion --- osu.Game/Graphics/ParticleExplosion.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Graphics/ParticleExplosion.cs b/osu.Game/Graphics/ParticleExplosion.cs index e0d2b50c55..094cc87bbe 100644 --- a/osu.Game/Graphics/ParticleExplosion.cs +++ b/osu.Game/Graphics/ParticleExplosion.cs @@ -115,6 +115,8 @@ namespace osu.Game.Graphics null, TextureCoords); } } + + protected override bool CanDrawOpaqueInterior => false; } private readonly struct ParticlePart From f0971cb90c43a50fe3c5b2c1185e9e3eb56335f8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Sep 2021 14:40:33 +0900 Subject: [PATCH 2073/2442] Fix kiai spawner using wrong current time --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index 2b0dfba1dd..9a7eb6835b 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy DrawableHitObject kiaiHitObject = null; // Check whether currently in a kiai section first. This is only done as an optimisation to avoid enumerating AliveObjects when not necessary. - if (gameplayBeatmap.ControlPointInfo.EffectPointAt(gameplayBeatmap.Time.Current).KiaiMode) + if (gameplayBeatmap.ControlPointInfo.EffectPointAt(Time.Current).KiaiMode) kiaiHitObject = playfield.HitObjectContainer.AliveObjects.FirstOrDefault(isTracking); kiaiSpewer.Active.Value = kiaiHitObject != null; From 69e28dc8a1bbd9edabd79dd1a69be7e1803f5124 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Sep 2021 14:44:22 +0900 Subject: [PATCH 2074/2442] Add failing test --- osu.Game.Rulesets.Osu.Tests/TestSceneCursorParticles.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneCursorParticles.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorParticles.cs index 11b1f5b2af..bd39dead34 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneCursorParticles.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorParticles.cs @@ -98,6 +98,7 @@ namespace osu.Game.Rulesets.Osu.Tests { var controlPointInfo = new ControlPointInfo(); controlPointInfo.Add(0, new EffectControlPoint { KiaiMode = true }); + controlPointInfo.Add(5000, new EffectControlPoint { KiaiMode = false }); return new Beatmap { From 6c91d39c15a0e6f4c563dac9e3b0a83537a187db Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Sep 2021 14:48:41 +0900 Subject: [PATCH 2075/2442] Remove GameplayClock dependency --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index 9a7eb6835b..c2db5f3f82 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -39,9 +39,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy [Resolved(canBeNull: true)] private GameplayBeatmap gameplayBeatmap { get; set; } - [Resolved(canBeNull: true)] - private GameplayClock gameplayClock { get; set; } - [BackgroundDependencyLoader] private void load(ISkinSource skin, OsuColour colours) { From 430ecc5409a903a1f871c3097a24c92beb3ff6f0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Sep 2021 15:20:04 +0900 Subject: [PATCH 2076/2442] Adjust to make HD slightly harder and not obsolete --- osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs index b3a54521d8..7f565cb82d 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs @@ -22,13 +22,13 @@ namespace osu.Game.Rulesets.Taiko.Mods /// How far away from the hit target should hitobjects start to fade out. /// Range: [0, 1] /// - private const float fade_out_start_time = 0.6f; + private const float fade_out_start_time = 1f; /// /// How long hitobjects take to fade out, in terms of the scrolling length. /// Range: [0, 1] /// - private const float fade_out_duration = 0.3f; + private const float fade_out_duration = 0.375f; private DrawableTaikoRuleset drawableRuleset; From 78e7755df1abe54f47d7b88215fa7d1a2418d20a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 17:54:09 +0900 Subject: [PATCH 2077/2442] Re-enable IPC import test --- osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index b2bd60d342..cba7f34ede 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -582,7 +582,6 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] [NonParallelizable] - [Ignore("Binding IPC on Appveyor isn't working (port in use). Need to figure out why")] public void TestImportOverIPC() { using (HeadlessGameHost host = new CleanRunHeadlessGameHost($"{nameof(ImportBeatmapTest)}-host", true)) From 3b174a71a3e73976ca591719ff37a5f3e1aac93c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 18:00:54 +0900 Subject: [PATCH 2078/2442] Move startup concurrency check to higher level Makes more sense to perform this check outside of the game itself now that we can. --- osu.Desktop/Program.cs | 5 ++++- osu.Game/OsuGame.cs | 6 ------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index dc712f2593..7434dec010 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -74,7 +74,10 @@ namespace osu.Desktop // we want to allow multiple instances to be started when in debug. if (!DebugUtils.IsDebugBuild) - return 0; + { + Logger.Log(@"osu! does not support multiple running instances.", LoggingTarget.Runtime, LogLevel.Error); + Environment.Exit(0); + } } if (tournamentClient) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 0e55a65aec..dde60158d1 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -210,12 +210,6 @@ namespace osu.Game [BackgroundDependencyLoader] private void load() { - if (!Host.IsPrimaryInstance && !DebugUtils.IsDebugBuild) - { - Logger.Log(@"osu! does not support multiple running instances.", LoggingTarget.Runtime, LogLevel.Error); - Environment.Exit(0); - } - if (args?.Length > 0) { var paths = args.Where(a => !a.StartsWith('-')).ToArray(); From 5e53058fbc39415b3b72c61e028b3e1805094f1d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 18:03:43 +0900 Subject: [PATCH 2079/2442] Use same method of exiting execution as previously for safety --- osu.Desktop/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 7434dec010..8ccd23b418 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -76,7 +76,7 @@ namespace osu.Desktop if (!DebugUtils.IsDebugBuild) { Logger.Log(@"osu! does not support multiple running instances.", LoggingTarget.Runtime, LogLevel.Error); - Environment.Exit(0); + return 0; } } From 5c7fe5dde0fdc861f48d1741c01d8312e52ce3b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 14:23:44 +0900 Subject: [PATCH 2080/2442] Retarget `master` repos in diffcalc CI runs --- .github/workflows/diffcalc.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/diffcalc.yml b/.github/workflows/diffcalc.yml index 842522ae87..3c57d971ae 100644 --- a/.github/workflows/diffcalc.yml +++ b/.github/workflows/diffcalc.yml @@ -78,8 +78,7 @@ jobs: - name: Checkout osu (master) uses: actions/checkout@v2 with: - repository: peppy/osu - ref: 'diffcalc-optimisations' + repository: ppy/osu path: 'master/osu' - name: Checkout osu (pr) uses: actions/checkout@v2 @@ -89,14 +88,12 @@ jobs: - name: Checkout osu-difficulty-calculator (master) uses: actions/checkout@v2 with: - repository: peppy/osu-difficulty-calculator - ref: 'bypass-attrib-row-insert' + repository: ppy/osu-difficulty-calculator path: 'master/osu-difficulty-calculator' - name: Checkout osu-difficulty-calculator (pr) uses: actions/checkout@v2 with: - repository: peppy/osu-difficulty-calculator - ref: 'bypass-attrib-row-insert' + repository: ppy/osu-difficulty-calculator path: 'pr/osu-difficulty-calculator' - name: Install .NET 5.0.x From 59d6a718d66eb39d81a3ad067354d1cbabb1c4b9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 20:09:34 +0900 Subject: [PATCH 2081/2442] Fix value not being loaded from beatmap in case of most dense grid setting --- osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs b/osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs index b93585af09..b8ff92bd37 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Edit private void load() { var gridSizeIndex = Array.IndexOf(grid_sizes, editorBeatmap.BeatmapInfo.GridSize); - if (gridSizeIndex > 0) + if (gridSizeIndex >= 0) currentGridSizeIndex = gridSizeIndex; updateSpacing(); } From 4cdce69f7e7c7b365c386f580def615723749658 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 23:45:03 +0900 Subject: [PATCH 2082/2442] Update test to match test beamap data --- osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs index a24cb6526d..47b2d3a098 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs @@ -57,12 +57,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { AddStep("enable rectangular grid", () => InputManager.Key(Key.Y)); AddUntilStep("rectangular grid visible", () => this.ChildrenOfType().Any()); - gridSizeIs(32); + gridSizeIs(4); - nextGridSizeIs(4); nextGridSizeIs(8); nextGridSizeIs(16); nextGridSizeIs(32); + nextGridSizeIs(4); } private void nextGridSizeIs(int size) From 77660d904820d2e632054a19ba1f1ec2ce36d5e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Sep 2021 01:13:47 +0900 Subject: [PATCH 2083/2442] Update diffcalc action to checkout the correct upstream branch --- .github/workflows/diffcalc.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/diffcalc.yml b/.github/workflows/diffcalc.yml index 3c57d971ae..9e6d1c9f71 100644 --- a/.github/workflows/diffcalc.yml +++ b/.github/workflows/diffcalc.yml @@ -74,16 +74,23 @@ jobs: mkdir -p $GITHUB_WORKSPACE/master/ mkdir -p $GITHUB_WORKSPACE/pr/ + - name: Get upstream branch # https://akaimo.hatenablog.jp/entry/2020/05/16/101251 + id: upstreambranch + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + echo "::set-output name=branchname::$(curl -H "Authorization: token ${GITHUB_TOKEN}" ${{ github.event.issue.pull_request.url }} | jq '.head.ref' | sed 's/\"//g')" + # Checkout osu - name: Checkout osu (master) uses: actions/checkout@v2 with: - repository: ppy/osu path: 'master/osu' - name: Checkout osu (pr) uses: actions/checkout@v2 with: path: 'pr/osu' + ref: ${{ steps.upstreambranch.outputs.branchname }} - name: Checkout osu-difficulty-calculator (master) uses: actions/checkout@v2 From d22fcc14fc3cfb095a17a392cee666bb7a8ba5ad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Sep 2021 01:28:17 +0900 Subject: [PATCH 2084/2442] Also grab correct repository path --- .github/workflows/diffcalc.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/diffcalc.yml b/.github/workflows/diffcalc.yml index 9e6d1c9f71..35fd45320c 100644 --- a/.github/workflows/diffcalc.yml +++ b/.github/workflows/diffcalc.yml @@ -80,6 +80,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | echo "::set-output name=branchname::$(curl -H "Authorization: token ${GITHUB_TOKEN}" ${{ github.event.issue.pull_request.url }} | jq '.head.ref' | sed 's/\"//g')" + echo "::set-output name=repo::$(curl -H "Authorization: token ${GITHUB_TOKEN}" ${{ github.event.issue.pull_request.url }} | jq '.head.repo.full_name' | sed 's/\"//g')" # Checkout osu - name: Checkout osu (master) @@ -90,6 +91,7 @@ jobs: uses: actions/checkout@v2 with: path: 'pr/osu' + repository: $${{ steps.upstreambranch.outputs.repo }} ref: ${{ steps.upstreambranch.outputs.branchname }} - name: Checkout osu-difficulty-calculator (master) From 29d69b2b83742246fddee1a208128bb53d5de913 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Sep 2021 01:29:56 +0900 Subject: [PATCH 2085/2442] Remove extra $ --- .github/workflows/diffcalc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/diffcalc.yml b/.github/workflows/diffcalc.yml index 35fd45320c..bc2626d3d6 100644 --- a/.github/workflows/diffcalc.yml +++ b/.github/workflows/diffcalc.yml @@ -91,7 +91,7 @@ jobs: uses: actions/checkout@v2 with: path: 'pr/osu' - repository: $${{ steps.upstreambranch.outputs.repo }} + repository: ${{ steps.upstreambranch.outputs.repo }} ref: ${{ steps.upstreambranch.outputs.branchname }} - name: Checkout osu-difficulty-calculator (master) From 2bac15ca1a5631a725179986a7e5c73a62efdeca Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Wed, 22 Sep 2021 14:17:27 +0100 Subject: [PATCH 2086/2442] base formula implementation --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index bf4d92652c..b5f5821823 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -49,6 +49,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(m => m is OsuModSpunOut)) multiplier *= 1.0 - Math.Pow((double)Attributes.SpinnerCount / totalHits, 0.85); + if (mods.Any(m => m is OsuModBlinds)) + multiplier *= 1.12 + (totalHits * (0.0008 / (1 + 3 * countMiss))); + + double aimValue = computeAimValue(); double speedValue = computeSpeedValue(); double accuracyValue = computeAccuracyValue(); From 85fd4bdbf888660f47f2430c3d6e2da8b20fa33a Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Wed, 22 Sep 2021 16:19:41 +0100 Subject: [PATCH 2087/2442] add accuracy nerf --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index b5f5821823..51e742e71c 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty multiplier *= 1.0 - Math.Pow((double)Attributes.SpinnerCount / totalHits, 0.85); if (mods.Any(m => m is OsuModBlinds)) - multiplier *= 1.12 + (totalHits * (0.0008 / (1 + 3 * countMiss))); + multiplier *= 1.0 + ((0.12 + totalHits * (0.0008 / (1 + 3 * countMiss))) * Math.Pow(accuracy, 16)); double aimValue = computeAimValue(); From e52621c60f2e78b884385364a09cc33c3d7ef627 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Wed, 22 Sep 2021 16:38:50 +0100 Subject: [PATCH 2088/2442] basically disable HD pp when blinds enabled --- .../Difficulty/OsuPerformanceCalculator.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 51e742e71c..f38208aa94 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -48,11 +48,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(m => m is OsuModSpunOut)) multiplier *= 1.0 - Math.Pow((double)Attributes.SpinnerCount / totalHits, 0.85); - - if (mods.Any(m => m is OsuModBlinds)) - multiplier *= 1.0 + ((0.12 + totalHits * (0.0008 / (1 + 3 * countMiss))) * Math.Pow(accuracy, 16)); - - + double aimValue = computeAimValue(); double speedValue = computeSpeedValue(); double accuracyValue = computeAccuracyValue(); @@ -113,7 +109,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty double approachRateBonus = 1.0 + (0.03 + 0.37 * approachRateTotalHitsFactor) * approachRateFactor; // We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR. - if (mods.Any(h => h is OsuModHidden)) + if (mods.Any(m => m is OsuModBlinds)) + aimValue *= 1.0 + ((0.12 + totalHits * (0.0008 / (1 + 3 * countMiss))) * Math.Pow(accuracy, 16)); + else if (mods.Any(h => h is OsuModHidden)) aimValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); aimValue *= approachRateBonus; @@ -151,7 +149,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty speedValue *= 1.0 + (0.03 + 0.37 * approachRateTotalHitsFactor) * approachRateFactor; - if (mods.Any(m => m is OsuModHidden)) + if (mods.Any(m => m is OsuModBlinds)) + speedValue *= 1.0 + ((0.12 + totalHits * (0.0008 / (1 + 3 * countMiss))) * Math.Pow(accuracy, 16)); + else if (mods.Any(m => m is OsuModHidden)) speedValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); // Scale the speed value with accuracy and OD. From 857ce721da7803cc12dad4e2d92d239c0572ec4f Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Wed, 22 Sep 2021 16:48:11 +0100 Subject: [PATCH 2089/2442] share BlindsMultiplier everywhere, make it completely negate HD pp --- .../Difficulty/OsuPerformanceCalculator.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index f38208aa94..a15a6d5ea1 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty // We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR. if (mods.Any(m => m is OsuModBlinds)) - aimValue *= 1.0 + ((0.12 + totalHits * (0.0008 / (1 + 3 * countMiss))) * Math.Pow(accuracy, 16)); + aimValue *= blindsMultiplier; else if (mods.Any(h => h is OsuModHidden)) aimValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); @@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty speedValue *= 1.0 + (0.03 + 0.37 * approachRateTotalHitsFactor) * approachRateFactor; if (mods.Any(m => m is OsuModBlinds)) - speedValue *= 1.0 + ((0.12 + totalHits * (0.0008 / (1 + 3 * countMiss))) * Math.Pow(accuracy, 16)); + speedValue *= blindsMultiplier; else if (mods.Any(m => m is OsuModHidden)) speedValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); @@ -184,7 +184,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty // Bonus for many hitcircles - it's harder to keep good accuracy up for longer. accuracyValue *= Math.Min(1.15, Math.Pow(amountHitObjectsWithAccuracy / 1000.0, 0.3)); - if (mods.Any(m => m is OsuModHidden)) + if (mods.Any(m => m is OsuModBlinds)) + accuracyValue *= blindsMultiplier; + else if (mods.Any(m => m is OsuModHidden)) accuracyValue *= 1.08; if (mods.Any(m => m is OsuModFlashlight)) accuracyValue *= 1.02; @@ -230,5 +232,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty private int totalHits => countGreat + countOk + countMeh + countMiss; private int totalSuccessfulHits => countGreat + countOk + countMeh; + private double blindsMultiplier => 1.0 + ((0.12 + totalHits * (0.0008 / (1 + 3 * countMiss))) * Math.Pow(accuracy, 16)); } } From c62e429cea7342652450561966a99611f0fb865b Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Wed, 22 Sep 2021 19:02:25 +0100 Subject: [PATCH 2090/2442] buff miss factor --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index a15a6d5ea1..8f2f286d8b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -232,6 +232,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty private int totalHits => countGreat + countOk + countMeh + countMiss; private int totalSuccessfulHits => countGreat + countOk + countMeh; - private double blindsMultiplier => 1.0 + ((0.12 + totalHits * (0.0008 / (1 + 3 * countMiss))) * Math.Pow(accuracy, 16)); + private double blindsMultiplier => 1.0 + ((0.12 + totalHits * (0.0008 / (1 + 2 * countMiss))) * Math.Pow(accuracy, 16)); } } From dcadf3b81dd7c012ffac4749778eb1a3cb081160 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Sep 2021 19:18:51 +0900 Subject: [PATCH 2091/2442] Add failing test coverage of some dialogs being held on to --- .../UserInterface/TestSceneDialogOverlay.cs | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs index f5cba2c900..405461eec8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs @@ -24,9 +24,10 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestBasic() { - TestPopupDialog dialog = null; + TestPopupDialog firstDialog = null; + TestPopupDialog secondDialog = null; - AddStep("dialog #1", () => overlay.Push(dialog = new TestPopupDialog + AddStep("dialog #1", () => overlay.Push(firstDialog = new TestPopupDialog { Icon = FontAwesome.Regular.TrashAlt, HeaderText = @"Confirm deletion of", @@ -46,9 +47,9 @@ namespace osu.Game.Tests.Visual.UserInterface }, })); - AddAssert("first dialog displayed", () => overlay.CurrentDialog == dialog); + AddAssert("first dialog displayed", () => overlay.CurrentDialog == firstDialog); - AddStep("dialog #2", () => overlay.Push(dialog = new TestPopupDialog + AddStep("dialog #2", () => overlay.Push(secondDialog = new TestPopupDialog { Icon = FontAwesome.Solid.Cog, HeaderText = @"What do you want to do with", @@ -82,30 +83,33 @@ namespace osu.Game.Tests.Visual.UserInterface }, })); - AddAssert("second dialog displayed", () => overlay.CurrentDialog == dialog); + AddAssert("second dialog displayed", () => overlay.CurrentDialog == secondDialog); + AddAssert("first dialog is not part of hierarchy", () => firstDialog.Parent == null); } [Test] public void TestDismissBeforePush() { + TestPopupDialog testDialog = null; AddStep("dismissed dialog push", () => { - overlay.Push(new TestPopupDialog + overlay.Push(testDialog = new TestPopupDialog { State = { Value = Visibility.Hidden } }); }); AddAssert("no dialog pushed", () => overlay.CurrentDialog == null); + AddAssert("dialog is not part of hierarchy", () => testDialog.Parent == null); } [Test] public void TestDismissBeforePushViaButtonPress() { + TestPopupDialog testDialog = null; AddStep("dismissed dialog push", () => { - TestPopupDialog dialog; - overlay.Push(dialog = new TestPopupDialog + overlay.Push(testDialog = new TestPopupDialog { Buttons = new PopupDialogButton[] { @@ -113,10 +117,11 @@ namespace osu.Game.Tests.Visual.UserInterface }, }); - dialog.PerformOkAction(); + testDialog.PerformOkAction(); }); AddAssert("no dialog pushed", () => overlay.CurrentDialog == null); + AddAssert("dialog is not part of hierarchy", () => testDialog.Parent == null); } private class TestPopupDialog : PopupDialog From 6b698047ab7dc0f937bcb61669ed326e990be51d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Sep 2021 19:19:03 +0900 Subject: [PATCH 2092/2442] Fix `DialogOverlay` potentially not expiring dialogs as soon as it should --- osu.Game/Overlays/DialogOverlay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index d5d31343f2..f051e09c08 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -49,6 +49,8 @@ namespace osu.Game.Overlays Show(); } + public override bool IsPresent => dialogContainer.Children.Count > 0; + protected override bool BlockNonPositionalInput => true; private void onDialogOnStateChanged(VisibilityContainer dialog, Visibility v) From ad6c4e38784cfaee12b7b4f232b8d5e673eca293 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Sep 2021 12:46:46 +0900 Subject: [PATCH 2093/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 4859510e6c..967405cd2e 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index e6afbe383a..61ae6aa69e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 51ca381b63..e032926a9c 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 70119d3a43d29de697fe2eba5b8d1515d66e5fe4 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Fri, 24 Sep 2021 15:02:19 +0100 Subject: [PATCH 2094/2442] add drain rate term --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs | 1 + osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 2 ++ osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index ac77a93239..bd4c0f2ad5 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -12,6 +12,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty public double FlashlightRating { get; set; } public double ApproachRate { get; set; } public double OverallDifficulty { get; set; } + public double DrainRate { get; set; } public int HitCircleCount { get; set; } public int SpinnerCount { get; set; } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 4c8d0b2ce6..ab5adbcc3e 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -54,6 +54,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty double starRating = basePerformance > 0.00001 ? Math.Cbrt(1.12) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0; double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; + double drainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate; int maxCombo = beatmap.HitObjects.Count; // Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above) @@ -71,6 +72,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty FlashlightRating = flashlightRating, ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5, OverallDifficulty = (80 - hitWindowGreat) / 6, + DrainRate = drainRate, MaxCombo = maxCombo, HitCircleCount = hitCirclesCount, SpinnerCount = spinnerCount, diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 8f2f286d8b..4d0a4bcde8 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -232,6 +232,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty private int totalHits => countGreat + countOk + countMeh + countMiss; private int totalSuccessfulHits => countGreat + countOk + countMeh; - private double blindsMultiplier => 1.0 + ((0.12 + totalHits * (0.0008 / (1 + 2 * countMiss))) * Math.Pow(accuracy, 16)); + private double blindsMultiplier => 1.0 + ((0.12 + totalHits * (0.0008 / (1 + 2 * countMiss))) * Math.Pow(accuracy, 16)) * (1 - 0.003 * Attributes.DrainRate * Attributes.DrainRate); } } From 2508171d41e5a27f6fff9c6116d9c585a2e75391 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Sat, 25 Sep 2021 03:52:05 +0000 Subject: [PATCH 2095/2442] nerfed rhythm further to attempt to balance --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index a5af3eb534..01d50c1f0d 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private const double pi_over_4 = Math.PI / 4; private const double pi_over_2 = Math.PI / 2; - private const double rhythm_multiplier = 0.75; + private const double rhythm_multiplier = 0.5; private const int history_time_max = 5000; // 5 seconds of calculatingRhythmBonus max. private double skillMultiplier => 1375; From 9b9c30c8a13bfa6b724f691d5c2b11dd76078a68 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Fri, 24 Sep 2021 21:57:52 -0700 Subject: [PATCH 2096/2442] Fix text overflowing on dropdown headers --- .../Graphics/UserInterface/OsuDropdown.cs | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index 42f628a75a..fe88e6f78a 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -274,21 +274,40 @@ namespace osu.Game.Graphics.UserInterface CornerRadius = corner_radius; Height = 40; - Foreground.Children = new Drawable[] + Foreground.Child = new GridContainer { - Text = new OsuSpriteText + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + RowDimensions = new[] { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, + new Dimension(GridSizeMode.AutoSize), }, - Icon = new SpriteIcon + ColumnDimensions = new[] { - Icon = FontAwesome.Solid.ChevronDown, - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Margin = new MarginPadding { Right = 5 }, - Size = new Vector2(12), + new Dimension(), + new Dimension(GridSizeMode.AutoSize), }, + Content = new[] + { + new Drawable[] + { + Text = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.X, + Truncate = true, + }, + Icon = new SpriteIcon + { + Icon = FontAwesome.Solid.ChevronDown, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Margin = new MarginPadding { Horizontal = 5 }, + Size = new Vector2(12), + }, + } + } }; AddInternal(new HoverClickSounds()); From b6f494cbb76cde8234a6e0ede820f3f7434b063a Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Sat, 25 Sep 2021 17:34:24 +0100 Subject: [PATCH 2097/2442] accuracy and speed changes pt 1 --- .../Difficulty/OsuPerformanceCalculator.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 4d0a4bcde8..621fb4588e 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty // We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR. if (mods.Any(m => m is OsuModBlinds)) - aimValue *= blindsMultiplier; + aimValue *= 1.0 + blindsMultiplier; else if (mods.Any(h => h is OsuModHidden)) aimValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); @@ -149,8 +149,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty speedValue *= 1.0 + (0.03 + 0.37 * approachRateTotalHitsFactor) * approachRateFactor; + // Increasing the speed value by object count for Blinds isn't ideal, so the minimum buff is given. if (mods.Any(m => m is OsuModBlinds)) - speedValue *= blindsMultiplier; + speedValue *= 1.12; else if (mods.Any(m => m is OsuModHidden)) speedValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); @@ -184,8 +185,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty // Bonus for many hitcircles - it's harder to keep good accuracy up for longer. accuracyValue *= Math.Min(1.15, Math.Pow(amountHitObjectsWithAccuracy / 1000.0, 0.3)); + // Increasing the accuracy value by object count for Blinds isn't ideal, so the minimum buff is given. if (mods.Any(m => m is OsuModBlinds)) - accuracyValue *= blindsMultiplier; + accuracyValue *= 1.14; else if (mods.Any(m => m is OsuModHidden)) accuracyValue *= 1.08; if (mods.Any(m => m is OsuModFlashlight)) @@ -232,6 +234,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty private int totalHits => countGreat + countOk + countMeh + countMiss; private int totalSuccessfulHits => countGreat + countOk + countMeh; - private double blindsMultiplier => 1.0 + ((0.12 + totalHits * (0.0008 / (1 + 2 * countMiss))) * Math.Pow(accuracy, 16)) * (1 - 0.003 * Attributes.DrainRate * Attributes.DrainRate); + private double blindsMultiplier => (0.12 + totalHits * (0.0008 / (1 + 2 * countMiss)) * Math.Pow(accuracy, 16)) * (1 - 0.003 * Attributes.DrainRate * Attributes.DrainRate); } } From 4aadff3fd795badade0f0c7816046d1180ff7de0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 26 Sep 2021 15:08:43 +0200 Subject: [PATCH 2098/2442] Add failing test for incorrect composer selection --- .../Editing/TestSceneBlueprintSelection.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneBlueprintSelection.cs b/osu.Game.Tests/Visual/Editing/TestSceneBlueprintSelection.cs index 976bf93c15..0dff6d9dd9 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneBlueprintSelection.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneBlueprintSelection.cs @@ -7,9 +7,13 @@ using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles; using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.UI; using osu.Game.Screens.Edit.Compose.Components; using osu.Game.Tests.Beatmaps; using osuTK; @@ -66,5 +70,36 @@ namespace osu.Game.Tests.Visual.Editing AddAssert("selection is unchanged", () => EditorBeatmap.SelectedHitObjects.Single() == firstSlider); } + + [Test] + public void TestOverlappingObjectsWithSameStartTime() + { + AddStep("add overlapping circles", () => + { + EditorBeatmap.Add(createHitCircle(50, OsuPlayfield.BASE_SIZE / 2)); + EditorBeatmap.Add(createHitCircle(50, OsuPlayfield.BASE_SIZE / 2 + new Vector2(-10, -20))); + EditorBeatmap.Add(createHitCircle(50, OsuPlayfield.BASE_SIZE / 2 + new Vector2(10, -20))); + }); + + AddStep("click at centre of playfield", () => + { + var hitObjectContainer = Editor.ChildrenOfType().Single(); + var centre = hitObjectContainer.ToScreenSpace(OsuPlayfield.BASE_SIZE / 2); + InputManager.MoveMouseTo(centre); + InputManager.Click(MouseButton.Left); + }); + + AddAssert("frontmost object selected", () => + { + var hasCombo = Editor.ChildrenOfType().Single(b => b.IsSelected).Item as IHasComboInformation; + return hasCombo?.IndexInCurrentCombo == 0; + }); + } + + private HitCircle createHitCircle(double startTime, Vector2 position) => new HitCircle + { + StartTime = startTime, + Position = position, + }; } } From 0057400bb761750d6d52815388e0b809a90d7e54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 26 Sep 2021 15:09:30 +0200 Subject: [PATCH 2099/2442] Rename test scene to reflect its new purpose --- ...SceneBlueprintSelection.cs => TestSceneBlueprintOrdering.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename osu.Game.Tests/Visual/Editing/{TestSceneBlueprintSelection.cs => TestSceneBlueprintOrdering.cs} (98%) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneBlueprintSelection.cs b/osu.Game.Tests/Visual/Editing/TestSceneBlueprintOrdering.cs similarity index 98% rename from osu.Game.Tests/Visual/Editing/TestSceneBlueprintSelection.cs rename to osu.Game.Tests/Visual/Editing/TestSceneBlueprintOrdering.cs index 0dff6d9dd9..955069f509 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneBlueprintSelection.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneBlueprintOrdering.cs @@ -21,7 +21,7 @@ using osuTK.Input; namespace osu.Game.Tests.Visual.Editing { - public class TestSceneBlueprintSelection : EditorTestScene + public class TestSceneBlueprintOrdering : EditorTestScene { protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); From ce70d1082d68d938250509856c1bd6b0f9a4f1ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 26 Sep 2021 15:29:00 +0200 Subject: [PATCH 2100/2442] Add failing test for "ghost timeline blueprint" --- .../Editing/TestSceneBlueprintOrdering.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneBlueprintOrdering.cs b/osu.Game.Tests/Visual/Editing/TestSceneBlueprintOrdering.cs index 955069f509..5d8a6dabd7 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneBlueprintOrdering.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneBlueprintOrdering.cs @@ -15,6 +15,7 @@ using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.UI; using osu.Game.Screens.Edit.Compose.Components; +using osu.Game.Screens.Edit.Compose.Components.Timeline; using osu.Game.Tests.Beatmaps; using osuTK; using osuTK.Input; @@ -96,6 +97,32 @@ namespace osu.Game.Tests.Visual.Editing }); } + [Test] + public void TestPlacementOfConcurrentObjectWithDuration() + { + AddStep("seek to timing point", () => EditorClock.Seek(2170)); + AddStep("add hit circle", () => EditorBeatmap.Add(createHitCircle(2170, Vector2.Zero))); + + AddStep("choose spinner placement tool", () => + { + InputManager.Key(Key.Number4); + var hitObjectContainer = Editor.ChildrenOfType().Single(); + InputManager.MoveMouseTo(hitObjectContainer.ScreenSpaceDrawQuad.Centre); + }); + + AddStep("begin placing spinner", () => + { + InputManager.Click(MouseButton.Left); + }); + AddStep("end placing spinner", () => + { + EditorClock.Seek(2500); + InputManager.Click(MouseButton.Right); + }); + + AddAssert("two timeline blueprints present", () => Editor.ChildrenOfType().Count() == 2); + } + private HitCircle createHitCircle(double startTime, Vector2 position) => new HitCircle { StartTime = startTime, From a86b9893acd65219aef8dd0c737125f35f32a5a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 26 Sep 2021 15:48:56 +0200 Subject: [PATCH 2101/2442] Always re-sort blueprints before adding/removing one --- .../HitObjectOrderedSelectionContainer.cs | 66 +++++++------------ 1 file changed, 24 insertions(+), 42 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/HitObjectOrderedSelectionContainer.cs b/osu.Game/Screens/Edit/Compose/Components/HitObjectOrderedSelectionContainer.cs index 4078661a26..eebaa25af1 100644 --- a/osu.Game/Screens/Edit/Compose/Components/HitObjectOrderedSelectionContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/HitObjectOrderedSelectionContainer.cs @@ -1,8 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; -using osu.Framework.Bindables; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Edit; @@ -15,53 +14,28 @@ namespace osu.Game.Screens.Edit.Compose.Components /// public sealed class HitObjectOrderedSelectionContainer : Container> { + [Resolved] + private EditorBeatmap editorBeatmap { get; set; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + editorBeatmap.HitObjectUpdated += hitObjectUpdated; + } + + private void hitObjectUpdated(HitObject _) => SortInternal(); + public override void Add(SelectionBlueprint drawable) { + SortInternal(); base.Add(drawable); - bindStartTime(drawable); } public override bool Remove(SelectionBlueprint drawable) { - if (!base.Remove(drawable)) - return false; - - unbindStartTime(drawable); - return true; - } - - public override void Clear(bool disposeChildren) - { - base.Clear(disposeChildren); - unbindAllStartTimes(); - } - - private readonly Dictionary, IBindable> startTimeMap = new Dictionary, IBindable>(); - - private void bindStartTime(SelectionBlueprint blueprint) - { - var bindable = blueprint.Item.StartTimeBindable.GetBoundCopy(); - - bindable.BindValueChanged(_ => - { - if (LoadState >= LoadState.Ready) - SortInternal(); - }); - - startTimeMap[blueprint] = bindable; - } - - private void unbindStartTime(SelectionBlueprint blueprint) - { - startTimeMap[blueprint].UnbindAll(); - startTimeMap.Remove(blueprint); - } - - private void unbindAllStartTimes() - { - foreach (var kvp in startTimeMap) - kvp.Value.UnbindAll(); - startTimeMap.Clear(); + SortInternal(); + return base.Remove(drawable); } protected override int Compare(Drawable x, Drawable y) @@ -79,5 +53,13 @@ namespace osu.Game.Screens.Edit.Compose.Components return i == 0 ? CompareReverseChildID(y, x) : i; } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (editorBeatmap != null) + editorBeatmap.HitObjectUpdated -= hitObjectUpdated; + } } } From 6dc3e66c930518c72889319a5c618a23148c51a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 26 Sep 2021 15:52:25 +0200 Subject: [PATCH 2102/2442] Include combo information when determining ordering if available --- .../HitObjectOrderedSelectionContainer.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/HitObjectOrderedSelectionContainer.cs b/osu.Game/Screens/Edit/Compose/Components/HitObjectOrderedSelectionContainer.cs index eebaa25af1..e8286c5128 100644 --- a/osu.Game/Screens/Edit/Compose/Components/HitObjectOrderedSelectionContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/HitObjectOrderedSelectionContainer.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Screens.Edit.Compose.Components { @@ -45,13 +46,23 @@ namespace osu.Game.Screens.Edit.Compose.Components // Put earlier blueprints towards the end of the list, so they handle input first int i = yObj.Item.StartTime.CompareTo(xObj.Item.StartTime); - if (i != 0) return i; // Fall back to end time if the start time is equal. i = yObj.Item.GetEndTime().CompareTo(xObj.Item.GetEndTime()); + if (i != 0) return i; - return i == 0 ? CompareReverseChildID(y, x) : i; + // As a final fallback, use combo information if available. + if (xObj.Item is IHasComboInformation xHasCombo && yObj.Item is IHasComboInformation yHasCombo) + { + i = yHasCombo.ComboIndex.CompareTo(xHasCombo.ComboIndex); + if (i != 0) return i; + + i = yHasCombo.IndexInCurrentCombo.CompareTo(xHasCombo.IndexInCurrentCombo); + if (i != 0) return i; + } + + return CompareReverseChildID(y, x); } protected override void Dispose(bool isDisposing) From 7caa0a81ec5525f5bbcd3599737e7b14aa2057da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 26 Sep 2021 17:22:58 +0200 Subject: [PATCH 2103/2442] Rename `TestScene{Editor -> Composer}Selection` In line with an upcoming split in functionality between the composer blueprint container and the timeline blueprint container. --- ...ceneEditorSelection.cs => TestSceneComposerSelection.cs} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename osu.Game.Tests/Visual/Editing/{TestSceneEditorSelection.cs => TestSceneComposerSelection.cs} (98%) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs b/osu.Game.Tests/Visual/Editing/TestSceneComposerSelection.cs similarity index 98% rename from osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs rename to osu.Game.Tests/Visual/Editing/TestSceneComposerSelection.cs index 4c4a87972f..a2a7b72283 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneComposerSelection.cs @@ -20,14 +20,14 @@ using osuTK.Input; namespace osu.Game.Tests.Visual.Editing { - public class TestSceneEditorSelection : EditorTestScene + public class TestSceneComposerSelection : EditorTestScene { protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false); - private EditorBlueprintContainer blueprintContainer - => Editor.ChildrenOfType().First(); + private ComposeBlueprintContainer blueprintContainer + => Editor.ChildrenOfType().First(); private void moveMouseToObject(Func targetFunc) { From 0de7db5840573d818f89a5607f43e0c68404f234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 26 Sep 2021 17:41:20 +0200 Subject: [PATCH 2104/2442] Add test coverage for timeline selection logic --- .../Editing/TestSceneTimelineSelection.cs | 163 ++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 osu.Game.Tests/Visual/Editing/TestSceneTimelineSelection.cs diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimelineSelection.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimelineSelection.cs new file mode 100644 index 0000000000..0a1ab522a1 --- /dev/null +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimelineSelection.cs @@ -0,0 +1,163 @@ +// 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.Linq; +using NUnit.Framework; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Screens.Edit.Compose.Components.Timeline; +using osu.Game.Tests.Beatmaps; +using osuTK; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Editing +{ + public class TestSceneTimelineSelection : EditorTestScene + { + protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); + + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false); + + private TimelineBlueprintContainer blueprintContainer + => Editor.ChildrenOfType().First(); + + private void moveMouseToObject(Func targetFunc) + { + AddStep("move mouse to object", () => + { + var pos = blueprintContainer.SelectionBlueprints + .First(s => s.Item == targetFunc()) + .ChildrenOfType() + .First().ScreenSpaceDrawQuad.Centre; + + InputManager.MoveMouseTo(pos); + }); + } + + [Test] + public void TestNudgeSelection() + { + HitCircle[] addedObjects = null; + + AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[] + { + new HitCircle { StartTime = 100 }, + new HitCircle { StartTime = 200, Position = new Vector2(100) }, + new HitCircle { StartTime = 300, Position = new Vector2(200) }, + new HitCircle { StartTime = 400, Position = new Vector2(300) }, + })); + + AddStep("select objects", () => EditorBeatmap.SelectedHitObjects.AddRange(addedObjects)); + + AddStep("nudge forwards", () => InputManager.Key(Key.K)); + AddAssert("objects moved forwards in time", () => addedObjects[0].StartTime > 100); + + AddStep("nudge backwards", () => InputManager.Key(Key.J)); + AddAssert("objects reverted to original position", () => addedObjects[0].StartTime == 100); + } + + [Test] + public void TestBasicSelect() + { + var addedObject = new HitCircle { StartTime = 100 }; + AddStep("add hitobject", () => EditorBeatmap.Add(addedObject)); + + moveMouseToObject(() => addedObject); + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + + AddAssert("hitobject selected", () => EditorBeatmap.SelectedHitObjects.Single() == addedObject); + + var addedObject2 = new HitCircle + { + StartTime = 200, + Position = new Vector2(100), + }; + + AddStep("add one more hitobject", () => EditorBeatmap.Add(addedObject2)); + AddAssert("selection unchanged", () => EditorBeatmap.SelectedHitObjects.Single() == addedObject); + + moveMouseToObject(() => addedObject2); + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + AddAssert("hitobject selected", () => EditorBeatmap.SelectedHitObjects.Single() == addedObject2); + } + + [Test] + public void TestMultiSelect() + { + var addedObjects = new[] + { + new HitCircle { StartTime = 100 }, + new HitCircle { StartTime = 200, Position = new Vector2(100) }, + new HitCircle { StartTime = 300, Position = new Vector2(200) }, + new HitCircle { StartTime = 400, Position = new Vector2(300) }, + }; + + AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects)); + + moveMouseToObject(() => addedObjects[0]); + AddStep("click first", () => InputManager.Click(MouseButton.Left)); + + AddAssert("hitobject selected", () => EditorBeatmap.SelectedHitObjects.Single() == addedObjects[0]); + + AddStep("hold control", () => InputManager.PressKey(Key.ControlLeft)); + + moveMouseToObject(() => addedObjects[1]); + AddStep("click second", () => InputManager.Click(MouseButton.Left)); + AddAssert("2 hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 2 && EditorBeatmap.SelectedHitObjects.Contains(addedObjects[1])); + + moveMouseToObject(() => addedObjects[2]); + AddStep("click third", () => InputManager.Click(MouseButton.Left)); + AddAssert("3 hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 3 && EditorBeatmap.SelectedHitObjects.Contains(addedObjects[2])); + + moveMouseToObject(() => addedObjects[1]); + AddStep("click second", () => InputManager.Click(MouseButton.Left)); + AddAssert("2 hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 2 && !EditorBeatmap.SelectedHitObjects.Contains(addedObjects[1])); + + AddStep("release control", () => InputManager.ReleaseKey(Key.ControlLeft)); + } + + [Test] + public void TestBasicDeselect() + { + var addedObject = new HitCircle { StartTime = 100 }; + AddStep("add hitobject", () => EditorBeatmap.Add(addedObject)); + + moveMouseToObject(() => addedObject); + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + + AddAssert("hitobject selected", () => EditorBeatmap.SelectedHitObjects.Single() == addedObject); + + AddStep("click away", () => + { + InputManager.MoveMouseTo(Editor.ChildrenOfType().Single().ScreenSpaceDrawQuad.TopLeft + Vector2.One); + InputManager.Click(MouseButton.Left); + }); + + AddAssert("selection lost", () => EditorBeatmap.SelectedHitObjects.Count == 0); + } + + [Test] + public void TestQuickDelete() + { + var addedObject = new HitCircle + { + StartTime = 0, + }; + + AddStep("add hitobject", () => EditorBeatmap.Add(addedObject)); + + moveMouseToObject(() => addedObject); + + AddStep("hold shift", () => InputManager.PressKey(Key.ShiftLeft)); + AddStep("right click", () => InputManager.Click(MouseButton.Right)); + AddStep("release shift", () => InputManager.ReleaseKey(Key.ShiftLeft)); + + AddAssert("no hitobjects in beatmap", () => EditorBeatmap.HitObjects.Count == 0); + } + } +} From 81d160c85a0e75b5994abcbda27a9ea9fde28bf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 26 Sep 2021 17:45:47 +0200 Subject: [PATCH 2105/2442] Add test covering expected UX of range selection --- .../Editing/TestSceneTimelineSelection.cs | 68 +++++++++++++++++++ .../Timeline/TimelineSelectionHandler.cs | 33 +++++++++ 2 files changed, 101 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimelineSelection.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimelineSelection.cs index 0a1ab522a1..f4703b79f0 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTimelineSelection.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimelineSelection.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Testing; @@ -159,5 +160,72 @@ namespace osu.Game.Tests.Visual.Editing AddAssert("no hitobjects in beatmap", () => EditorBeatmap.HitObjects.Count == 0); } + + [Test] + public void TestRangeSelect() + { + var addedObjects = new[] + { + new HitCircle { StartTime = 100 }, + new HitCircle { StartTime = 200, Position = new Vector2(100) }, + new HitCircle { StartTime = 300, Position = new Vector2(200) }, + new HitCircle { StartTime = 400, Position = new Vector2(300) }, + new HitCircle { StartTime = 500, Position = new Vector2(400) }, + }; + + AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects)); + + moveMouseToObject(() => addedObjects[1]); + AddStep("click second", () => InputManager.Click(MouseButton.Left)); + + AddAssert("hitobject selected", () => EditorBeatmap.SelectedHitObjects.Single() == addedObjects[1]); + + AddStep("hold shift", () => InputManager.PressKey(Key.ShiftLeft)); + + moveMouseToObject(() => addedObjects[3]); + AddStep("click fourth", () => InputManager.Click(MouseButton.Left)); + assertSelectionIs(addedObjects.Skip(1).Take(3)); + + moveMouseToObject(() => addedObjects[0]); + AddStep("click first", () => InputManager.Click(MouseButton.Left)); + assertSelectionIs(addedObjects.Take(2)); + + AddStep("clear selection", () => EditorBeatmap.SelectedHitObjects.Clear()); + AddStep("release shift", () => InputManager.ReleaseKey(Key.ShiftLeft)); + + moveMouseToObject(() => addedObjects[0]); + AddStep("click first", () => InputManager.Click(MouseButton.Left)); + assertSelectionIs(addedObjects.Take(1)); + + AddStep("hold ctrl", () => InputManager.PressKey(Key.ControlLeft)); + moveMouseToObject(() => addedObjects[2]); + AddStep("click third", () => InputManager.Click(MouseButton.Left)); + assertSelectionIs(new[] { addedObjects[0], addedObjects[2] }); + + AddStep("hold shift", () => InputManager.PressKey(Key.ShiftLeft)); + moveMouseToObject(() => addedObjects[4]); + AddStep("click fifth", () => InputManager.Click(MouseButton.Left)); + assertSelectionIs(addedObjects.Except(new[] { addedObjects[1] })); + + moveMouseToObject(() => addedObjects[0]); + AddStep("click first", () => InputManager.Click(MouseButton.Left)); + assertSelectionIs(addedObjects); + + AddStep("clear selection", () => EditorBeatmap.SelectedHitObjects.Clear()); + moveMouseToObject(() => addedObjects[0]); + AddStep("click first", () => InputManager.Click(MouseButton.Left)); + assertSelectionIs(addedObjects.Take(1)); + + moveMouseToObject(() => addedObjects[1]); + AddStep("click first", () => InputManager.Click(MouseButton.Left)); + assertSelectionIs(addedObjects.Take(2)); + + moveMouseToObject(() => addedObjects[2]); + AddStep("click first", () => InputManager.Click(MouseButton.Left)); + assertSelectionIs(addedObjects.Take(3)); + } + + private void assertSelectionIs(IEnumerable hitObjects) + => AddAssert("correct hitobjects selected", () => EditorBeatmap.SelectedHitObjects.OrderBy(h => h.StartTime).SequenceEqual(hitObjects)); } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs index c43e554b85..ff385b0ab3 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs @@ -1,13 +1,16 @@ // 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.Linq; using osu.Framework.Allocation; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Input.Bindings; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osuTK; +using osuTK.Input; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { @@ -62,5 +65,35 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline EditorBeatmap.Update(h); }); } + + internal override bool MouseDownSelectionRequested(SelectionBlueprint blueprint, MouseButtonEvent e) + { + if (e.ShiftPressed && e.Button == MouseButton.Left && SelectedItems.Any()) + { + handleRangeSelection(blueprint); + return true; + } + + return base.MouseDownSelectionRequested(blueprint, e); + } + + private void handleRangeSelection(SelectionBlueprint blueprint) + { + var clickedObject = blueprint.Item; + double rangeStart = clickedObject.StartTime; + double rangeEnd = clickedObject.GetEndTime(); + + foreach (var selectedObject in SelectedItems) + { + rangeStart = Math.Min(rangeStart, selectedObject.StartTime); + rangeEnd = Math.Max(rangeEnd, selectedObject.GetEndTime()); + } + + EditorBeatmap.SelectedHitObjects.Clear(); + EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects.Where(obj => isInRange(obj, rangeStart, rangeEnd))); + + bool isInRange(HitObject hitObject, double start, double end) + => hitObject.StartTime >= start && hitObject.GetEndTime() <= end; + } } } From d3203f83dd538efdee901ac8eefca79694e3291b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 26 Sep 2021 19:07:38 +0200 Subject: [PATCH 2106/2442] Add implementation of range selection --- .../Compose/Components/SelectionHandler.cs | 2 +- .../Timeline/TimelineSelectionHandler.cs | 48 +++++++++++++++---- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 44eb062db8..ee35b6a47c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -191,7 +191,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// The blueprint. /// The mouse event responsible for selection. /// Whether a selection was performed. - internal bool MouseDownSelectionRequested(SelectionBlueprint blueprint, MouseButtonEvent e) + internal virtual bool MouseDownSelectionRequested(SelectionBlueprint blueprint, MouseButtonEvent e) { if (e.ShiftPressed && e.Button == MouseButton.Right) { diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs index ff385b0ab3..ad770b40b5 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs @@ -2,7 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; @@ -66,31 +69,58 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }); } + /// + /// The "pivot" object, used in range selection mode. + /// When in range selection, the range to select is determined by the pivot object + /// (last existing object interacted with prior to holding down Shift) + /// and by the object clicked last when Shift was pressed. + /// + [CanBeNull] + private HitObject pivot; + internal override bool MouseDownSelectionRequested(SelectionBlueprint blueprint, MouseButtonEvent e) { if (e.ShiftPressed && e.Button == MouseButton.Left && SelectedItems.Any()) { - handleRangeSelection(blueprint); + handleRangeSelection(blueprint, e.ControlPressed); return true; } - return base.MouseDownSelectionRequested(blueprint, e); + bool result = base.MouseDownSelectionRequested(blueprint, e); + // ensure that the object wasn't removed by the base implementation before making it the new pivot. + if (EditorBeatmap.HitObjects.Contains(blueprint.Item)) + pivot = blueprint.Item; + return result; } - private void handleRangeSelection(SelectionBlueprint blueprint) + /// + /// Handles a request for range selection (triggered when Shift is held down). + /// + /// The blueprint which was clicked in range selection mode. + /// + /// Whether the selection should be cumulative. + /// In cumulative mode, consecutive range selections will shift the pivot (which usually stays fixed for the duration of a range selection) + /// and will never deselect an object that was previously selected. + /// + private void handleRangeSelection(SelectionBlueprint blueprint, bool cumulative) { var clickedObject = blueprint.Item; - double rangeStart = clickedObject.StartTime; - double rangeEnd = clickedObject.GetEndTime(); - foreach (var selectedObject in SelectedItems) + Debug.Assert(pivot != null); + + double rangeStart = Math.Min(clickedObject.StartTime, pivot.StartTime); + double rangeEnd = Math.Max(clickedObject.GetEndTime(), pivot.GetEndTime()); + + var newSelection = new HashSet(EditorBeatmap.HitObjects.Where(obj => isInRange(obj, rangeStart, rangeEnd))); + + if (cumulative) { - rangeStart = Math.Min(rangeStart, selectedObject.StartTime); - rangeEnd = Math.Max(rangeEnd, selectedObject.GetEndTime()); + pivot = clickedObject; + newSelection.UnionWith(EditorBeatmap.SelectedHitObjects); } EditorBeatmap.SelectedHitObjects.Clear(); - EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects.Where(obj => isInRange(obj, rangeStart, rangeEnd))); + EditorBeatmap.SelectedHitObjects.AddRange(newSelection); bool isInRange(HitObject hitObject, double start, double end) => hitObject.StartTime >= start && hitObject.GetEndTime() <= end; From 81921bee118901a64b893fca036f217a4723fd45 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Sun, 26 Sep 2021 19:46:24 +0000 Subject: [PATCH 2107/2442] updated rhythmbonus to be OD sensitive --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 01d50c1f0d..5706e9f2cb 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private const double pi_over_4 = Math.PI / 4; private const double pi_over_2 = Math.PI / 2; - private const double rhythm_multiplier = 0.5; + private const double rhythm_multiplier = 2.0; private const int history_time_max = 5000; // 5 seconds of calculatingRhythmBonus max. private double skillMultiplier => 1375; @@ -80,9 +80,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double effectiveRatio = Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta); if (effectiveRatio > 0.5) - effectiveRatio = 0.5 + (effectiveRatio - 0.5) * 10; // large buff for 1/3 -> 1/4 type transitions. - - effectiveRatio *= Math.Max(prevDelta, currDelta) / greatWindowFull; // Increase scaling for when hitwindow is large but accuracy range is small. + effectiveRatio = 0.5 + (effectiveRatio - 0.5) * 5; // large buff for 1/3 -> 1/4 type transitions. effectiveRatio *= currHistoricalDecay; // scale with time @@ -130,7 +128,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } } - return Math.Sqrt(4 + rhythmComplexitySum * rhythm_multiplier) / 2; //produces multiplier that can be applied to strain. range [1, infinity) (not really though) + return Math.Sqrt(4 + rhythmComplexitySum * rhythm_multiplier * Math.Sqrt(52 / greatWindowFull)) / 2; //produces multiplier that can be applied to strain. range [1, infinity) (not really though) } private double tapStrainOf(DifficultyHitObject current, double speedBonus, double strainTime) From df850924262e726c5b4c5d71a159314dad55e769 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Sep 2021 14:24:17 +0900 Subject: [PATCH 2108/2442] Resolve inner items early in process and rename variable --- .../HitObjectOrderedSelectionContainer.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/HitObjectOrderedSelectionContainer.cs b/osu.Game/Screens/Edit/Compose/Components/HitObjectOrderedSelectionContainer.cs index e8286c5128..3fc26fa974 100644 --- a/osu.Game/Screens/Edit/Compose/Components/HitObjectOrderedSelectionContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/HitObjectOrderedSelectionContainer.cs @@ -41,25 +41,25 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override int Compare(Drawable x, Drawable y) { - var xObj = (SelectionBlueprint)x; - var yObj = (SelectionBlueprint)y; + var xObj = ((SelectionBlueprint)x).Item; + var yObj = ((SelectionBlueprint)y).Item; // Put earlier blueprints towards the end of the list, so they handle input first - int i = yObj.Item.StartTime.CompareTo(xObj.Item.StartTime); - if (i != 0) return i; + int result = yObj.StartTime.CompareTo(xObj.StartTime); + if (result != 0) return result; // Fall back to end time if the start time is equal. - i = yObj.Item.GetEndTime().CompareTo(xObj.Item.GetEndTime()); - if (i != 0) return i; + result = yObj.GetEndTime().CompareTo(xObj.GetEndTime()); + if (result != 0) return result; // As a final fallback, use combo information if available. - if (xObj.Item is IHasComboInformation xHasCombo && yObj.Item is IHasComboInformation yHasCombo) + if (xObj is IHasComboInformation xHasCombo && yObj is IHasComboInformation yHasCombo) { - i = yHasCombo.ComboIndex.CompareTo(xHasCombo.ComboIndex); - if (i != 0) return i; + result = yHasCombo.ComboIndex.CompareTo(xHasCombo.ComboIndex); + if (result != 0) return result; - i = yHasCombo.IndexInCurrentCombo.CompareTo(xHasCombo.IndexInCurrentCombo); - if (i != 0) return i; + result = yHasCombo.IndexInCurrentCombo.CompareTo(xHasCombo.IndexInCurrentCombo); + if (result != 0) return result; } return CompareReverseChildID(y, x); From d4310f5d9a709b49f97f85c17a1afa7f6d80ecda Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Sep 2021 17:32:40 +0900 Subject: [PATCH 2109/2442] Move database connection string operations local In line with framework changes in https://github.com/ppy/osu-framework/pull/4793. --- .../Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs | 3 ++- osu.Game/Database/DatabaseContextFactory.cs | 8 +++++--- osu.Game/IO/WrappedStorage.cs | 5 ----- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs index 6ae7f7481e..3dd34f6c2f 100644 --- a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs +++ b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs @@ -13,6 +13,7 @@ using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Framework.Threading; +using osu.Game.Database; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using SharpCompress.Compressors; @@ -160,7 +161,7 @@ namespace osu.Game.Beatmaps try { - using (var db = new SqliteConnection(storage.GetDatabaseConnectionString("online"))) + using (var db = new SqliteConnection(DatabaseContextFactory.CreateDatabaseConnectionString("online.db", storage))) { db.Open(); diff --git a/osu.Game/Database/DatabaseContextFactory.cs b/osu.Game/Database/DatabaseContextFactory.cs index d402195f29..94fa967d72 100644 --- a/osu.Game/Database/DatabaseContextFactory.cs +++ b/osu.Game/Database/DatabaseContextFactory.cs @@ -13,7 +13,7 @@ namespace osu.Game.Database { private readonly Storage storage; - private const string database_name = @"client"; + private const string database_name = @"client.db"; private ThreadLocal threadContexts; @@ -139,7 +139,7 @@ namespace osu.Game.Database threadContexts = new ThreadLocal(CreateContext, true); } - protected virtual OsuDbContext CreateContext() => new OsuDbContext(storage.GetDatabaseConnectionString(database_name)) + protected virtual OsuDbContext CreateContext() => new OsuDbContext(CreateDatabaseConnectionString(database_name, storage)) { Database = { AutoTransactionsEnabled = false } }; @@ -152,7 +152,7 @@ namespace osu.Game.Database try { - storage.DeleteDatabase(database_name); + storage.Delete(database_name); } catch { @@ -171,5 +171,7 @@ namespace osu.Game.Database recycleThreadContexts(); } + + public static string CreateDatabaseConnectionString(string filename, Storage storage) => string.Concat("Data Source=", storage.GetFullPath($@"{filename}", true)); } } diff --git a/osu.Game/IO/WrappedStorage.cs b/osu.Game/IO/WrappedStorage.cs index 5b2549d2ee..b9ccc907d9 100644 --- a/osu.Game/IO/WrappedStorage.cs +++ b/osu.Game/IO/WrappedStorage.cs @@ -70,11 +70,6 @@ namespace osu.Game.IO public override Stream GetStream(string path, FileAccess access = FileAccess.Read, FileMode mode = FileMode.OpenOrCreate) => UnderlyingStorage.GetStream(MutatePath(path), access, mode); - public override string GetDatabaseConnectionString(string name) => - UnderlyingStorage.GetDatabaseConnectionString(MutatePath(name)); - - public override void DeleteDatabase(string name) => UnderlyingStorage.DeleteDatabase(MutatePath(name)); - public override void OpenPathInNativeExplorer(string path) => UnderlyingStorage.OpenPathInNativeExplorer(MutatePath(path)); public override Storage GetStorageForDirectory(string path) From bcdbffbca0168c295f313691ccdde3b0b250c5dc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Sep 2021 20:56:54 +0900 Subject: [PATCH 2110/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 967405cd2e..27df6cf296 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 61ae6aa69e..363bf7f517 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index e032926a9c..4b8b47f982 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 1a1fc00b44198cb0e2cfd26b36a9c935f07e01fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 27 Sep 2021 20:43:08 +0200 Subject: [PATCH 2111/2442] Add failing test case --- .../Editing/TestSceneTimelineSelection.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimelineSelection.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimelineSelection.cs index f4703b79f0..2544b6c2a1 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTimelineSelection.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimelineSelection.cs @@ -223,6 +223,41 @@ namespace osu.Game.Tests.Visual.Editing moveMouseToObject(() => addedObjects[2]); AddStep("click first", () => InputManager.Click(MouseButton.Left)); assertSelectionIs(addedObjects.Take(3)); + + AddStep("release keys", () => + { + InputManager.ReleaseKey(Key.ControlLeft); + InputManager.ReleaseKey(Key.ShiftLeft); + }); + } + + [Test] + public void TestRangeSelectAfterExternalSelection() + { + var addedObjects = new[] + { + new HitCircle { StartTime = 100 }, + new HitCircle { StartTime = 200, Position = new Vector2(100) }, + new HitCircle { StartTime = 300, Position = new Vector2(200) }, + new HitCircle { StartTime = 400, Position = new Vector2(300) }, + }; + + AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects)); + + AddStep("select all without mouse", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects)); + assertSelectionIs(addedObjects); + + AddStep("hold down shift", () => InputManager.PressKey(Key.ShiftLeft)); + + moveMouseToObject(() => addedObjects[1]); + AddStep("click second object", () => InputManager.Click(MouseButton.Left)); + assertSelectionIs(addedObjects); + + moveMouseToObject(() => addedObjects[3]); + AddStep("click fourth object", () => InputManager.Click(MouseButton.Left)); + assertSelectionIs(addedObjects.Skip(1)); + + AddStep("release shift", () => InputManager.ReleaseKey(Key.ShiftLeft)); } private void assertSelectionIs(IEnumerable hitObjects) From ca6cbca04a1bbbd14536110e068f79c2f06537e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 27 Sep 2021 20:54:01 +0200 Subject: [PATCH 2112/2442] Fix range selection crashing after non-mouse selection --- .../Compose/Components/Timeline/TimelineSelectionHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs index ad770b40b5..845a671e2c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineSelectionHandler.cs @@ -80,7 +80,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline internal override bool MouseDownSelectionRequested(SelectionBlueprint blueprint, MouseButtonEvent e) { - if (e.ShiftPressed && e.Button == MouseButton.Left && SelectedItems.Any()) + if (e.ShiftPressed && e.Button == MouseButton.Left && pivot != null) { handleRangeSelection(blueprint, e.ControlPressed); return true; From 7a0499ad07bc19449981cbb5c4983934deda8658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 27 Sep 2021 21:45:26 +0200 Subject: [PATCH 2113/2442] Fix repeat arrow texture not falling back to default legacy skin --- .../Skinning/Legacy/LegacyReverseArrow.cs | 12 +++--------- .../Skinning/Legacy/OsuLegacySkinTransformer.cs | 2 +- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs index fd7bfe7e60..f605b36122 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs @@ -13,26 +13,20 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { public class LegacyReverseArrow : CompositeDrawable { - private ISkin skin { get; } - [Resolved(canBeNull: true)] private DrawableHitObject drawableHitObject { get; set; } private Drawable proxy; - public LegacyReverseArrow(ISkin skin) - { - this.skin = skin; - } - [BackgroundDependencyLoader] - private void load() + private void load(ISkinSource skinSource) { AutoSizeAxes = Axes.Both; string lookupName = new OsuSkinComponent(OsuSkinComponents.ReverseArrow).LookupName; - InternalChild = skin.GetAnimation(lookupName, true, true) ?? Empty(); + var skin = skinSource.FindProvider(skin => skin.GetTexture(lookupName) != null); + InternalChild = skin?.GetAnimation(lookupName, true, true) ?? Empty(); } protected override void LoadComplete() diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index 8a24e36420..ff9f6f0e07 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy case OsuSkinComponents.ReverseArrow: if (hasHitCircle.Value) - return new LegacyReverseArrow(this); + return new LegacyReverseArrow(); return null; From 5bd09a4a30ec343a7aa78224522041a8121021ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 27 Sep 2021 22:21:14 +0200 Subject: [PATCH 2114/2442] Rename inner lambda parameter --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs index f605b36122..7a071b5a03 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy string lookupName = new OsuSkinComponent(OsuSkinComponents.ReverseArrow).LookupName; - var skin = skinSource.FindProvider(skin => skin.GetTexture(lookupName) != null); + var skin = skinSource.FindProvider(s => s.GetTexture(lookupName) != null); InternalChild = skin?.GetAnimation(lookupName, true, true) ?? Empty(); } From f148fbcc94774816b607a33307e7627279e12631 Mon Sep 17 00:00:00 2001 From: Sebastian Krajewski Date: Wed, 29 Sep 2021 00:59:08 +0200 Subject: [PATCH 2115/2442] Cap LoopCount to at least 1 --- osu.Game/Storyboards/CommandLoop.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Storyboards/CommandLoop.cs b/osu.Game/Storyboards/CommandLoop.cs index c22ca0d8c0..c17436d813 100644 --- a/osu.Game/Storyboards/CommandLoop.cs +++ b/osu.Game/Storyboards/CommandLoop.cs @@ -1,6 +1,7 @@ // 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; namespace osu.Game.Storyboards @@ -16,7 +17,7 @@ namespace osu.Game.Storyboards public CommandLoop(double startTime, int loopCount) { LoopStartTime = startTime; - LoopCount = loopCount; + LoopCount = Math.Max(1, loopCount); } public override IEnumerable.TypedCommand> GetCommands(CommandTimelineSelector timelineSelector, double offset = 0) From 117d47bf7fab71653dc7cc35b469486a3a8df43a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Sep 2021 11:26:37 +0900 Subject: [PATCH 2116/2442] Rename JSON key resolver to better expose what it's doing --- osu.Game/IO/Serialization/JsonSerializableExtensions.cs | 2 +- .../{KeyContractResolver.cs => SnakeCaseKeyContractResolver.cs} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename osu.Game/IO/Serialization/{KeyContractResolver.cs => SnakeCaseKeyContractResolver.cs} (84%) diff --git a/osu.Game/IO/Serialization/JsonSerializableExtensions.cs b/osu.Game/IO/Serialization/JsonSerializableExtensions.cs index 5b47d0bad1..68b2110ede 100644 --- a/osu.Game/IO/Serialization/JsonSerializableExtensions.cs +++ b/osu.Game/IO/Serialization/JsonSerializableExtensions.cs @@ -22,7 +22,7 @@ namespace osu.Game.IO.Serialization ObjectCreationHandling = ObjectCreationHandling.Replace, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate, Converters = new List { new Vector2Converter() }, - ContractResolver = new KeyContractResolver() + ContractResolver = new SnakeCaseKeyContractResolver() }; } } diff --git a/osu.Game/IO/Serialization/KeyContractResolver.cs b/osu.Game/IO/Serialization/SnakeCaseKeyContractResolver.cs similarity index 84% rename from osu.Game/IO/Serialization/KeyContractResolver.cs rename to osu.Game/IO/Serialization/SnakeCaseKeyContractResolver.cs index 7b6905643c..68cb66d0dc 100644 --- a/osu.Game/IO/Serialization/KeyContractResolver.cs +++ b/osu.Game/IO/Serialization/SnakeCaseKeyContractResolver.cs @@ -6,7 +6,7 @@ using Newtonsoft.Json.Serialization; namespace osu.Game.IO.Serialization { - public class KeyContractResolver : DefaultContractResolver + public class SnakeCaseKeyContractResolver : DefaultContractResolver { protected override string ResolvePropertyName(string propertyName) { From dcf87e97c282ce1d3580087a3d900b905bfe0a95 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Sep 2021 14:03:41 +0900 Subject: [PATCH 2117/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 27df6cf296..8fad10d247 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 363bf7f517..ba118c5240 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 4b8b47f982..37931d0c38 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From c5b7e97bd9bde610074c8f529d30f347083a8003 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Sep 2021 18:50:55 +0900 Subject: [PATCH 2118/2442] Fix skin editor potentially crashing during close process As reported at https://github.com/ppy/osu/discussions/14850#discussioncomment-1399382. --- osu.Game/Skinning/Editor/SkinEditorOverlay.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs index a892ec1ca3..2b25ccbe87 100644 --- a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs +++ b/osu.Game/Skinning/Editor/SkinEditorOverlay.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.Diagnostics; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -20,6 +22,8 @@ namespace osu.Game.Skinning.Editor public class SkinEditorOverlay : CompositeDrawable, IKeyBindingHandler { private readonly ScalingContainer target; + + [CanBeNull] private SkinEditor skinEditor; public const float VISIBLE_TARGET_SCALE = 0.8f; @@ -62,6 +66,8 @@ namespace osu.Game.Skinning.Editor public override void Hide() { + Debug.Assert(skinEditor != null); + // base call intentionally omitted. skinEditor.Hide(); } @@ -71,8 +77,12 @@ namespace osu.Game.Skinning.Editor // base call intentionally omitted. if (skinEditor == null) { - LoadComponentAsync(skinEditor = new SkinEditor(target), AddInternal); + skinEditor = new SkinEditor(target); skinEditor.State.BindValueChanged(editorVisibilityChanged); + + Debug.Assert(skinEditor != null); + + LoadComponentAsync(skinEditor, AddInternal); } else skinEditor.Show(); @@ -98,8 +108,13 @@ namespace osu.Game.Skinning.Editor } } - private void updateMasking() => + private void updateMasking() + { + if (skinEditor == null) + return; + target.Masking = skinEditor.State.Value == Visibility.Visible; + } public void OnReleased(KeyBindingReleaseEvent e) { From 4fd19cbb0d54e664e644d0a65c968acea9eb3760 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Sep 2021 18:58:43 +0900 Subject: [PATCH 2119/2442] Use null check instead of assert in `Hide()` due to public exposure --- osu.Game/Skinning/Editor/SkinEditorOverlay.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs index 2b25ccbe87..d27122aea8 100644 --- a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs +++ b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs @@ -66,10 +66,8 @@ namespace osu.Game.Skinning.Editor public override void Hide() { - Debug.Assert(skinEditor != null); - // base call intentionally omitted. - skinEditor.Hide(); + skinEditor?.Hide(); } public override void Show() From 7914daaef0731ad86c63716f8df5d0b1e338617a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Sep 2021 19:09:48 +0900 Subject: [PATCH 2120/2442] Remove unused using --- osu.Game/OsuGame.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index dde60158d1..99925bb1fb 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -22,7 +22,6 @@ using Humanizer; using JetBrains.Annotations; using osu.Framework.Audio; using osu.Framework.Bindables; -using osu.Framework.Development; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics.Sprites; using osu.Framework.Input; From 73ee82ee2b95fcde64fe18168d3f705efe089414 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Sep 2021 19:15:58 +0900 Subject: [PATCH 2121/2442] Rename RecentParticipantsList -> DrawableRoomParticipantsList --- .../Multiplayer/TestSceneDrawableRoom.cs | 2 +- ... TestSceneDrawableRoomParticipantsList.cs} | 30 +++++++++---------- .../Lounge/Components/DrawableRoom.cs | 8 ++--- ...ist.cs => DrawableRoomParticipantsList.cs} | 4 +-- 4 files changed, 22 insertions(+), 22 deletions(-) rename osu.Game.Tests/Visual/Multiplayer/{TestSceneRecentParticipantsList.cs => TestSceneDrawableRoomParticipantsList.cs} (82%) rename osu.Game/Screens/OnlinePlay/Lounge/Components/{RecentParticipantsList.cs => DrawableRoomParticipantsList.cs} (98%) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index b1f5781f6f..0d4b14f90b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -130,7 +130,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Type = { Value = MatchType.HeadToHead }, })); - AddUntilStep("wait for panel load", () => drawableRoom.ChildrenOfType().Any()); + AddUntilStep("wait for panel load", () => drawableRoom.ChildrenOfType().Any()); AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType().Single().Alpha)); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs similarity index 82% rename from osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs rename to osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs index 50ec2bf3ac..ea75898946 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs @@ -13,16 +13,16 @@ using osu.Game.Users.Drawables; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneRecentParticipantsList : OnlinePlayTestScene + public class TestSceneDrawableRoomParticipantsList : OnlinePlayTestScene { - private RecentParticipantsList list; + private DrawableRoomParticipantsList list; [SetUp] public new void Setup() => Schedule(() => { SelectedRoom.Value = new Room { Name = { Value = "test room" } }; - Child = list = new RecentParticipantsList + Child = list = new DrawableRoomParticipantsList { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -40,19 +40,19 @@ namespace osu.Game.Tests.Visual.Multiplayer }); AddStep("set 8 circles", () => list.NumberOfCircles = 8); - AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0); + AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0); AddStep("add one more user", () => addUser(9)); - AddAssert("2 hidden users", () => list.ChildrenOfType().Single().Count == 2); + AddAssert("2 hidden users", () => list.ChildrenOfType().Single().Count == 2); AddStep("remove first user", () => removeUserAt(0)); - AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0); + AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0); AddStep("add one more user", () => addUser(9)); - AddAssert("2 hidden users", () => list.ChildrenOfType().Single().Count == 2); + AddAssert("2 hidden users", () => list.ChildrenOfType().Single().Count == 2); AddStep("remove last user", () => removeUserAt(8)); - AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0); + AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0); } [Test] @@ -87,11 +87,11 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("set 3 circles", () => list.NumberOfCircles = 3); AddAssert("2 users displayed", () => list.ChildrenOfType().Count() == 2); - AddAssert("48 hidden users", () => list.ChildrenOfType().Single().Count == 48); + AddAssert("48 hidden users", () => list.ChildrenOfType().Single().Count == 48); AddStep("set 10 circles", () => list.NumberOfCircles = 10); AddAssert("9 users displayed", () => list.ChildrenOfType().Count() == 9); - AddAssert("41 hidden users", () => list.ChildrenOfType().Single().Count == 41); + AddAssert("41 hidden users", () => list.ChildrenOfType().Single().Count == 41); } [Test] @@ -105,20 +105,20 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("remove from start", () => removeUserAt(0)); AddAssert("3 circles displayed", () => list.ChildrenOfType().Count() == 3); - AddAssert("46 hidden users", () => list.ChildrenOfType().Single().Count == 46); + AddAssert("46 hidden users", () => list.ChildrenOfType().Single().Count == 46); AddStep("remove from end", () => removeUserAt(SelectedRoom.Value.RecentParticipants.Count - 1)); AddAssert("3 circles displayed", () => list.ChildrenOfType().Count() == 3); - AddAssert("45 hidden users", () => list.ChildrenOfType().Single().Count == 45); + AddAssert("45 hidden users", () => list.ChildrenOfType().Single().Count == 45); AddRepeatStep("remove 45 users", () => removeUserAt(0), 45); AddAssert("3 circles displayed", () => list.ChildrenOfType().Count() == 3); - AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0); - AddAssert("hidden users bubble hidden", () => list.ChildrenOfType().Single().Alpha < 0.5f); + AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0); + AddAssert("hidden users bubble hidden", () => list.ChildrenOfType().Single().Alpha < 0.5f); AddStep("remove another user", () => removeUserAt(0)); AddAssert("2 circles displayed", () => list.ChildrenOfType().Count() == 2); - AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0); + AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0); AddRepeatStep("remove the remaining two users", () => removeUserAt(0), 2); AddAssert("0 circles displayed", () => !list.ChildrenOfType().Any()); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 80070aa6ba..f2cd9d1410 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -38,7 +38,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private readonly Bindable roomCategory = new Bindable(); private readonly Bindable hasPassword = new Bindable(); - private RecentParticipantsList recentParticipantsList; + private DrawableRoomParticipantsList drawableRoomParticipantsList; private RoomSpecialCategoryPill specialCategoryPill; private PasswordProtectedIcon passwordIcon; private EndDateInfo endDateInfo; @@ -217,7 +217,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Children = new Drawable[] { ButtonsContainer, - recentParticipantsList = new RecentParticipantsList + drawableRoomParticipantsList = new DrawableRoomParticipantsList { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, @@ -280,8 +280,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { numberOfAvatars = value; - if (recentParticipantsList != null) - recentParticipantsList.NumberOfCircles = value; + if (drawableRoomParticipantsList != null) + drawableRoomParticipantsList.NumberOfCircles = value; } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs similarity index 98% rename from osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs rename to osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs index bc658f45e4..961ab276dc 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RecentParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs @@ -17,7 +17,7 @@ using osuTK; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { - public class RecentParticipantsList : OnlinePlayComposite + public class DrawableRoomParticipantsList : OnlinePlayComposite { private const float avatar_size = 36; @@ -26,7 +26,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private HiddenUserCount hiddenUsers; private OsuSpriteText totalCount; - public RecentParticipantsList() + public DrawableRoomParticipantsList() { AutoSizeAxes = Axes.X; Height = 60; From d89577b2e7d3056631b082418be5488775363183 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Sep 2021 19:34:40 +0900 Subject: [PATCH 2122/2442] Add host to DrawableRoomParticipantsList --- .../TestSceneDrawableRoomParticipantsList.cs | 13 +- .../DrawableRoomParticipantsList.cs | 133 ++++++++++++++---- 2 files changed, 115 insertions(+), 31 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs index ea75898946..6909bc3f97 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs @@ -20,7 +20,18 @@ namespace osu.Game.Tests.Visual.Multiplayer [SetUp] public new void Setup() => Schedule(() => { - SelectedRoom.Value = new Room { Name = { Value = "test room" } }; + SelectedRoom.Value = new Room + { + Name = { Value = "test room" }, + Host = + { + Value = new User + { + Id = 2, + Username = "peppy", + } + } + }; Child = list = new DrawableRoomParticipantsList { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs index 961ab276dc..3d366a4c92 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs @@ -4,11 +4,13 @@ using System.Collections.Specialized; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osu.Game.Users; @@ -23,6 +25,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private FillFlowContainer avatarFlow; + private CircularAvatar hostAvatar; + private LinkFlowContainer hostText; private HiddenUserCount hiddenUsers; private OsuSpriteText totalCount; @@ -51,42 +55,99 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components }, new FillFlowContainer { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(4), - Padding = new MarginPadding { Right = 16 }, + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, Children = new Drawable[] { - new SpriteIcon + new FillFlowContainer { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Size = new Vector2(16), - Margin = new MarginPadding { Left = 8 }, - Icon = FontAwesome.Solid.User, + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Spacing = new Vector2(8), + Padding = new MarginPadding + { + Left = 8, + Right = 16 + }, + Children = new Drawable[] + { + hostAvatar = new CircularAvatar + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + hostText = new LinkFlowContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + Text = "hosted by smoogipoo" + } + } }, - totalCount = new OsuSpriteText + new Container { - Font = OsuFont.Default.With(weight: FontWeight.Bold), - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 10, + Shear = new Vector2(0.2f, 0), + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Background3, + } + }, + new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(4), + Padding = new MarginPadding + { + Left = 8, + Right = 16 + }, + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(16), + Icon = FontAwesome.Solid.User, + }, + totalCount = new OsuSpriteText + { + Font = OsuFont.Default.With(weight: FontWeight.Bold), + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + avatarFlow = new FillFlowContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(4), + Margin = new MarginPadding { Left = 4 }, + }, + hiddenUsers = new HiddenUserCount + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + } + } + } + } }, - avatarFlow = new FillFlowContainer - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(4), - Margin = new MarginPadding { Left = 4 }, - }, - hiddenUsers = new HiddenUserCount - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - } } } }; @@ -102,6 +163,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components updateHiddenUsers(); totalCount.Text = ParticipantCount.Value.ToString(); }, true); + + Host.BindValueChanged(onHostChanged, true); } private int numberOfCircles = 4; @@ -194,6 +257,16 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } + private void onHostChanged(ValueChangedEvent host) + { + hostAvatar.User = host.NewValue; + hostText.Clear(); + + hostText.AddText(@"hosted by "); + if (host.NewValue != null) + hostText.AddUserLink(host.NewValue); + } + private class CircularAvatar : CompositeDrawable { public User User From 5f921c7836d8be698a3d3f66bc67a27251c36cd3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Sep 2021 20:24:32 +0900 Subject: [PATCH 2123/2442] Change SelectedItem to show the last item by default --- osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index 24b3b4ec94..722a2a0b94 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -66,7 +66,7 @@ namespace osu.Game.Screens.OnlinePlay protected Bindable Duration { get; private set; } /// - /// The currently selected item in the , or the first item from + /// The currently selected item in the , or the last item from /// if this is not within a . /// protected readonly Bindable SelectedItem = new Bindable(); @@ -80,7 +80,7 @@ namespace osu.Game.Screens.OnlinePlay protected virtual void UpdateSelectedItem() { - SelectedItem.Value = Playlist.FirstOrDefault(); + SelectedItem.Value = Playlist.LastOrDefault(); } } } From 67d847fbd3493c30fb1821345e38dfec4299487c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Sep 2021 20:24:49 +0900 Subject: [PATCH 2124/2442] Add room status text to DrawableRoom --- .../Multiplayer/TestSceneDrawableRoom.cs | 21 ++++----- .../Lounge/Components/DrawableRoom.cs | 43 +++++++++++++------ 2 files changed, 40 insertions(+), 24 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index 0d4b14f90b..22ff2b98ce 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -43,11 +43,12 @@ namespace osu.Game.Tests.Visual.Multiplayer Spacing = new Vector2(10), Children = new Drawable[] { - createDrawableRoom(new Room + createLoungeRoom(new Room { - Name = { Value = "Flyte's Trash Playlist" }, + Name = { Value = "Multiplayer room" }, Status = { Value = new RoomStatusOpen() }, EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, + Type = { Value = MatchType.HeadToHead }, Playlist = { new PlaylistItem @@ -65,9 +66,9 @@ namespace osu.Game.Tests.Visual.Multiplayer } } }), - createDrawableRoom(new Room + createLoungeRoom(new Room { - Name = { Value = "Room 2" }, + Name = { Value = "Playlist room with multiple beatmaps" }, Status = { Value = new RoomStatusPlaying() }, EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, Playlist = @@ -100,15 +101,15 @@ namespace osu.Game.Tests.Visual.Multiplayer } } }), - createDrawableRoom(new Room + createLoungeRoom(new Room { - Name = { Value = "Room 3" }, + Name = { Value = "Finished room" }, Status = { Value = new RoomStatusEnded() }, EndDate = { Value = DateTimeOffset.Now }, }), - createDrawableRoom(new Room + createLoungeRoom(new Room { - Name = { Value = "Room 4 (spotlight)" }, + Name = { Value = "Spotlight room" }, Status = { Value = new RoomStatusOpen() }, Category = { Value = RoomCategory.Spotlight }, }), @@ -123,7 +124,7 @@ namespace osu.Game.Tests.Visual.Multiplayer DrawableRoom drawableRoom = null; Room room = null; - AddStep("create room", () => Child = drawableRoom = createDrawableRoom(room = new Room + AddStep("create room", () => Child = drawableRoom = createLoungeRoom(room = new Room { Name = { Value = "Room with password" }, Status = { Value = new RoomStatusOpen() }, @@ -141,7 +142,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType().Single().Alpha)); } - private DrawableRoom createDrawableRoom(Room room) + private DrawableRoom createLoungeRoom(Room room) { room.Host.Value ??= new User { Username = "peppy", Id = 2 }; diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index f2cd9d1410..df356e64bd 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -14,6 +14,7 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Online.Chat; using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Screens.OnlinePlay.Components; @@ -172,7 +173,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Children = new Drawable[] { new RoomNameText(), - new RoomHostText(), + new RoomStatusText() } } }, @@ -304,11 +305,14 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } - private class RoomHostText : OnlinePlayComposite + private class RoomStatusText : OnlinePlayComposite { - private LinkFlowContainer hostText; + [Resolved] + private OsuColour colours { get; set; } - public RoomHostText() + private LinkFlowContainer linkFlow; + + public RoomStatusText() { AutoSizeAxes = Axes.Both; } @@ -316,26 +320,37 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components [BackgroundDependencyLoader] private void load() { - InternalChild = hostText = new LinkFlowContainer(s => s.Font = OsuFont.GetFont(size: 16)) + InternalChild = linkFlow = new LinkFlowContainer(s => { - AutoSizeAxes = Axes.Both + s.Font = OsuFont.Default.With(size: 16); + s.Colour = colours.Lime1; + }) + { + AutoSizeAxes = Axes.Both, }; } protected override void LoadComplete() { base.LoadComplete(); + SelectedItem.BindValueChanged(onSelectedItemChanged, true); + } - Host.BindValueChanged(host => + private void onSelectedItemChanged(ValueChangedEvent item) + { + if (Type.Value == MatchType.Playlists) { - hostText.Clear(); + linkFlow.Text = "Waiting for players"; + return; + } - if (host.NewValue != null) - { - hostText.AddText("hosted by "); - hostText.AddUserLink(host.NewValue); - } - }, true); + linkFlow.Clear(); + + if (item.NewValue?.Beatmap.Value != null) + { + linkFlow.AddText("Currently playing "); + linkFlow.AddLink(item.NewValue.Beatmap.Value.ToRomanisableString(), LinkAction.OpenBeatmap, item.NewValue.Beatmap.Value.OnlineBeatmapID.ToString()); + } } } From 94e2dbd7e76f4da32b6be693d7d17ae740ca83b5 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 23 Sep 2021 20:14:32 +0900 Subject: [PATCH 2125/2442] Add a low-pass filter effect to music when certain popup dialogs are shown --- osu.Game/Audio/Effects/LowPassFilter.cs | 75 +++++++++++++++++++++++++ osu.Game/Overlays/DialogOverlay.cs | 15 +++++ 2 files changed, 90 insertions(+) create mode 100644 osu.Game/Audio/Effects/LowPassFilter.cs diff --git a/osu.Game/Audio/Effects/LowPassFilter.cs b/osu.Game/Audio/Effects/LowPassFilter.cs new file mode 100644 index 0000000000..b1e78e4324 --- /dev/null +++ b/osu.Game/Audio/Effects/LowPassFilter.cs @@ -0,0 +1,75 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using ManagedBass.Fx; +using osu.Framework.Audio.Mixing; +using osu.Framework.Bindables; +using osu.Framework.Graphics; + +namespace osu.Game.Audio.Effects +{ + public class LowPassFilter : Component + { + private const float filter_cutoff_start = 2000; + private const float filter_cutoff_end = 150; + private const float filter_sweep_duration = 100; + private readonly Bindable filterFreq = new Bindable(filter_cutoff_start); + private readonly AudioMixer mixer; + private readonly BQFParameters filter; + + /// + /// A toggle-able low-pass filter with a subtle filter-sweep effect when toggled that can be attached to an . + /// + public LowPassFilter(AudioMixer mixer) + { + this.mixer = mixer; + filter = new BQFParameters + { + lFilter = BQFType.LowPass, + fCenter = filterFreq.Value + }; + } + + public void Enable() + { + attachFilter(); + this.TransformBindableTo(filterFreq, filter_cutoff_end, filter_sweep_duration); + } + + public void Disable() + { + this.TransformBindableTo(filterFreq, filter_cutoff_start, filter_sweep_duration) + .OnComplete(_ => detatchFilter()); + } + + private void attachFilter() + { + mixer.Effects.Add(filter); + filterFreq.ValueChanged += updateFilter; + } + + private void detatchFilter() + { + filterFreq.ValueChanged -= updateFilter; + mixer.Effects.Remove(filter); + } + + private void updateFilter(ValueChangedEvent cutoff) + { + var filterIndex = mixer.Effects.IndexOf(filter); + if (filterIndex < 0) return; + + var existingFilter = mixer.Effects[filterIndex] as BQFParameters; + if (existingFilter == null) return; + + existingFilter.fCenter = cutoff.NewValue; + mixer.Effects[filterIndex] = existingFilter; + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + detatchFilter(); + } + } +} diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index f051e09c08..d653ef3888 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -7,7 +7,10 @@ using osu.Game.Overlays.Dialog; using osu.Game.Graphics.Containers; using osu.Game.Input.Bindings; using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Input.Events; +using osu.Game.Audio.Effects; namespace osu.Game.Overlays { @@ -18,6 +21,8 @@ namespace osu.Game.Overlays protected override string PopInSampleName => "UI/dialog-pop-in"; protected override string PopOutSampleName => "UI/dialog-pop-out"; + private LowPassFilter filter; + public PopupDialog CurrentDialog { get; private set; } public DialogOverlay() @@ -34,6 +39,12 @@ namespace osu.Game.Overlays Origin = Anchor.BottomCentre; } + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + AddInternal(filter = new LowPassFilter(audio.TrackMixer)); + } + public void Push(PopupDialog dialog) { if (dialog == CurrentDialog || dialog.State.Value != Visibility.Visible) return; @@ -71,12 +82,16 @@ namespace osu.Game.Overlays { base.PopIn(); this.FadeIn(PopupDialog.ENTER_DURATION, Easing.OutQuint); + filter.Enable(); } protected override void PopOut() { base.PopOut(); + if (IsLoaded) + filter.Disable(); + if (CurrentDialog?.State.Value == Visibility.Visible) { CurrentDialog.Hide(); From 2608d193a9518e2403cc3c1053a93e5149a3fb5d Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Wed, 29 Sep 2021 19:19:33 +0900 Subject: [PATCH 2126/2442] Refactor `Filter` component to be more re-usable --- .../Effects/{LowPassFilter.cs => Filter.cs} | 44 ++++++++++++------- osu.Game/Overlays/DialogOverlay.cs | 4 +- 2 files changed, 30 insertions(+), 18 deletions(-) rename osu.Game/Audio/Effects/{LowPassFilter.cs => Filter.cs} (60%) diff --git a/osu.Game/Audio/Effects/LowPassFilter.cs b/osu.Game/Audio/Effects/Filter.cs similarity index 60% rename from osu.Game/Audio/Effects/LowPassFilter.cs rename to osu.Game/Audio/Effects/Filter.cs index b1e78e4324..37a2fa93db 100644 --- a/osu.Game/Audio/Effects/LowPassFilter.cs +++ b/osu.Game/Audio/Effects/Filter.cs @@ -8,50 +8,62 @@ using osu.Framework.Graphics; namespace osu.Game.Audio.Effects { - public class LowPassFilter : Component + public class Filter : Component { - private const float filter_cutoff_start = 2000; - private const float filter_cutoff_end = 150; - private const float filter_sweep_duration = 100; - private readonly Bindable filterFreq = new Bindable(filter_cutoff_start); + public BQFType FilterType = BQFType.LowPass; + public float SweepCutoffStart = 2000; + public float SweepCutoffEnd = 150; + public float SweepDuration = 100; + public Easing SweepEasing = Easing.None; + + public bool IsActive { get; private set; } + + private readonly Bindable filterFreq = new Bindable(); private readonly AudioMixer mixer; - private readonly BQFParameters filter; + private BQFParameters filter; /// - /// A toggle-able low-pass filter with a subtle filter-sweep effect when toggled that can be attached to an . + /// A BiQuad filter that performs a filter-sweep when toggled on or off. /// - public LowPassFilter(AudioMixer mixer) + /// The mixer this effect should be attached to. + public Filter(AudioMixer mixer) { this.mixer = mixer; - filter = new BQFParameters - { - lFilter = BQFType.LowPass, - fCenter = filterFreq.Value - }; } public void Enable() { attachFilter(); - this.TransformBindableTo(filterFreq, filter_cutoff_end, filter_sweep_duration); + this.TransformBindableTo(filterFreq, SweepCutoffEnd, SweepDuration, SweepEasing); } public void Disable() { - this.TransformBindableTo(filterFreq, filter_cutoff_start, filter_sweep_duration) - .OnComplete(_ => detatchFilter()); + this.TransformBindableTo(filterFreq, SweepCutoffStart, SweepDuration, SweepEasing).OnComplete(_ => detatchFilter()); } private void attachFilter() { + if (IsActive) return; + + filter = new BQFParameters + { + lFilter = FilterType, + fCenter = filterFreq.Value = SweepCutoffStart + }; + mixer.Effects.Add(filter); filterFreq.ValueChanged += updateFilter; + IsActive = true; } private void detatchFilter() { + if (!IsActive) return; + filterFreq.ValueChanged -= updateFilter; mixer.Effects.Remove(filter); + IsActive = false; } private void updateFilter(ValueChangedEvent cutoff) diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index d653ef3888..3fade81cec 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays protected override string PopInSampleName => "UI/dialog-pop-in"; protected override string PopOutSampleName => "UI/dialog-pop-out"; - private LowPassFilter filter; + private Filter filter; public PopupDialog CurrentDialog { get; private set; } @@ -42,7 +42,7 @@ namespace osu.Game.Overlays [BackgroundDependencyLoader] private void load(AudioManager audio) { - AddInternal(filter = new LowPassFilter(audio.TrackMixer)); + AddInternal(filter = new Filter(audio.TrackMixer)); } public void Push(PopupDialog dialog) From c9c2d205447b5ddd5ade0787da36f88b79c20f5e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Sep 2021 20:44:38 +0900 Subject: [PATCH 2127/2442] Limit max size --- .../Lounge/Components/DrawableRoom.cs | 63 ++++++++++++++----- 1 file changed, 48 insertions(+), 15 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index df356e64bd..9e693b0b0c 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -137,7 +137,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { new FillFlowContainer { - AutoSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, Children = new Drawable[] { @@ -167,7 +168,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components }, new FillFlowContainer { - AutoSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Top = 3 }, Direction = FillDirection.Vertical, Children = new Drawable[] @@ -310,23 +312,47 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components [Resolved] private OsuColour colours { get; set; } - private LinkFlowContainer linkFlow; + private SpriteText statusText; + private LinkFlowContainer beatmapText; public RoomStatusText() { - AutoSizeAxes = Axes.Both; + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Width = 0.5f; } [BackgroundDependencyLoader] private void load() { - InternalChild = linkFlow = new LinkFlowContainer(s => + InternalChild = new GridContainer { - s.Font = OsuFont.Default.With(size: 16); - s.Colour = colours.Lime1; - }) - { - AutoSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new Drawable[] + { + statusText = new OsuSpriteText + { + Font = OsuFont.Default.With(size: 16), + Colour = colours.Lime1 + }, + beatmapText = new LinkFlowContainer(s => + { + s.Font = OsuFont.Default.With(size: 16); + s.Colour = colours.Lime1; + }) + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + } + } + } }; } @@ -338,18 +364,25 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private void onSelectedItemChanged(ValueChangedEvent item) { + beatmapText.Clear(); + if (Type.Value == MatchType.Playlists) { - linkFlow.Text = "Waiting for players"; + statusText.Text = "Waiting for players"; return; } - linkFlow.Clear(); - if (item.NewValue?.Beatmap.Value != null) { - linkFlow.AddText("Currently playing "); - linkFlow.AddLink(item.NewValue.Beatmap.Value.ToRomanisableString(), LinkAction.OpenBeatmap, item.NewValue.Beatmap.Value.OnlineBeatmapID.ToString()); + statusText.Text = "Currently playing "; + beatmapText.AddLink(item.NewValue.Beatmap.Value.ToRomanisableString(), + LinkAction.OpenBeatmap, + item.NewValue.Beatmap.Value.OnlineBeatmapID.ToString(), + creationParameters: s => + { + s.Truncate = true; + s.RelativeSizeAxes = Axes.X; + }); } } } From 909f8d1d9800eb7ffd820463865b38ea4e6f5264 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Wed, 29 Sep 2021 19:20:05 +0900 Subject: [PATCH 2128/2442] Add visual tests for `Filter` --- osu.Game.Tests/Audio/TestSceneFilter.cs | 95 +++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 osu.Game.Tests/Audio/TestSceneFilter.cs diff --git a/osu.Game.Tests/Audio/TestSceneFilter.cs b/osu.Game.Tests/Audio/TestSceneFilter.cs new file mode 100644 index 0000000000..fd4fe530eb --- /dev/null +++ b/osu.Game.Tests/Audio/TestSceneFilter.cs @@ -0,0 +1,95 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using ManagedBass.Fx; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Graphics; +using osu.Game.Audio.Effects; +using osu.Game.Beatmaps; +using osu.Game.Tests.Visual; + +namespace osu.Game.Tests.Audio +{ + public class TestSceneFilter : OsuTestScene + { + [Resolved] + private AudioManager audio { get; set; } + + private WorkingBeatmap testBeatmap; + private Filter lowPassFilter; + private Filter highPassFilter; + private Filter bandPassFilter; + + [BackgroundDependencyLoader] + private void load() + { + testBeatmap = new WaveformTestBeatmap(audio); + AddRange(new Drawable[] + { + lowPassFilter = new Filter(audio.TrackMixer) + { + FilterType = BQFType.LowPass, + SweepCutoffStart = 2000, + SweepCutoffEnd = 150, + SweepDuration = 1000 + }, + highPassFilter = new Filter(audio.TrackMixer) + { + FilterType = BQFType.HighPass, + SweepCutoffStart = 150, + SweepCutoffEnd = 2000, + SweepDuration = 1000 + }, + bandPassFilter = new Filter(audio.TrackMixer) + { + FilterType = BQFType.BandPass, + SweepCutoffStart = 150, + SweepCutoffEnd = 20000, + SweepDuration = 1000 + }, + }); + } + + [Test] + public void TestLowPass() + { + testFilter(lowPassFilter); + } + + [Test] + public void TestHighPass() + { + testFilter(highPassFilter); + } + + [Test] + public void TestBandPass() + { + testFilter(bandPassFilter); + } + + private void testFilter(Filter filter) + { + AddStep("Prepare Track", () => + { + testBeatmap = new WaveformTestBeatmap(audio); + testBeatmap.LoadTrack(); + }); + AddStep("Play Track", () => + { + testBeatmap.Track.Start(); + }); + AddWaitStep("Let track play", 10); + AddStep("Enable Filter", filter.Enable); + AddWaitStep("Let track play", 10); + AddStep("Disable Filter", filter.Disable); + AddWaitStep("Let track play", 10); + AddStep("Stop Track", () => + { + testBeatmap.Track.Stop(); + }); + } + } +} From 4d91204faff435d6104a1333800f8b94e248a7d1 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Wed, 29 Sep 2021 20:29:27 +0900 Subject: [PATCH 2129/2442] Pause `Editor` sample playback while popup dialog is shown --- osu.Game/Screens/Edit/Editor.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 2ff0101dc0..1170658abb 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -508,6 +508,7 @@ namespace osu.Game.Screens.Edit if (isNewBeatmap || HasUnsavedChanges) { + samplePlaybackDisabled.Value = true; dialogOverlay?.Push(new PromptForSaveDialog(confirmExit, confirmExitWithSave, cancelExit)); return true; } @@ -756,7 +757,11 @@ namespace osu.Game.Screens.Edit ClipboardContent = editorBeatmap.BeatmapInfo.RulesetID == nextBeatmap.RulesetID ? clipboard.Value : string.Empty }); - private void cancelExit() => loader?.CancelPendingDifficultySwitch(); + private void cancelExit() + { + samplePlaybackDisabled.Value = false; + loader?.CancelPendingDifficultySwitch(); + } public double SnapTime(double time, double? referenceTime) => editorBeatmap.SnapTime(time, referenceTime); From c83dd7d2b62117c09ab942fb5bd8aeaa790f624e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Sep 2021 20:52:03 +0900 Subject: [PATCH 2130/2442] Merge OnlinePlayComposite and RoomSubScreenComposite --- .../Match/BeatmapSelectionControl.cs | 2 +- .../Screens/OnlinePlay/OnlinePlayComposite.cs | 10 +++-- .../OnlinePlay/RoomSubScreenComposite.cs | 38 ------------------- 3 files changed, 8 insertions(+), 42 deletions(-) delete mode 100644 osu.Game/Screens/OnlinePlay/RoomSubScreenComposite.cs diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs index 6f1817a77c..35f30edf65 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/BeatmapSelectionControl.cs @@ -12,7 +12,7 @@ using osuTK; namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { - public class BeatmapSelectionControl : RoomSubScreenComposite + public class BeatmapSelectionControl : OnlinePlayComposite { [Resolved] private MultiplayerMatchSubScreen matchSubScreen { get; set; } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index 722a2a0b94..aa971864ef 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -65,6 +65,9 @@ namespace osu.Game.Screens.OnlinePlay [Resolved(typeof(Room))] protected Bindable Duration { get; private set; } + [Resolved(CanBeNull = true)] + private IBindable subScreenSelectedItem { get; set; } + /// /// The currently selected item in the , or the last item from /// if this is not within a . @@ -75,12 +78,13 @@ namespace osu.Game.Screens.OnlinePlay { base.LoadComplete(); + subScreenSelectedItem?.BindValueChanged(_ => UpdateSelectedItem()); Playlist.BindCollectionChanged((_, __) => UpdateSelectedItem(), true); } protected virtual void UpdateSelectedItem() - { - SelectedItem.Value = Playlist.LastOrDefault(); - } + => SelectedItem.Value = RoomID.Value == null || subScreenSelectedItem == null + ? Playlist.LastOrDefault() + : subScreenSelectedItem.Value; } } diff --git a/osu.Game/Screens/OnlinePlay/RoomSubScreenComposite.cs b/osu.Game/Screens/OnlinePlay/RoomSubScreenComposite.cs deleted file mode 100644 index 4cfd881aa3..0000000000 --- a/osu.Game/Screens/OnlinePlay/RoomSubScreenComposite.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Game.Online.Rooms; -using osu.Game.Screens.OnlinePlay.Match; - -namespace osu.Game.Screens.OnlinePlay -{ - /// - /// An with additional logic tracking the currently-selected inside a . - /// - public class RoomSubScreenComposite : OnlinePlayComposite - { - [Resolved] - private IBindable subScreenSelectedItem { get; set; } - - protected override void LoadComplete() - { - base.LoadComplete(); - - subScreenSelectedItem.BindValueChanged(_ => UpdateSelectedItem(), true); - } - - protected override void UpdateSelectedItem() - { - if (RoomID.Value == null) - { - // If the room hasn't been created yet, fall-back to the base logic. - base.UpdateSelectedItem(); - return; - } - - SelectedItem.Value = subScreenSelectedItem.Value; - } - } -} From f55c4ac6406cabd1997f14e7643cf4a2156f8062 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Wed, 29 Sep 2021 21:05:46 +0900 Subject: [PATCH 2131/2442] IsLoaded check no longer required --- osu.Game/Overlays/DialogOverlay.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index 3fade81cec..8a274968b7 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -89,8 +89,7 @@ namespace osu.Game.Overlays { base.PopOut(); - if (IsLoaded) - filter.Disable(); + filter.Disable(); if (CurrentDialog?.State.Value == Visibility.Visible) { From d14eed88fde0996855716be8394551c973a5d862 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Wed, 29 Sep 2021 19:14:54 +0000 Subject: [PATCH 2132/2442] final clean up before PR --- .../Difficulty/Skills/Speed.cs | 60 ++++++++----------- 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 5706e9f2cb..6b21ae7ac6 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -16,13 +16,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills public class Speed : OsuStrainSkill { private const double single_spacing_threshold = 125; - - private const double angle_bonus_begin = 5 * Math.PI / 6; - private const double pi_over_4 = Math.PI / 4; - private const double pi_over_2 = Math.PI / 2; - - private const double rhythm_multiplier = 2.0; + private const double rhythm_multiplier = 4.0; private const int history_time_max = 5000; // 5 seconds of calculatingRhythmBonus max. + private const double min_speed_bonus = 75; // ~200BPM + private const double max_speed_bonus = 45; + private const double speed_balancing_factor = 40; private double skillMultiplier => 1375; private double strainDecayBase => 0.3; @@ -33,11 +31,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override int ReducedSectionCount => 5; protected override double DifficultyMultiplier => 1.04; - - private const double min_speed_bonus = 75; // ~200BPM - private const double max_speed_bonus = 45; - private const double speed_balancing_factor = 40; - protected override int HistoryLength => 32; private readonly double greatWindow; @@ -59,6 +52,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills int previousIslandSize = -1; double rhythmComplexitySum = 0; int islandSize = 0; + double startRatio = 0; // store the ratio of the current start of an island to buff for tighter rhythms bool firstDeltaSwitch = false; @@ -66,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { DifficultyHitObject currObj = Previous[i - 1]; DifficultyHitObject prevObj = Previous[i]; - DifficultyHitObject prevPrevObj = Previous[i + 1]; + DifficultyHitObject lastObj = Previous[i + 1]; double currHistoricalDecay = Math.Max(0, (history_time_max - (current.StartTime - currObj.StartTime))) / history_time_max; // scales note 0 to 1 from history to now @@ -76,11 +70,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double currDelta = Math.Max(25, currObj.DeltaTime); double prevDelta = Math.Max(25, prevObj.DeltaTime); - double prevPrevDelta = ((OsuDifficultyHitObject)prevPrevObj).StrainTime; + double lastDelta = ((OsuDifficultyHitObject)lastObj).StrainTime; double effectiveRatio = Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta); if (effectiveRatio > 0.5) - effectiveRatio = 0.5 + (effectiveRatio - 0.5) * 5; // large buff for 1/3 -> 1/4 type transitions. + effectiveRatio = 0.5 + (effectiveRatio - 0.5) * 6; // large buff for 1/3 -> 1/4 type transitions. + else + effectiveRatio = 0.5; effectiveRatio *= currHistoricalDecay; // scale with time @@ -92,10 +88,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } else { - - - if (islandSize > 6) - islandSize = 6; + if (islandSize > 12) + islandSize = 12; if (Previous[i - 1].BaseObject is Slider) // bpm change is into slider, this is easy acc window effectiveRatio *= 0.25; @@ -104,12 +98,17 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills effectiveRatio *= 0.5; if (previousIslandSize == islandSize) // repeated island size (ex: triplet -> triplet) - effectiveRatio *= 0.25; + effectiveRatio *= 0.35; - if (prevPrevDelta > prevDelta + 10 && prevDelta > currDelta + 10) // previous increase happened a note ago, 1/1->1/2-1/4, dont want to buff this. + if (previousIslandSize % 2 == islandSize % 2) // repeated island polartiy (2 -> 4, 3 -> 5) + effectiveRatio *= 0.75; + + if (lastDelta > prevDelta + 10 && prevDelta > currDelta + 10) // previous increase happened a note ago, 1/1->1/2-1/4, dont want to buff this. effectiveRatio *= 0.125; - rhythmComplexitySum += effectiveRatio; + rhythmComplexitySum += effectiveRatio * startRatio; + + startRatio = Math.Sqrt(Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta)); previousIslandSize = islandSize; // log the last island size. @@ -124,11 +123,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills // Begin counting island until we change speed again. firstDeltaSwitch = true; islandSize = 0; + startRatio = Math.Sqrt(Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta)); } } } - return Math.Sqrt(4 + rhythmComplexitySum * rhythm_multiplier * Math.Sqrt(52 / greatWindowFull)) / 2; //produces multiplier that can be applied to strain. range [1, infinity) (not really though) + if (greatWindowFull > 62) + rhythmComplexitySum *= Math.Sqrt(62 / greatWindowFull); + + return Math.Sqrt(4 + rhythmComplexitySum * rhythm_multiplier) / 2; //produces multiplier that can be applied to strain. range [1, infinity) (not really though) } private double tapStrainOf(DifficultyHitObject current, double speedBonus, double strainTime) @@ -147,19 +150,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills var osuCurrObj = (OsuDifficultyHitObject)current; double distance = Math.Min(single_spacing_threshold, osuCurrObj.TravelDistance + osuCurrObj.JumpDistance); - double angleBonus = 1.0; - if (osuCurrObj.Angle != null) - { - double angle = osuCurrObj.Angle.Value; - - if (angle < pi_over_2) - angleBonus = 1.25; - else if (angle < angle_bonus_begin) - angleBonus = 1 + Math.Pow(Math.Sin(1.5 * (angle_bonus_begin - angle)), 2) / 4; - } - - return (angleBonus * speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / strainTime; + return (speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / strainTime; } private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000); From 56b3c8aa9a72be245240de2eea652b8a77a5a64d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Sep 2021 11:52:14 +0900 Subject: [PATCH 2133/2442] Remove forgotten text --- .../Lounge/Components/DrawableRoomParticipantsList.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs index 3d366a4c92..db1686ef4a 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs @@ -80,8 +80,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - Text = "hosted by smoogipoo" + AutoSizeAxes = Axes.Both } } }, From 619a907c47df793c6c03e1c6bb111423acff02b4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Sep 2021 12:01:26 +0900 Subject: [PATCH 2134/2442] Fix zero height grid --- osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 9e693b0b0c..f2af38c2df 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -333,6 +333,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { new Dimension(GridSizeMode.AutoSize), }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize) + }, Content = new[] { new Drawable[] From 18ab6747f73306610a607bec265bacffa009d16a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Sep 2021 12:01:28 +0900 Subject: [PATCH 2135/2442] Fix tests --- .../TestSceneDrawableRoomParticipantsList.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs index 6909bc3f97..982dfc5cd9 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs @@ -80,9 +80,9 @@ namespace osu.Game.Tests.Visual.Multiplayer for (int i = 0; i < 8; i++) { AddStep("remove user", () => removeUserAt(0)); - int remainingUsers = 7 - i; + int remainingUsers = 8 - i; - int displayedUsers = remainingUsers > 3 ? 2 : remainingUsers; + int displayedUsers = remainingUsers > 4 ? 3 : remainingUsers; AddAssert($"{displayedUsers} avatars displayed", () => list.ChildrenOfType().Count() == displayedUsers); } } @@ -97,11 +97,11 @@ namespace osu.Game.Tests.Visual.Multiplayer }); AddStep("set 3 circles", () => list.NumberOfCircles = 3); - AddAssert("2 users displayed", () => list.ChildrenOfType().Count() == 2); + AddAssert("3 users displayed", () => list.ChildrenOfType().Count() == 3); AddAssert("48 hidden users", () => list.ChildrenOfType().Single().Count == 48); AddStep("set 10 circles", () => list.NumberOfCircles = 10); - AddAssert("9 users displayed", () => list.ChildrenOfType().Count() == 9); + AddAssert("10 users displayed", () => list.ChildrenOfType().Count() == 10); AddAssert("41 hidden users", () => list.ChildrenOfType().Single().Count == 41); } @@ -115,24 +115,24 @@ namespace osu.Game.Tests.Visual.Multiplayer }); AddStep("remove from start", () => removeUserAt(0)); - AddAssert("3 circles displayed", () => list.ChildrenOfType().Count() == 3); + AddAssert("4 circles displayed", () => list.ChildrenOfType().Count() == 4); AddAssert("46 hidden users", () => list.ChildrenOfType().Single().Count == 46); AddStep("remove from end", () => removeUserAt(SelectedRoom.Value.RecentParticipants.Count - 1)); - AddAssert("3 circles displayed", () => list.ChildrenOfType().Count() == 3); + AddAssert("4 circles displayed", () => list.ChildrenOfType().Count() == 4); AddAssert("45 hidden users", () => list.ChildrenOfType().Single().Count == 45); AddRepeatStep("remove 45 users", () => removeUserAt(0), 45); - AddAssert("3 circles displayed", () => list.ChildrenOfType().Count() == 3); + AddAssert("4 circles displayed", () => list.ChildrenOfType().Count() == 4); AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0); AddAssert("hidden users bubble hidden", () => list.ChildrenOfType().Single().Alpha < 0.5f); AddStep("remove another user", () => removeUserAt(0)); - AddAssert("2 circles displayed", () => list.ChildrenOfType().Count() == 2); + AddAssert("3 circles displayed", () => list.ChildrenOfType().Count() == 3); AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0); AddRepeatStep("remove the remaining two users", () => removeUserAt(0), 2); - AddAssert("0 circles displayed", () => !list.ChildrenOfType().Any()); + AddAssert("1 circle displayed", () => list.ChildrenOfType().Count() == 1); } private void addUser(int id) From ea30445efc41a508c4fe865956ccabee13b7d9b3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Sep 2021 12:03:34 +0900 Subject: [PATCH 2136/2442] Remove verbatim string --- .../Lounge/Components/DrawableRoomParticipantsList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs index db1686ef4a..e9be805d44 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs @@ -261,7 +261,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components hostAvatar.User = host.NewValue; hostText.Clear(); - hostText.AddText(@"hosted by "); + hostText.AddText("hosted by "); if (host.NewValue != null) hostText.AddUserLink(host.NewValue); } From 202a602d2f8f249aa0bfc33ff79cc4f1e91d4226 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Sep 2021 12:03:44 +0900 Subject: [PATCH 2137/2442] Change default status to "ready to play" --- osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index f2af38c2df..03d13c353a 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -372,7 +372,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components if (Type.Value == MatchType.Playlists) { - statusText.Text = "Waiting for players"; + statusText.Text = "Ready to play"; return; } From 816018edb71b81dcee304f0e850216cd4abd5486 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Sep 2021 12:04:30 +0900 Subject: [PATCH 2138/2442] Move hosted by text into nullcheck --- .../Lounge/Components/DrawableRoomParticipantsList.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs index e9be805d44..31eb5db9bc 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs @@ -261,9 +261,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components hostAvatar.User = host.NewValue; hostText.Clear(); - hostText.AddText("hosted by "); if (host.NewValue != null) + { + hostText.AddText("hosted by "); hostText.AddUserLink(host.NewValue); + } } private class CircularAvatar : CompositeDrawable From 6ffd9fdcfa20476f80a45fbbd60602782323148d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Sep 2021 14:46:01 +0900 Subject: [PATCH 2139/2442] Split out `BeatmapOnlineLookupQueue` from `BeatmapManager` --- ...eneOnlinePlayBeatmapAvailabilityTracker.cs | 4 +- osu.Game/Beatmaps/BeatmapManager.cs | 25 +- ...BeatmapManager_BeatmapOnlineLookupQueue.cs | 215 ------------------ osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs | 215 ++++++++++++++++++ osu.Game/OsuGameBase.cs | 9 +- 5 files changed, 234 insertions(+), 234 deletions(-) delete mode 100644 osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs create mode 100644 osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs diff --git a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs index 7e7e5ebc45..a7d34fadbe 100644 --- a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs @@ -161,8 +161,8 @@ namespace osu.Game.Tests.Online protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) => new TestDownloadRequest(set); - public TestBeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore resources, GameHost host = null, WorkingBeatmap defaultBeatmap = null, bool performOnlineLookups = false) - : base(storage, contextFactory, rulesets, api, audioManager, resources, host, defaultBeatmap, performOnlineLookups) + public TestBeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore resources, GameHost host = null, WorkingBeatmap defaultBeatmap = null) + : base(storage, contextFactory, rulesets, api, audioManager, resources, host, defaultBeatmap) { } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index bd85017d58..a2f9740779 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -40,7 +40,7 @@ namespace osu.Game.Beatmaps /// Handles the storage and retrieval of Beatmaps/WorkingBeatmaps. /// [ExcludeFromDynamicCompile] - public partial class BeatmapManager : DownloadableArchiveModelManager, IDisposable, IBeatmapResourceProvider + public partial class BeatmapManager : DownloadableArchiveModelManager, IBeatmapResourceProvider { /// /// Fired when a single difficulty has been hidden. @@ -54,6 +54,12 @@ namespace osu.Game.Beatmaps /// public IBindable> BeatmapRestored => beatmapRestored; + /// + /// A function which populates online information during the import process. + /// It is run as the final step of import. + /// + public Func PopulateOnlineInformation; + private readonly Bindable> beatmapRestored = new Bindable>(); /// @@ -79,11 +85,8 @@ namespace osu.Game.Beatmaps [CanBeNull] private readonly GameHost host; - [CanBeNull] - private readonly BeatmapOnlineLookupQueue onlineLookupQueue; - public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore resources, GameHost host = null, - WorkingBeatmap defaultBeatmap = null, bool performOnlineLookups = false) + WorkingBeatmap defaultBeatmap = null) : base(storage, contextFactory, api, new BeatmapStore(contextFactory), host) { this.rulesets = rulesets; @@ -99,9 +102,6 @@ namespace osu.Game.Beatmaps beatmaps.ItemRemoved += removeWorkingCache; beatmaps.ItemUpdated += removeWorkingCache; - if (performOnlineLookups) - onlineLookupQueue = new BeatmapOnlineLookupQueue(api, storage); - largeTextureStore = new LargeTextureStore(host?.CreateTextureLoaderStore(Files.Store)); trackStore = audioManager.GetTrackStore(Files.Store); } @@ -156,8 +156,8 @@ namespace osu.Game.Beatmaps bool hadOnlineBeatmapIDs = beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0); - if (onlineLookupQueue != null) - await onlineLookupQueue.UpdateAsync(beatmapSet, cancellationToken).ConfigureAwait(false); + if (PopulateOnlineInformation != null) + await PopulateOnlineInformation(beatmapSet, cancellationToken).ConfigureAwait(false); // ensure at least one beatmap was able to retrieve or keep an online ID, else drop the set ID. if (hadOnlineBeatmapIDs && !beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0)) @@ -533,11 +533,6 @@ namespace osu.Game.Beatmaps } } - public void Dispose() - { - onlineLookupQueue?.Dispose(); - } - #region IResourceStorageProvider TextureStore IBeatmapResourceProvider.LargeTextureStore => largeTextureStore; diff --git a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs deleted file mode 100644 index 3dd34f6c2f..0000000000 --- a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs +++ /dev/null @@ -1,215 +0,0 @@ -// 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.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Data.Sqlite; -using osu.Framework.Development; -using osu.Framework.IO.Network; -using osu.Framework.Logging; -using osu.Framework.Platform; -using osu.Framework.Testing; -using osu.Framework.Threading; -using osu.Game.Database; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; -using SharpCompress.Compressors; -using SharpCompress.Compressors.BZip2; - -namespace osu.Game.Beatmaps -{ - public partial class BeatmapManager - { - [ExcludeFromDynamicCompile] - private class BeatmapOnlineLookupQueue : IDisposable - { - private readonly IAPIProvider api; - private readonly Storage storage; - - private const int update_queue_request_concurrency = 4; - - private readonly ThreadedTaskScheduler updateScheduler = new ThreadedTaskScheduler(update_queue_request_concurrency, nameof(BeatmapOnlineLookupQueue)); - - private FileWebRequest cacheDownloadRequest; - - private const string cache_database_name = "online.db"; - - public BeatmapOnlineLookupQueue(IAPIProvider api, Storage storage) - { - this.api = api; - this.storage = storage; - - // avoid downloading / using cache for unit tests. - if (!DebugUtils.IsNUnitRunning && !storage.Exists(cache_database_name)) - prepareLocalCache(); - } - - public Task UpdateAsync(BeatmapSetInfo beatmapSet, CancellationToken cancellationToken) - { - return Task.WhenAll(beatmapSet.Beatmaps.Select(b => UpdateAsync(beatmapSet, b, cancellationToken)).ToArray()); - } - - // todo: expose this when we need to do individual difficulty lookups. - protected Task UpdateAsync(BeatmapSetInfo beatmapSet, BeatmapInfo beatmap, CancellationToken cancellationToken) - => Task.Factory.StartNew(() => lookup(beatmapSet, beatmap), cancellationToken, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler); - - private void lookup(BeatmapSetInfo set, BeatmapInfo beatmap) - { - if (checkLocalCache(set, beatmap)) - return; - - if (api?.State.Value != APIState.Online) - return; - - var req = new GetBeatmapRequest(beatmap); - - req.Failure += fail; - - try - { - // intentionally blocking to limit web request concurrency - api.Perform(req); - - var res = req.Result; - - if (res != null) - { - beatmap.Status = res.Status; - beatmap.BeatmapSet.Status = res.BeatmapSet.Status; - beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID; - beatmap.OnlineBeatmapID = res.OnlineBeatmapID; - - if (beatmap.Metadata != null) - beatmap.Metadata.AuthorID = res.AuthorID; - - if (beatmap.BeatmapSet.Metadata != null) - beatmap.BeatmapSet.Metadata.AuthorID = res.AuthorID; - - LogForModel(set, $"Online retrieval mapped {beatmap} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}."); - } - } - catch (Exception e) - { - fail(e); - } - - void fail(Exception e) - { - beatmap.OnlineBeatmapID = null; - LogForModel(set, $"Online retrieval failed for {beatmap} ({e.Message})"); - } - } - - private void prepareLocalCache() - { - string cacheFilePath = storage.GetFullPath(cache_database_name); - string compressedCacheFilePath = $"{cacheFilePath}.bz2"; - - cacheDownloadRequest = new FileWebRequest(compressedCacheFilePath, $"https://assets.ppy.sh/client-resources/{cache_database_name}.bz2?{DateTimeOffset.UtcNow:yyyyMMdd}"); - - cacheDownloadRequest.Failed += ex => - { - File.Delete(compressedCacheFilePath); - File.Delete(cacheFilePath); - - Logger.Log($"{nameof(BeatmapOnlineLookupQueue)}'s online cache download failed: {ex}", LoggingTarget.Database); - }; - - cacheDownloadRequest.Finished += () => - { - try - { - using (var stream = File.OpenRead(cacheDownloadRequest.Filename)) - using (var outStream = File.OpenWrite(cacheFilePath)) - using (var bz2 = new BZip2Stream(stream, CompressionMode.Decompress, false)) - bz2.CopyTo(outStream); - - // set to null on completion to allow lookups to begin using the new source - cacheDownloadRequest = null; - } - catch (Exception ex) - { - Logger.Log($"{nameof(BeatmapOnlineLookupQueue)}'s online cache extraction failed: {ex}", LoggingTarget.Database); - File.Delete(cacheFilePath); - } - finally - { - File.Delete(compressedCacheFilePath); - } - }; - - cacheDownloadRequest.PerformAsync(); - } - - private bool checkLocalCache(BeatmapSetInfo set, BeatmapInfo beatmap) - { - // download is in progress (or was, and failed). - if (cacheDownloadRequest != null) - return false; - - // database is unavailable. - if (!storage.Exists(cache_database_name)) - return false; - - if (string.IsNullOrEmpty(beatmap.MD5Hash) - && string.IsNullOrEmpty(beatmap.Path) - && beatmap.OnlineBeatmapID == null) - return false; - - try - { - using (var db = new SqliteConnection(DatabaseContextFactory.CreateDatabaseConnectionString("online.db", storage))) - { - db.Open(); - - using (var cmd = db.CreateCommand()) - { - cmd.CommandText = "SELECT beatmapset_id, beatmap_id, approved, user_id FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path"; - - cmd.Parameters.Add(new SqliteParameter("@MD5Hash", beatmap.MD5Hash)); - cmd.Parameters.Add(new SqliteParameter("@OnlineBeatmapID", beatmap.OnlineBeatmapID ?? (object)DBNull.Value)); - cmd.Parameters.Add(new SqliteParameter("@Path", beatmap.Path)); - - using (var reader = cmd.ExecuteReader()) - { - if (reader.Read()) - { - var status = (BeatmapSetOnlineStatus)reader.GetByte(2); - - beatmap.Status = status; - beatmap.BeatmapSet.Status = status; - beatmap.BeatmapSet.OnlineBeatmapSetID = reader.GetInt32(0); - beatmap.OnlineBeatmapID = reader.GetInt32(1); - - if (beatmap.Metadata != null) - beatmap.Metadata.AuthorID = reader.GetInt32(3); - - if (beatmap.BeatmapSet.Metadata != null) - beatmap.BeatmapSet.Metadata.AuthorID = reader.GetInt32(3); - - LogForModel(set, $"Cached local retrieval for {beatmap}."); - return true; - } - } - } - } - } - catch (Exception ex) - { - LogForModel(set, $"Cached local retrieval for {beatmap} failed with {ex}."); - } - - return false; - } - - public void Dispose() - { - cacheDownloadRequest?.Dispose(); - updateScheduler?.Dispose(); - } - } - } -} diff --git a/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs b/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs new file mode 100644 index 0000000000..bbac30f2bb --- /dev/null +++ b/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs @@ -0,0 +1,215 @@ +// 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.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Data.Sqlite; +using osu.Framework.Development; +using osu.Framework.IO.Network; +using osu.Framework.Logging; +using osu.Framework.Platform; +using osu.Framework.Testing; +using osu.Framework.Threading; +using osu.Game.Database; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using SharpCompress.Compressors; +using SharpCompress.Compressors.BZip2; + +namespace osu.Game.Beatmaps +{ + [ExcludeFromDynamicCompile] + public class BeatmapOnlineLookupQueue : IDisposable + { + private readonly IAPIProvider api; + private readonly Storage storage; + + private const int update_queue_request_concurrency = 4; + + private readonly ThreadedTaskScheduler updateScheduler = new ThreadedTaskScheduler(update_queue_request_concurrency, nameof(BeatmapOnlineLookupQueue)); + + private FileWebRequest cacheDownloadRequest; + + private const string cache_database_name = "online.db"; + + public BeatmapOnlineLookupQueue(IAPIProvider api, Storage storage) + { + this.api = api; + this.storage = storage; + + // avoid downloading / using cache for unit tests. + if (!DebugUtils.IsNUnitRunning && !storage.Exists(cache_database_name)) + prepareLocalCache(); + } + + public Task UpdateAsync(BeatmapSetInfo beatmapSet, CancellationToken cancellationToken) + { + return Task.WhenAll(beatmapSet.Beatmaps.Select(b => UpdateAsync(beatmapSet, b, cancellationToken)).ToArray()); + } + + // todo: expose this when we need to do individual difficulty lookups. + protected Task UpdateAsync(BeatmapSetInfo beatmapSet, BeatmapInfo beatmap, CancellationToken cancellationToken) + => Task.Factory.StartNew(() => lookup(beatmapSet, beatmap), cancellationToken, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler); + + private void lookup(BeatmapSetInfo set, BeatmapInfo beatmap) + { + if (checkLocalCache(set, beatmap)) + return; + + if (api?.State.Value != APIState.Online) + return; + + var req = new GetBeatmapRequest(beatmap); + + req.Failure += fail; + + try + { + // intentionally blocking to limit web request concurrency + api.Perform(req); + + var res = req.Result; + + if (res != null) + { + beatmap.Status = res.Status; + beatmap.BeatmapSet.Status = res.BeatmapSet.Status; + beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID; + beatmap.OnlineBeatmapID = res.OnlineBeatmapID; + + if (beatmap.Metadata != null) + beatmap.Metadata.AuthorID = res.AuthorID; + + if (beatmap.BeatmapSet.Metadata != null) + beatmap.BeatmapSet.Metadata.AuthorID = res.AuthorID; + + logForModel(set, $"Online retrieval mapped {beatmap} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}."); + } + } + catch (Exception e) + { + fail(e); + } + + void fail(Exception e) + { + beatmap.OnlineBeatmapID = null; + logForModel(set, $"Online retrieval failed for {beatmap} ({e.Message})"); + } + } + + private void prepareLocalCache() + { + string cacheFilePath = storage.GetFullPath(cache_database_name); + string compressedCacheFilePath = $"{cacheFilePath}.bz2"; + + cacheDownloadRequest = new FileWebRequest(compressedCacheFilePath, $"https://assets.ppy.sh/client-resources/{cache_database_name}.bz2?{DateTimeOffset.UtcNow:yyyyMMdd}"); + + cacheDownloadRequest.Failed += ex => + { + File.Delete(compressedCacheFilePath); + File.Delete(cacheFilePath); + + Logger.Log($"{nameof(BeatmapOnlineLookupQueue)}'s online cache download failed: {ex}", LoggingTarget.Database); + }; + + cacheDownloadRequest.Finished += () => + { + try + { + using (var stream = File.OpenRead(cacheDownloadRequest.Filename)) + using (var outStream = File.OpenWrite(cacheFilePath)) + using (var bz2 = new BZip2Stream(stream, CompressionMode.Decompress, false)) + bz2.CopyTo(outStream); + + // set to null on completion to allow lookups to begin using the new source + cacheDownloadRequest = null; + } + catch (Exception ex) + { + Logger.Log($"{nameof(BeatmapOnlineLookupQueue)}'s online cache extraction failed: {ex}", LoggingTarget.Database); + File.Delete(cacheFilePath); + } + finally + { + File.Delete(compressedCacheFilePath); + } + }; + + cacheDownloadRequest.PerformAsync(); + } + + private bool checkLocalCache(BeatmapSetInfo set, BeatmapInfo beatmap) + { + // download is in progress (or was, and failed). + if (cacheDownloadRequest != null) + return false; + + // database is unavailable. + if (!storage.Exists(cache_database_name)) + return false; + + if (string.IsNullOrEmpty(beatmap.MD5Hash) + && string.IsNullOrEmpty(beatmap.Path) + && beatmap.OnlineBeatmapID == null) + return false; + + try + { + using (var db = new SqliteConnection(DatabaseContextFactory.CreateDatabaseConnectionString("online.db", storage))) + { + db.Open(); + + using (var cmd = db.CreateCommand()) + { + cmd.CommandText = "SELECT beatmapset_id, beatmap_id, approved, user_id FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path"; + + cmd.Parameters.Add(new SqliteParameter("@MD5Hash", beatmap.MD5Hash)); + cmd.Parameters.Add(new SqliteParameter("@OnlineBeatmapID", beatmap.OnlineBeatmapID ?? (object)DBNull.Value)); + cmd.Parameters.Add(new SqliteParameter("@Path", beatmap.Path)); + + using (var reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + var status = (BeatmapSetOnlineStatus)reader.GetByte(2); + + beatmap.Status = status; + beatmap.BeatmapSet.Status = status; + beatmap.BeatmapSet.OnlineBeatmapSetID = reader.GetInt32(0); + beatmap.OnlineBeatmapID = reader.GetInt32(1); + + if (beatmap.Metadata != null) + beatmap.Metadata.AuthorID = reader.GetInt32(3); + + if (beatmap.BeatmapSet.Metadata != null) + beatmap.BeatmapSet.Metadata.AuthorID = reader.GetInt32(3); + + logForModel(set, $"Cached local retrieval for {beatmap}."); + return true; + } + } + } + } + } + catch (Exception ex) + { + logForModel(set, $"Cached local retrieval for {beatmap} failed with {ex}."); + } + + return false; + } + + private void logForModel(BeatmapSetInfo set, string message) => + ArchiveModelManager.LogForModel(set, $"{nameof(BeatmapOnlineLookupQueue)}] {message}"); + + public void Dispose() + { + cacheDownloadRequest?.Dispose(); + updateScheduler?.Dispose(); + } + } +} diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 7aa460981a..8263e26dec 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -138,6 +138,8 @@ namespace osu.Game private UserLookupCache userCache; + private BeatmapOnlineLookupQueue onlineBeatmapLookupCache; + private FileStore fileStore; private RulesetConfigCache rulesetConfigCache; @@ -242,7 +244,11 @@ namespace osu.Game // ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup() dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Scheduler, Host, () => difficultyCache, LocalConfig)); - dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Resources, Host, defaultBeatmap, true)); + dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Resources, Host, defaultBeatmap)); + + onlineBeatmapLookupCache = new BeatmapOnlineLookupQueue(API, Storage); + + BeatmapManager.PopulateOnlineInformation = onlineBeatmapLookupCache.UpdateAsync; // this should likely be moved to ArchiveModelManager when another case appears where it is necessary // to have inter-dependent model managers. this could be obtained with an IHasForeign interface to @@ -524,7 +530,6 @@ namespace osu.Game base.Dispose(isDisposing); RulesetStore?.Dispose(); - BeatmapManager?.Dispose(); LocalConfig?.Dispose(); contextFactory?.FlushConnections(); From 8a6501fa58f67f49aa71df20dbb76a5caba92516 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Sep 2021 14:46:07 +0900 Subject: [PATCH 2140/2442] Add basic component level xmldoc --- osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs b/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs index bbac30f2bb..19f02c82ec 100644 --- a/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs +++ b/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs @@ -21,6 +21,13 @@ using SharpCompress.Compressors.BZip2; namespace osu.Game.Beatmaps { + /// + /// A component which handles population of online IDs for beatmaps using a two part lookup procedure. + /// + /// + /// On creating the component, a copy of a database containing metadata for a large subset of beatmaps (stored to ). + /// This will always be checked before doing a second online query to get required metadata. + /// [ExcludeFromDynamicCompile] public class BeatmapOnlineLookupQueue : IDisposable { From 968826c9e7b0b65015c9d26c250695ab2887c973 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 30 Sep 2021 15:17:39 +0900 Subject: [PATCH 2141/2442] Fix typo --- osu.Game/Audio/Effects/Filter.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Audio/Effects/Filter.cs b/osu.Game/Audio/Effects/Filter.cs index 37a2fa93db..04ad862879 100644 --- a/osu.Game/Audio/Effects/Filter.cs +++ b/osu.Game/Audio/Effects/Filter.cs @@ -39,7 +39,7 @@ namespace osu.Game.Audio.Effects public void Disable() { - this.TransformBindableTo(filterFreq, SweepCutoffStart, SweepDuration, SweepEasing).OnComplete(_ => detatchFilter()); + this.TransformBindableTo(filterFreq, SweepCutoffStart, SweepDuration, SweepEasing).OnComplete(_ => detachFilter()); } private void attachFilter() @@ -57,7 +57,7 @@ namespace osu.Game.Audio.Effects IsActive = true; } - private void detatchFilter() + private void detachFilter() { if (!IsActive) return; @@ -81,7 +81,7 @@ namespace osu.Game.Audio.Effects protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - detatchFilter(); + detachFilter(); } } } From 1304b55c4182db06c22b5830cb449584c3e18dde Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 30 Sep 2021 15:19:09 +0900 Subject: [PATCH 2142/2442] Move visual test to correct namespace --- osu.Game.Tests/{ => Visual}/Audio/TestSceneFilter.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) rename osu.Game.Tests/{ => Visual}/Audio/TestSceneFilter.cs (97%) diff --git a/osu.Game.Tests/Audio/TestSceneFilter.cs b/osu.Game.Tests/Visual/Audio/TestSceneFilter.cs similarity index 97% rename from osu.Game.Tests/Audio/TestSceneFilter.cs rename to osu.Game.Tests/Visual/Audio/TestSceneFilter.cs index fd4fe530eb..6a7a404d8a 100644 --- a/osu.Game.Tests/Audio/TestSceneFilter.cs +++ b/osu.Game.Tests/Visual/Audio/TestSceneFilter.cs @@ -8,9 +8,8 @@ using osu.Framework.Audio; using osu.Framework.Graphics; using osu.Game.Audio.Effects; using osu.Game.Beatmaps; -using osu.Game.Tests.Visual; -namespace osu.Game.Tests.Audio +namespace osu.Game.Tests.Visual.Audio { public class TestSceneFilter : OsuTestScene { From e7e04733234cf2de40abf9241548c786f64d8ed6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Sep 2021 15:40:41 +0900 Subject: [PATCH 2143/2442] Split out `WorkingBeatmapCache` from `BeatmapManager` --- osu.Game/Beatmaps/BeatmapManager.cs | 134 +-------- .../Beatmaps/BeatmapManager_WorkingBeatmap.cs | 147 --------- osu.Game/Beatmaps/WorkingBeatmapCache.cs | 279 ++++++++++++++++++ 3 files changed, 289 insertions(+), 271 deletions(-) delete mode 100644 osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs create mode 100644 osu.Game/Beatmaps/WorkingBeatmapCache.cs diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index a2f9740779..1c0e7dc319 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -9,18 +9,13 @@ using System.Linq.Expressions; using System.Text; using System.Threading; using System.Threading.Tasks; -using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; -using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics.Textures; -using osu.Framework.IO.Stores; -using osu.Framework.Lists; using osu.Framework.Logging; using osu.Framework.Platform; -using osu.Framework.Statistics; using osu.Framework.Testing; using osu.Game.Beatmaps.Formats; using osu.Game.Database; @@ -31,7 +26,6 @@ using osu.Game.Online.API.Requests; using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; using osu.Game.Skinning; -using osu.Game.Users; using Decoder = osu.Game.Beatmaps.Formats.Decoder; namespace osu.Game.Beatmaps @@ -40,7 +34,7 @@ namespace osu.Game.Beatmaps /// Handles the storage and retrieval of Beatmaps/WorkingBeatmaps. /// [ExcludeFromDynamicCompile] - public partial class BeatmapManager : DownloadableArchiveModelManager, IBeatmapResourceProvider + public class BeatmapManager : DownloadableArchiveModelManager { /// /// Fired when a single difficulty has been hidden. @@ -60,12 +54,12 @@ namespace osu.Game.Beatmaps /// public Func PopulateOnlineInformation; - private readonly Bindable> beatmapRestored = new Bindable>(); - /// - /// A default representation of a WorkingBeatmap to use when no beatmap is available. + /// The game working beatmap cache, used to invalidate entries on changes. /// - public readonly WorkingBeatmap DefaultBeatmap; + public WorkingBeatmapCache WorkingBeatmapCache { private get; set; } + + private readonly Bindable> beatmapRestored = new Bindable>(); public override IEnumerable HandledExtensions => new[] { ".osz" }; @@ -75,35 +69,19 @@ namespace osu.Game.Beatmaps protected override Storage PrepareStableStorage(StableStorage stableStorage) => stableStorage.GetSongStorage(); - private readonly RulesetStore rulesets; private readonly BeatmapStore beatmaps; - private readonly AudioManager audioManager; - private readonly IResourceStore resources; - private readonly LargeTextureStore largeTextureStore; - private readonly ITrackStore trackStore; + private readonly RulesetStore rulesets; - [CanBeNull] - private readonly GameHost host; - - public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore resources, GameHost host = null, - WorkingBeatmap defaultBeatmap = null) + public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, GameHost host = null) : base(storage, contextFactory, api, new BeatmapStore(contextFactory), host) { this.rulesets = rulesets; - this.audioManager = audioManager; - this.resources = resources; - this.host = host; - - DefaultBeatmap = defaultBeatmap; beatmaps = (BeatmapStore)ModelStore; beatmaps.BeatmapHidden += b => beatmapHidden.Value = new WeakReference(b); beatmaps.BeatmapRestored += b => beatmapRestored.Value = new WeakReference(b); - beatmaps.ItemRemoved += removeWorkingCache; - beatmaps.ItemUpdated += removeWorkingCache; - - largeTextureStore = new LargeTextureStore(host?.CreateTextureLoaderStore(Files.Store)); - trackStore = audioManager.GetTrackStore(Files.Store); + beatmaps.ItemRemoved += b => WorkingBeatmapCache?.Invalidate(b); + beatmaps.ItemUpdated += obj => WorkingBeatmapCache?.Invalidate(obj); } protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) => @@ -111,33 +89,6 @@ namespace osu.Game.Beatmaps protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == ".osz"; - public WorkingBeatmap CreateNew(RulesetInfo ruleset, User user) - { - var metadata = new BeatmapMetadata - { - Author = user, - }; - - var set = new BeatmapSetInfo - { - Metadata = metadata, - Beatmaps = new List - { - new BeatmapInfo - { - BaseDifficulty = new BeatmapDifficulty(), - Ruleset = ruleset, - Metadata = metadata, - WidescreenStoryboard = true, - SamplesMatchPlaybackRate = true, - } - } - }; - - var working = Import(set).Result; - return GetWorkingBeatmap(working.Beatmaps.First()); - } - protected override async Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive, CancellationToken cancellationToken = default) { if (archive != null) @@ -278,43 +229,7 @@ namespace osu.Game.Beatmaps } } - removeWorkingCache(info); - } - - private readonly WeakList workingCache = new WeakList(); - - /// - /// Retrieve a instance for the provided - /// - /// The beatmap to lookup. - /// A instance correlating to the provided . - public virtual WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo) - { - // if there are no files, presume the full beatmap info has not yet been fetched from the database. - if (beatmapInfo?.BeatmapSet?.Files.Count == 0) - { - int lookupId = beatmapInfo.ID; - beatmapInfo = QueryBeatmap(b => b.ID == lookupId); - } - - if (beatmapInfo?.BeatmapSet == null) - return DefaultBeatmap; - - lock (workingCache) - { - var working = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == beatmapInfo.ID); - if (working != null) - return working; - - beatmapInfo.Metadata ??= beatmapInfo.BeatmapSet.Metadata; - - workingCache.Add(working = new BeatmapManagerWorkingBeatmap(beatmapInfo, this)); - - // best effort; may be higher than expected. - GlobalStatistics.Get(nameof(Beatmaps), $"Cached {nameof(WorkingBeatmap)}s").Value = workingCache.Count(); - - return working; - } + WorkingBeatmapCache?.Invalidate(info); } /// @@ -515,35 +430,6 @@ namespace osu.Game.Beatmaps return endTime - startTime; } - private void removeWorkingCache(BeatmapSetInfo info) - { - if (info.Beatmaps == null) return; - - foreach (var b in info.Beatmaps) - removeWorkingCache(b); - } - - private void removeWorkingCache(BeatmapInfo info) - { - lock (workingCache) - { - var working = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == info.ID); - if (working != null) - workingCache.Remove(working); - } - } - - #region IResourceStorageProvider - - TextureStore IBeatmapResourceProvider.LargeTextureStore => largeTextureStore; - ITrackStore IBeatmapResourceProvider.Tracks => trackStore; - AudioManager IStorageResourceProvider.AudioManager => audioManager; - IResourceStore IStorageResourceProvider.Files => Files.Store; - IResourceStore IStorageResourceProvider.Resources => resources; - IResourceStore IStorageResourceProvider.CreateTextureLoaderStore(IResourceStore underlyingStore) => host?.CreateTextureLoaderStore(underlyingStore); - - #endregion - /// /// A dummy WorkingBeatmap for the purpose of retrieving a beatmap for star difficulty calculation. /// diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs deleted file mode 100644 index 45112ae74c..0000000000 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ /dev/null @@ -1,147 +0,0 @@ -// 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.Diagnostics.CodeAnalysis; -using System.IO; -using osu.Framework.Audio.Track; -using osu.Framework.Graphics.Textures; -using osu.Framework.Logging; -using osu.Framework.Testing; -using osu.Game.Beatmaps.Formats; -using osu.Game.IO; -using osu.Game.Skinning; -using osu.Game.Storyboards; - -namespace osu.Game.Beatmaps -{ - public partial class BeatmapManager - { - [ExcludeFromDynamicCompile] - private class BeatmapManagerWorkingBeatmap : WorkingBeatmap - { - [NotNull] - private readonly IBeatmapResourceProvider resources; - - public BeatmapManagerWorkingBeatmap(BeatmapInfo beatmapInfo, [NotNull] IBeatmapResourceProvider resources) - : base(beatmapInfo, resources.AudioManager) - { - this.resources = resources; - } - - protected override IBeatmap GetBeatmap() - { - if (BeatmapInfo.Path == null) - return new Beatmap { BeatmapInfo = BeatmapInfo }; - - try - { - using (var stream = new LineBufferedReader(GetStream(BeatmapSetInfo.GetPathForFile(BeatmapInfo.Path)))) - return Decoder.GetDecoder(stream).Decode(stream); - } - catch (Exception e) - { - Logger.Error(e, "Beatmap failed to load"); - return null; - } - } - - 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) - return null; - - try - { - return resources.LargeTextureStore.Get(BeatmapSetInfo.GetPathForFile(Metadata.BackgroundFile)); - } - catch (Exception e) - { - Logger.Error(e, "Background failed to load"); - return null; - } - } - - protected override Track GetBeatmapTrack() - { - if (Metadata?.AudioFile == null) - return null; - - try - { - return resources.Tracks.Get(BeatmapSetInfo.GetPathForFile(Metadata.AudioFile)); - } - catch (Exception e) - { - Logger.Error(e, "Track failed to load"); - return null; - } - } - - protected override Waveform GetWaveform() - { - if (Metadata?.AudioFile == null) - return null; - - try - { - var trackData = GetStream(BeatmapSetInfo.GetPathForFile(Metadata.AudioFile)); - return trackData == null ? null : new Waveform(trackData); - } - catch (Exception e) - { - Logger.Error(e, "Waveform failed to load"); - return null; - } - } - - protected override Storyboard GetStoryboard() - { - Storyboard storyboard; - - try - { - using (var stream = new LineBufferedReader(GetStream(BeatmapSetInfo.GetPathForFile(BeatmapInfo.Path)))) - { - var decoder = Decoder.GetDecoder(stream); - - // todo: support loading from both set-wide storyboard *and* beatmap specific. - if (BeatmapSetInfo?.StoryboardFile == null) - storyboard = decoder.Decode(stream); - else - { - using (var secondaryStream = new LineBufferedReader(GetStream(BeatmapSetInfo.GetPathForFile(BeatmapSetInfo.StoryboardFile)))) - storyboard = decoder.Decode(stream, secondaryStream); - } - } - } - catch (Exception e) - { - Logger.Error(e, "Storyboard failed to load"); - storyboard = new Storyboard(); - } - - storyboard.BeatmapInfo = BeatmapInfo; - - return storyboard; - } - - protected internal override ISkin GetSkin() - { - try - { - return new LegacyBeatmapSkin(BeatmapInfo, resources.Files, resources); - } - catch (Exception e) - { - Logger.Error(e, "Skin failed to load"); - return null; - } - } - - public override Stream GetStream(string storagePath) => resources.Files.GetStream(storagePath); - } - } -} diff --git a/osu.Game/Beatmaps/WorkingBeatmapCache.cs b/osu.Game/Beatmaps/WorkingBeatmapCache.cs new file mode 100644 index 0000000000..9f40eb4898 --- /dev/null +++ b/osu.Game/Beatmaps/WorkingBeatmapCache.cs @@ -0,0 +1,279 @@ +// 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.IO; +using System.Linq; +using JetBrains.Annotations; +using osu.Framework.Audio; +using osu.Framework.Audio.Track; +using osu.Framework.Graphics.Textures; +using osu.Framework.IO.Stores; +using osu.Framework.Lists; +using osu.Framework.Logging; +using osu.Framework.Platform; +using osu.Framework.Statistics; +using osu.Framework.Testing; +using osu.Game.Beatmaps.Formats; +using osu.Game.IO; +using osu.Game.Rulesets; +using osu.Game.Skinning; +using osu.Game.Storyboards; +using osu.Game.Users; + +namespace osu.Game.Beatmaps +{ + public class WorkingBeatmapCache : IBeatmapResourceProvider + { + private readonly WeakList workingCache = new WeakList(); + + /// + /// A default representation of a WorkingBeatmap to use when no beatmap is available. + /// + public readonly WorkingBeatmap DefaultBeatmap; + + public BeatmapManager BeatmapManager { private get; set; } + + private readonly AudioManager audioManager; + private readonly IResourceStore resources; + private readonly LargeTextureStore largeTextureStore; + private readonly ITrackStore trackStore; + private readonly IResourceStore files; + + [CanBeNull] + private readonly GameHost host; + + public WorkingBeatmapCache([NotNull] AudioManager audioManager, IResourceStore resources, IResourceStore files, WorkingBeatmap defaultBeatmap = null, GameHost host = null) + { + DefaultBeatmap = defaultBeatmap; + + this.audioManager = audioManager; + this.resources = resources; + this.host = host; + this.files = files; + largeTextureStore = new LargeTextureStore(host?.CreateTextureLoaderStore(files)); + trackStore = audioManager.GetTrackStore(files); + } + + public void Invalidate(BeatmapSetInfo info) + { + if (info.Beatmaps == null) return; + + foreach (var b in info.Beatmaps) + Invalidate(b); + } + + public void Invalidate(BeatmapInfo info) + { + lock (workingCache) + { + var working = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == info.ID); + if (working != null) + workingCache.Remove(working); + } + } + + /// + /// Create a new . + /// + public WorkingBeatmap CreateNew(RulesetInfo ruleset, User user) + { + var metadata = new BeatmapMetadata + { + Author = user, + }; + + var set = new BeatmapSetInfo + { + Metadata = metadata, + Beatmaps = new List + { + new BeatmapInfo + { + BaseDifficulty = new BeatmapDifficulty(), + Ruleset = ruleset, + Metadata = metadata, + WidescreenStoryboard = true, + SamplesMatchPlaybackRate = true, + } + } + }; + + var working = BeatmapManager.Import(set).Result; + return GetWorkingBeatmap(working.Beatmaps.First()); + } + + /// + /// Retrieve a instance for the provided + /// + /// The beatmap to lookup. + /// A instance correlating to the provided . + public virtual WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo) + { + // if there are no files, presume the full beatmap info has not yet been fetched from the database. + if (beatmapInfo?.BeatmapSet?.Files.Count == 0) + { + int lookupId = beatmapInfo.ID; + beatmapInfo = BeatmapManager.QueryBeatmap(b => b.ID == lookupId); + } + + if (beatmapInfo?.BeatmapSet == null) + return DefaultBeatmap; + + lock (workingCache) + { + var working = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == beatmapInfo.ID); + if (working != null) + return working; + + beatmapInfo.Metadata ??= beatmapInfo.BeatmapSet.Metadata; + + workingCache.Add(working = new BeatmapManagerWorkingBeatmap(beatmapInfo, this)); + + // best effort; may be higher than expected. + GlobalStatistics.Get(nameof(Beatmaps), $"Cached {nameof(WorkingBeatmap)}s").Value = workingCache.Count(); + + return working; + } + } + + #region IResourceStorageProvider + + TextureStore IBeatmapResourceProvider.LargeTextureStore => largeTextureStore; + ITrackStore IBeatmapResourceProvider.Tracks => trackStore; + AudioManager IStorageResourceProvider.AudioManager => audioManager; + IResourceStore IStorageResourceProvider.Files => files; + IResourceStore IStorageResourceProvider.Resources => resources; + IResourceStore IStorageResourceProvider.CreateTextureLoaderStore(IResourceStore underlyingStore) => host?.CreateTextureLoaderStore(underlyingStore); + + #endregion + + [ExcludeFromDynamicCompile] + private class BeatmapManagerWorkingBeatmap : WorkingBeatmap + { + [NotNull] + private readonly IBeatmapResourceProvider resources; + + public BeatmapManagerWorkingBeatmap(BeatmapInfo beatmapInfo, [NotNull] IBeatmapResourceProvider resources) + : base(beatmapInfo, resources.AudioManager) + { + this.resources = resources; + } + + protected override IBeatmap GetBeatmap() + { + if (BeatmapInfo.Path == null) + return new Beatmap { BeatmapInfo = BeatmapInfo }; + + try + { + using (var stream = new LineBufferedReader(GetStream(BeatmapSetInfo.GetPathForFile(BeatmapInfo.Path)))) + return Decoder.GetDecoder(stream).Decode(stream); + } + catch (Exception e) + { + Logger.Error(e, "Beatmap failed to load"); + return null; + } + } + + 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) + return null; + + try + { + return resources.LargeTextureStore.Get(BeatmapSetInfo.GetPathForFile(Metadata.BackgroundFile)); + } + catch (Exception e) + { + Logger.Error(e, "Background failed to load"); + return null; + } + } + + protected override Track GetBeatmapTrack() + { + if (Metadata?.AudioFile == null) + return null; + + try + { + return resources.Tracks.Get(BeatmapSetInfo.GetPathForFile(Metadata.AudioFile)); + } + catch (Exception e) + { + Logger.Error(e, "Track failed to load"); + return null; + } + } + + protected override Waveform GetWaveform() + { + if (Metadata?.AudioFile == null) + return null; + + try + { + var trackData = GetStream(BeatmapSetInfo.GetPathForFile(Metadata.AudioFile)); + return trackData == null ? null : new Waveform(trackData); + } + catch (Exception e) + { + Logger.Error(e, "Waveform failed to load"); + return null; + } + } + + protected override Storyboard GetStoryboard() + { + Storyboard storyboard; + + try + { + using (var stream = new LineBufferedReader(GetStream(BeatmapSetInfo.GetPathForFile(BeatmapInfo.Path)))) + { + var decoder = Decoder.GetDecoder(stream); + + // todo: support loading from both set-wide storyboard *and* beatmap specific. + if (BeatmapSetInfo?.StoryboardFile == null) + storyboard = decoder.Decode(stream); + else + { + using (var secondaryStream = new LineBufferedReader(GetStream(BeatmapSetInfo.GetPathForFile(BeatmapSetInfo.StoryboardFile)))) + storyboard = decoder.Decode(stream, secondaryStream); + } + } + } + catch (Exception e) + { + Logger.Error(e, "Storyboard failed to load"); + storyboard = new Storyboard(); + } + + storyboard.BeatmapInfo = BeatmapInfo; + + return storyboard; + } + + protected internal override ISkin GetSkin() + { + try + { + return new LegacyBeatmapSkin(BeatmapInfo, resources.Files, resources); + } + catch (Exception e) + { + Logger.Error(e, "Skin failed to load"); + return null; + } + } + + public override Stream GetStream(string storagePath) => resources.Files.GetStream(storagePath); + } + } +} From d21139b03efb77e7f5aeeea2d8236320d0e0d693 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Sep 2021 15:43:49 +0900 Subject: [PATCH 2144/2442] Split out database portion from `BeatmapManager` --- osu.Game/Beatmaps/BeatmapManager.cs | 466 +--------------------- osu.Game/Beatmaps/BeatmapModelManager.cs | 479 +++++++++++++++++++++++ 2 files changed, 483 insertions(+), 462 deletions(-) create mode 100644 osu.Game/Beatmaps/BeatmapModelManager.cs diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 1c0e7dc319..c445925a90 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -1,479 +1,21 @@ // 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.IO; -using System.Linq; -using System.Linq.Expressions; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; -using osu.Framework.Audio.Track; -using osu.Framework.Bindables; -using osu.Framework.Extensions; -using osu.Framework.Graphics.Textures; -using osu.Framework.Logging; -using osu.Framework.Platform; using osu.Framework.Testing; -using osu.Game.Beatmaps.Formats; -using osu.Game.Database; -using osu.Game.IO; -using osu.Game.IO.Archives; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Objects; -using osu.Game.Skinning; -using Decoder = osu.Game.Beatmaps.Formats.Decoder; namespace osu.Game.Beatmaps { /// - /// Handles the storage and retrieval of Beatmaps/WorkingBeatmaps. + /// Handles general operations related to global beatmap management. /// [ExcludeFromDynamicCompile] - public class BeatmapManager : DownloadableArchiveModelManager + public class BeatmapManager { - /// - /// Fired when a single difficulty has been hidden. - /// - public IBindable> BeatmapHidden => beatmapHidden; - - private readonly Bindable> beatmapHidden = new Bindable>(); - - /// - /// Fired when a single difficulty has been restored. - /// - public IBindable> BeatmapRestored => beatmapRestored; - - /// - /// A function which populates online information during the import process. - /// It is run as the final step of import. - /// - public Func PopulateOnlineInformation; - - /// - /// The game working beatmap cache, used to invalidate entries on changes. - /// - public WorkingBeatmapCache WorkingBeatmapCache { private get; set; } - - private readonly Bindable> beatmapRestored = new Bindable>(); - - public override IEnumerable HandledExtensions => new[] { ".osz" }; - - protected override string[] HashableFileTypes => new[] { ".osu" }; - - protected override string ImportFromStablePath => "."; - - protected override Storage PrepareStableStorage(StableStorage stableStorage) => stableStorage.GetSongStorage(); - - private readonly BeatmapStore beatmaps; - private readonly RulesetStore rulesets; - - public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, GameHost host = null) - : base(storage, contextFactory, api, new BeatmapStore(contextFactory), host) + public BeatmapManager() { - this.rulesets = rulesets; - - beatmaps = (BeatmapStore)ModelStore; - beatmaps.BeatmapHidden += b => beatmapHidden.Value = new WeakReference(b); - beatmaps.BeatmapRestored += b => beatmapRestored.Value = new WeakReference(b); - beatmaps.ItemRemoved += b => WorkingBeatmapCache?.Invalidate(b); - beatmaps.ItemUpdated += obj => WorkingBeatmapCache?.Invalidate(obj); + beatmapModelManager = new BeatmapModelManager() } - protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) => - new DownloadBeatmapSetRequest(set, minimiseDownloadSize); - - protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == ".osz"; - - protected override async Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive, CancellationToken cancellationToken = default) - { - if (archive != null) - beatmapSet.Beatmaps = createBeatmapDifficulties(beatmapSet.Files); - - foreach (BeatmapInfo b in beatmapSet.Beatmaps) - { - // remove metadata from difficulties where it matches the set - if (beatmapSet.Metadata.Equals(b.Metadata)) - b.Metadata = null; - - b.BeatmapSet = beatmapSet; - } - - validateOnlineIds(beatmapSet); - - bool hadOnlineBeatmapIDs = beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0); - - if (PopulateOnlineInformation != null) - await PopulateOnlineInformation(beatmapSet, cancellationToken).ConfigureAwait(false); - - // ensure at least one beatmap was able to retrieve or keep an online ID, else drop the set ID. - if (hadOnlineBeatmapIDs && !beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0)) - { - if (beatmapSet.OnlineBeatmapSetID != null) - { - beatmapSet.OnlineBeatmapSetID = null; - LogForModel(beatmapSet, "Disassociating beatmap set ID due to loss of all beatmap IDs"); - } - } - } - - protected override void PreImport(BeatmapSetInfo beatmapSet) - { - if (beatmapSet.Beatmaps.Any(b => b.BaseDifficulty == null)) - throw new InvalidOperationException($"Cannot import {nameof(BeatmapInfo)} with null {nameof(BeatmapInfo.BaseDifficulty)}."); - - // check if a set already exists with the same online id, delete if it does. - if (beatmapSet.OnlineBeatmapSetID != null) - { - var existingOnlineId = beatmaps.ConsumableItems.FirstOrDefault(b => b.OnlineBeatmapSetID == beatmapSet.OnlineBeatmapSetID); - - if (existingOnlineId != null) - { - Delete(existingOnlineId); - - // in order to avoid a unique key constraint, immediately remove the online ID from the previous set. - existingOnlineId.OnlineBeatmapSetID = null; - foreach (var b in existingOnlineId.Beatmaps) - b.OnlineBeatmapID = null; - - LogForModel(beatmapSet, $"Found existing beatmap set with same OnlineBeatmapSetID ({beatmapSet.OnlineBeatmapSetID}). It has been deleted."); - } - } - } - - private void validateOnlineIds(BeatmapSetInfo beatmapSet) - { - var beatmapIds = beatmapSet.Beatmaps.Where(b => b.OnlineBeatmapID.HasValue).Select(b => b.OnlineBeatmapID).ToList(); - - // ensure all IDs are unique - if (beatmapIds.GroupBy(b => b).Any(g => g.Count() > 1)) - { - LogForModel(beatmapSet, "Found non-unique IDs, resetting..."); - resetIds(); - return; - } - - // find any existing beatmaps in the database that have matching online ids - var existingBeatmaps = QueryBeatmaps(b => beatmapIds.Contains(b.OnlineBeatmapID)).ToList(); - - if (existingBeatmaps.Count > 0) - { - // reset the import ids (to force a re-fetch) *unless* they match the candidate CheckForExisting set. - // we can ignore the case where the new ids are contained by the CheckForExisting set as it will either be used (import skipped) or deleted. - var existing = CheckForExisting(beatmapSet); - - if (existing == null || existingBeatmaps.Any(b => !existing.Beatmaps.Contains(b))) - { - LogForModel(beatmapSet, "Found existing import with IDs already, resetting..."); - resetIds(); - } - } - - void resetIds() => beatmapSet.Beatmaps.ForEach(b => b.OnlineBeatmapID = null); - } - - protected override bool CheckLocalAvailability(BeatmapSetInfo model, IQueryable items) - => base.CheckLocalAvailability(model, items) - || (model.OnlineBeatmapSetID != null && items.Any(b => b.OnlineBeatmapSetID == model.OnlineBeatmapSetID)); - - /// - /// Delete a beatmap difficulty. - /// - /// The beatmap difficulty to hide. - public void Hide(BeatmapInfo beatmap) => beatmaps.Hide(beatmap); - - /// - /// Restore a beatmap difficulty. - /// - /// The beatmap difficulty to restore. - public void Restore(BeatmapInfo beatmap) => beatmaps.Restore(beatmap); - - /// - /// Saves an file against a given . - /// - /// The to save the content against. The file referenced by will be replaced. - /// The content to write. - /// The beatmap content to write, null if to be omitted. - public virtual void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null) - { - var setInfo = info.BeatmapSet; - - using (var stream = new MemoryStream()) - { - using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) - new LegacyBeatmapEncoder(beatmapContent, beatmapSkin).Encode(sw); - - stream.Seek(0, SeekOrigin.Begin); - - using (ContextFactory.GetForWrite()) - { - var beatmapInfo = setInfo.Beatmaps.Single(b => b.ID == info.ID); - var metadata = beatmapInfo.Metadata ?? setInfo.Metadata; - - // grab the original file (or create a new one if not found). - var fileInfo = setInfo.Files.SingleOrDefault(f => string.Equals(f.Filename, beatmapInfo.Path, StringComparison.OrdinalIgnoreCase)) ?? new BeatmapSetFileInfo(); - - // metadata may have changed; update the path with the standard format. - beatmapInfo.Path = $"{metadata.Artist} - {metadata.Title} ({metadata.Author}) [{beatmapInfo.Version}].osu"; - beatmapInfo.MD5Hash = stream.ComputeMD5Hash(); - - // update existing or populate new file's filename. - fileInfo.Filename = beatmapInfo.Path; - - stream.Seek(0, SeekOrigin.Begin); - ReplaceFile(setInfo, fileInfo, stream); - } - } - - WorkingBeatmapCache?.Invalidate(info); - } - - /// - /// Perform a lookup query on available s. - /// - /// The query. - /// The first result for the provided query, or null if no results were found. - public BeatmapSetInfo QueryBeatmapSet(Expression> query) => beatmaps.ConsumableItems.AsNoTracking().FirstOrDefault(query); - - protected override bool CanSkipImport(BeatmapSetInfo existing, BeatmapSetInfo import) - { - if (!base.CanSkipImport(existing, import)) - return false; - - return existing.Beatmaps.Any(b => b.OnlineBeatmapID != null); - } - - protected override bool CanReuseExisting(BeatmapSetInfo existing, BeatmapSetInfo import) - { - if (!base.CanReuseExisting(existing, import)) - return false; - - var existingIds = existing.Beatmaps.Select(b => b.OnlineBeatmapID).OrderBy(i => i); - var importIds = import.Beatmaps.Select(b => b.OnlineBeatmapID).OrderBy(i => i); - - // force re-import if we are not in a sane state. - return existing.OnlineBeatmapSetID == import.OnlineBeatmapSetID && existingIds.SequenceEqual(importIds); - } - - /// - /// Returns a list of all usable s. - /// - /// A list of available . - public List GetAllUsableBeatmapSets(IncludedDetails includes = IncludedDetails.All, bool includeProtected = false) => - GetAllUsableBeatmapSetsEnumerable(includes, includeProtected).ToList(); - - /// - /// Returns a list of all usable s. Note that files are not populated. - /// - /// The level of detail to include in the returned objects. - /// Whether to include protected (system) beatmaps. These should not be included for gameplay playable use cases. - /// A list of available . - public IEnumerable GetAllUsableBeatmapSetsEnumerable(IncludedDetails includes, bool includeProtected = false) - { - IQueryable queryable; - - switch (includes) - { - case IncludedDetails.Minimal: - queryable = beatmaps.BeatmapSetsOverview; - break; - - case IncludedDetails.AllButRuleset: - queryable = beatmaps.BeatmapSetsWithoutRuleset; - break; - - case IncludedDetails.AllButFiles: - queryable = beatmaps.BeatmapSetsWithoutFiles; - break; - - default: - queryable = beatmaps.ConsumableItems; - break; - } - - // AsEnumerable used here to avoid applying the WHERE in sql. When done so, ef core 2.x uses an incorrect ORDER BY - // clause which causes queries to take 5-10x longer. - // TODO: remove if upgrading to EF core 3.x. - return queryable.AsEnumerable().Where(s => !s.DeletePending && (includeProtected || !s.Protected)); - } - - /// - /// Perform a lookup query on available s. - /// - /// The query. - /// The level of detail to include in the returned objects. - /// Results from the provided query. - public IEnumerable QueryBeatmapSets(Expression> query, IncludedDetails includes = IncludedDetails.All) - { - IQueryable queryable; - - switch (includes) - { - case IncludedDetails.Minimal: - queryable = beatmaps.BeatmapSetsOverview; - break; - - case IncludedDetails.AllButRuleset: - queryable = beatmaps.BeatmapSetsWithoutRuleset; - break; - - case IncludedDetails.AllButFiles: - queryable = beatmaps.BeatmapSetsWithoutFiles; - break; - - default: - queryable = beatmaps.ConsumableItems; - break; - } - - return queryable.AsNoTracking().Where(query); - } - - /// - /// Perform a lookup query on available s. - /// - /// The query. - /// The first result for the provided query, or null if no results were found. - public BeatmapInfo QueryBeatmap(Expression> query) => beatmaps.Beatmaps.AsNoTracking().FirstOrDefault(query); - - /// - /// Perform a lookup query on available s. - /// - /// The query. - /// Results from the provided query. - public IQueryable QueryBeatmaps(Expression> query) => beatmaps.Beatmaps.AsNoTracking().Where(query); - - protected override string HumanisedModelName => "beatmap"; - - protected override BeatmapSetInfo CreateModel(ArchiveReader reader) - { - // let's make sure there are actually .osu files to import. - string mapName = reader.Filenames.FirstOrDefault(f => f.EndsWith(".osu", StringComparison.OrdinalIgnoreCase)); - - if (string.IsNullOrEmpty(mapName)) - { - Logger.Log($"No beatmap files found in the beatmap archive ({reader.Name}).", LoggingTarget.Database); - return null; - } - - Beatmap beatmap; - using (var stream = new LineBufferedReader(reader.GetStream(mapName))) - beatmap = Decoder.GetDecoder(stream).Decode(stream); - - return new BeatmapSetInfo - { - OnlineBeatmapSetID = beatmap.BeatmapInfo.BeatmapSet?.OnlineBeatmapSetID, - Beatmaps = new List(), - Metadata = beatmap.Metadata, - DateAdded = DateTimeOffset.UtcNow - }; - } - - /// - /// Create all required s for the provided archive. - /// - private List createBeatmapDifficulties(List files) - { - var beatmapInfos = new List(); - - foreach (var file in files.Where(f => f.Filename.EndsWith(".osu", StringComparison.OrdinalIgnoreCase))) - { - using (var raw = Files.Store.GetStream(file.FileInfo.StoragePath)) - using (var ms = new MemoryStream()) // we need a memory stream so we can seek - using (var sr = new LineBufferedReader(ms)) - { - raw.CopyTo(ms); - ms.Position = 0; - - var decoder = Decoder.GetDecoder(sr); - IBeatmap beatmap = decoder.Decode(sr); - - string hash = ms.ComputeSHA2Hash(); - - if (beatmapInfos.Any(b => b.Hash == hash)) - continue; - - beatmap.BeatmapInfo.Path = file.Filename; - beatmap.BeatmapInfo.Hash = hash; - beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash(); - - var ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID); - beatmap.BeatmapInfo.Ruleset = ruleset; - - // TODO: this should be done in a better place once we actually need to dynamically update it. - beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating ?? 0; - beatmap.BeatmapInfo.Length = calculateLength(beatmap); - beatmap.BeatmapInfo.BPM = 60000 / beatmap.GetMostCommonBeatLength(); - - beatmapInfos.Add(beatmap.BeatmapInfo); - } - } - - return beatmapInfos; - } - - private double calculateLength(IBeatmap b) - { - if (!b.HitObjects.Any()) - return 0; - - var lastObject = b.HitObjects.Last(); - - //TODO: this isn't always correct (consider mania where a non-last object may last for longer than the last in the list). - double endTime = lastObject.GetEndTime(); - double startTime = b.HitObjects.First().StartTime; - - return endTime - startTime; - } - - /// - /// A dummy WorkingBeatmap for the purpose of retrieving a beatmap for star difficulty calculation. - /// - private class DummyConversionBeatmap : WorkingBeatmap - { - private readonly IBeatmap beatmap; - - public DummyConversionBeatmap(IBeatmap beatmap) - : base(beatmap.BeatmapInfo, null) - { - this.beatmap = beatmap; - } - - protected override IBeatmap GetBeatmap() => beatmap; - protected override Texture GetBackground() => null; - protected override Track GetBeatmapTrack() => null; - protected internal override ISkin GetSkin() => null; - public override Stream GetStream(string storagePath) => null; - } } - /// - /// The level of detail to include in database results. - /// - public enum IncludedDetails - { - /// - /// Only include beatmap difficulties and set level metadata. - /// - Minimal, - - /// - /// Include all difficulties, rulesets, difficulty metadata but no files. - /// - AllButFiles, - - /// - /// Include everything except ruleset. Used for cases where we aren't sure the ruleset is present but still want to consume the beatmap. - /// - AllButRuleset, - - /// - /// Include everything. - /// - All - } } diff --git a/osu.Game/Beatmaps/BeatmapModelManager.cs b/osu.Game/Beatmaps/BeatmapModelManager.cs new file mode 100644 index 0000000000..be3adc412c --- /dev/null +++ b/osu.Game/Beatmaps/BeatmapModelManager.cs @@ -0,0 +1,479 @@ +// 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.IO; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using osu.Framework.Audio.Track; +using osu.Framework.Bindables; +using osu.Framework.Extensions; +using osu.Framework.Graphics.Textures; +using osu.Framework.Logging; +using osu.Framework.Platform; +using osu.Framework.Testing; +using osu.Game.Beatmaps.Formats; +using osu.Game.Database; +using osu.Game.IO; +using osu.Game.IO.Archives; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Objects; +using osu.Game.Skinning; +using Decoder = osu.Game.Beatmaps.Formats.Decoder; + +namespace osu.Game.Beatmaps +{ + /// + /// Handles ef-core storage of beatmaps. + /// + [ExcludeFromDynamicCompile] + public class BeatmapModelManager : DownloadableArchiveModelManager + { + /// + /// Fired when a single difficulty has been hidden. + /// + public IBindable> BeatmapHidden => beatmapHidden; + + private readonly Bindable> beatmapHidden = new Bindable>(); + + /// + /// Fired when a single difficulty has been restored. + /// + public IBindable> BeatmapRestored => beatmapRestored; + + /// + /// A function which populates online information during the import process. + /// It is run as the final step of import. + /// + public Func PopulateOnlineInformation; + + /// + /// The game working beatmap cache, used to invalidate entries on changes. + /// + public WorkingBeatmapCache WorkingBeatmapCache { private get; set; } + + private readonly Bindable> beatmapRestored = new Bindable>(); + + public override IEnumerable HandledExtensions => new[] { ".osz" }; + + protected override string[] HashableFileTypes => new[] { ".osu" }; + + protected override string ImportFromStablePath => "."; + + protected override Storage PrepareStableStorage(StableStorage stableStorage) => stableStorage.GetSongStorage(); + + private readonly BeatmapStore beatmaps; + private readonly RulesetStore rulesets; + + public BeatmapModelManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, GameHost host = null) + : base(storage, contextFactory, api, new BeatmapStore(contextFactory), host) + { + this.rulesets = rulesets; + + beatmaps = (BeatmapStore)ModelStore; + beatmaps.BeatmapHidden += b => beatmapHidden.Value = new WeakReference(b); + beatmaps.BeatmapRestored += b => beatmapRestored.Value = new WeakReference(b); + beatmaps.ItemRemoved += b => WorkingBeatmapCache?.Invalidate(b); + beatmaps.ItemUpdated += obj => WorkingBeatmapCache?.Invalidate(obj); + } + + protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) => + new DownloadBeatmapSetRequest(set, minimiseDownloadSize); + + protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == ".osz"; + + protected override async Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive, CancellationToken cancellationToken = default) + { + if (archive != null) + beatmapSet.Beatmaps = createBeatmapDifficulties(beatmapSet.Files); + + foreach (BeatmapInfo b in beatmapSet.Beatmaps) + { + // remove metadata from difficulties where it matches the set + if (beatmapSet.Metadata.Equals(b.Metadata)) + b.Metadata = null; + + b.BeatmapSet = beatmapSet; + } + + validateOnlineIds(beatmapSet); + + bool hadOnlineBeatmapIDs = beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0); + + if (PopulateOnlineInformation != null) + await PopulateOnlineInformation(beatmapSet, cancellationToken).ConfigureAwait(false); + + // ensure at least one beatmap was able to retrieve or keep an online ID, else drop the set ID. + if (hadOnlineBeatmapIDs && !beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0)) + { + if (beatmapSet.OnlineBeatmapSetID != null) + { + beatmapSet.OnlineBeatmapSetID = null; + LogForModel(beatmapSet, "Disassociating beatmap set ID due to loss of all beatmap IDs"); + } + } + } + + protected override void PreImport(BeatmapSetInfo beatmapSet) + { + if (beatmapSet.Beatmaps.Any(b => b.BaseDifficulty == null)) + throw new InvalidOperationException($"Cannot import {nameof(BeatmapInfo)} with null {nameof(BeatmapInfo.BaseDifficulty)}."); + + // check if a set already exists with the same online id, delete if it does. + if (beatmapSet.OnlineBeatmapSetID != null) + { + var existingOnlineId = beatmaps.ConsumableItems.FirstOrDefault(b => b.OnlineBeatmapSetID == beatmapSet.OnlineBeatmapSetID); + + if (existingOnlineId != null) + { + Delete(existingOnlineId); + + // in order to avoid a unique key constraint, immediately remove the online ID from the previous set. + existingOnlineId.OnlineBeatmapSetID = null; + foreach (var b in existingOnlineId.Beatmaps) + b.OnlineBeatmapID = null; + + LogForModel(beatmapSet, $"Found existing beatmap set with same OnlineBeatmapSetID ({beatmapSet.OnlineBeatmapSetID}). It has been deleted."); + } + } + } + + private void validateOnlineIds(BeatmapSetInfo beatmapSet) + { + var beatmapIds = beatmapSet.Beatmaps.Where(b => b.OnlineBeatmapID.HasValue).Select(b => b.OnlineBeatmapID).ToList(); + + // ensure all IDs are unique + if (beatmapIds.GroupBy(b => b).Any(g => g.Count() > 1)) + { + LogForModel(beatmapSet, "Found non-unique IDs, resetting..."); + resetIds(); + return; + } + + // find any existing beatmaps in the database that have matching online ids + var existingBeatmaps = QueryBeatmaps(b => beatmapIds.Contains(b.OnlineBeatmapID)).ToList(); + + if (existingBeatmaps.Count > 0) + { + // reset the import ids (to force a re-fetch) *unless* they match the candidate CheckForExisting set. + // we can ignore the case where the new ids are contained by the CheckForExisting set as it will either be used (import skipped) or deleted. + var existing = CheckForExisting(beatmapSet); + + if (existing == null || existingBeatmaps.Any(b => !existing.Beatmaps.Contains(b))) + { + LogForModel(beatmapSet, "Found existing import with IDs already, resetting..."); + resetIds(); + } + } + + void resetIds() => beatmapSet.Beatmaps.ForEach(b => b.OnlineBeatmapID = null); + } + + protected override bool CheckLocalAvailability(BeatmapSetInfo model, IQueryable items) + => base.CheckLocalAvailability(model, items) + || (model.OnlineBeatmapSetID != null && items.Any(b => b.OnlineBeatmapSetID == model.OnlineBeatmapSetID)); + + /// + /// Delete a beatmap difficulty. + /// + /// The beatmap difficulty to hide. + public void Hide(BeatmapInfo beatmap) => beatmaps.Hide(beatmap); + + /// + /// Restore a beatmap difficulty. + /// + /// The beatmap difficulty to restore. + public void Restore(BeatmapInfo beatmap) => beatmaps.Restore(beatmap); + + /// + /// Saves an file against a given . + /// + /// The to save the content against. The file referenced by will be replaced. + /// The content to write. + /// The beatmap content to write, null if to be omitted. + public virtual void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null) + { + var setInfo = info.BeatmapSet; + + using (var stream = new MemoryStream()) + { + using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) + new LegacyBeatmapEncoder(beatmapContent, beatmapSkin).Encode(sw); + + stream.Seek(0, SeekOrigin.Begin); + + using (ContextFactory.GetForWrite()) + { + var beatmapInfo = setInfo.Beatmaps.Single(b => b.ID == info.ID); + var metadata = beatmapInfo.Metadata ?? setInfo.Metadata; + + // grab the original file (or create a new one if not found). + var fileInfo = setInfo.Files.SingleOrDefault(f => string.Equals(f.Filename, beatmapInfo.Path, StringComparison.OrdinalIgnoreCase)) ?? new BeatmapSetFileInfo(); + + // metadata may have changed; update the path with the standard format. + beatmapInfo.Path = $"{metadata.Artist} - {metadata.Title} ({metadata.Author}) [{beatmapInfo.Version}].osu"; + beatmapInfo.MD5Hash = stream.ComputeMD5Hash(); + + // update existing or populate new file's filename. + fileInfo.Filename = beatmapInfo.Path; + + stream.Seek(0, SeekOrigin.Begin); + ReplaceFile(setInfo, fileInfo, stream); + } + } + + WorkingBeatmapCache?.Invalidate(info); + } + + /// + /// Perform a lookup query on available s. + /// + /// The query. + /// The first result for the provided query, or null if no results were found. + public BeatmapSetInfo QueryBeatmapSet(Expression> query) => beatmaps.ConsumableItems.AsNoTracking().FirstOrDefault(query); + + protected override bool CanSkipImport(BeatmapSetInfo existing, BeatmapSetInfo import) + { + if (!base.CanSkipImport(existing, import)) + return false; + + return existing.Beatmaps.Any(b => b.OnlineBeatmapID != null); + } + + protected override bool CanReuseExisting(BeatmapSetInfo existing, BeatmapSetInfo import) + { + if (!base.CanReuseExisting(existing, import)) + return false; + + var existingIds = existing.Beatmaps.Select(b => b.OnlineBeatmapID).OrderBy(i => i); + var importIds = import.Beatmaps.Select(b => b.OnlineBeatmapID).OrderBy(i => i); + + // force re-import if we are not in a sane state. + return existing.OnlineBeatmapSetID == import.OnlineBeatmapSetID && existingIds.SequenceEqual(importIds); + } + + /// + /// Returns a list of all usable s. + /// + /// A list of available . + public List GetAllUsableBeatmapSets(IncludedDetails includes = IncludedDetails.All, bool includeProtected = false) => + GetAllUsableBeatmapSetsEnumerable(includes, includeProtected).ToList(); + + /// + /// Returns a list of all usable s. Note that files are not populated. + /// + /// The level of detail to include in the returned objects. + /// Whether to include protected (system) beatmaps. These should not be included for gameplay playable use cases. + /// A list of available . + public IEnumerable GetAllUsableBeatmapSetsEnumerable(IncludedDetails includes, bool includeProtected = false) + { + IQueryable queryable; + + switch (includes) + { + case IncludedDetails.Minimal: + queryable = beatmaps.BeatmapSetsOverview; + break; + + case IncludedDetails.AllButRuleset: + queryable = beatmaps.BeatmapSetsWithoutRuleset; + break; + + case IncludedDetails.AllButFiles: + queryable = beatmaps.BeatmapSetsWithoutFiles; + break; + + default: + queryable = beatmaps.ConsumableItems; + break; + } + + // AsEnumerable used here to avoid applying the WHERE in sql. When done so, ef core 2.x uses an incorrect ORDER BY + // clause which causes queries to take 5-10x longer. + // TODO: remove if upgrading to EF core 3.x. + return queryable.AsEnumerable().Where(s => !s.DeletePending && (includeProtected || !s.Protected)); + } + + /// + /// Perform a lookup query on available s. + /// + /// The query. + /// The level of detail to include in the returned objects. + /// Results from the provided query. + public IEnumerable QueryBeatmapSets(Expression> query, IncludedDetails includes = IncludedDetails.All) + { + IQueryable queryable; + + switch (includes) + { + case IncludedDetails.Minimal: + queryable = beatmaps.BeatmapSetsOverview; + break; + + case IncludedDetails.AllButRuleset: + queryable = beatmaps.BeatmapSetsWithoutRuleset; + break; + + case IncludedDetails.AllButFiles: + queryable = beatmaps.BeatmapSetsWithoutFiles; + break; + + default: + queryable = beatmaps.ConsumableItems; + break; + } + + return queryable.AsNoTracking().Where(query); + } + + /// + /// Perform a lookup query on available s. + /// + /// The query. + /// The first result for the provided query, or null if no results were found. + public BeatmapInfo QueryBeatmap(Expression> query) => beatmaps.Beatmaps.AsNoTracking().FirstOrDefault(query); + + /// + /// Perform a lookup query on available s. + /// + /// The query. + /// Results from the provided query. + public IQueryable QueryBeatmaps(Expression> query) => beatmaps.Beatmaps.AsNoTracking().Where(query); + + protected override string HumanisedModelName => "beatmap"; + + protected override BeatmapSetInfo CreateModel(ArchiveReader reader) + { + // let's make sure there are actually .osu files to import. + string mapName = reader.Filenames.FirstOrDefault(f => f.EndsWith(".osu", StringComparison.OrdinalIgnoreCase)); + + if (string.IsNullOrEmpty(mapName)) + { + Logger.Log($"No beatmap files found in the beatmap archive ({reader.Name}).", LoggingTarget.Database); + return null; + } + + Beatmap beatmap; + using (var stream = new LineBufferedReader(reader.GetStream(mapName))) + beatmap = Decoder.GetDecoder(stream).Decode(stream); + + return new BeatmapSetInfo + { + OnlineBeatmapSetID = beatmap.BeatmapInfo.BeatmapSet?.OnlineBeatmapSetID, + Beatmaps = new List(), + Metadata = beatmap.Metadata, + DateAdded = DateTimeOffset.UtcNow + }; + } + + /// + /// Create all required s for the provided archive. + /// + private List createBeatmapDifficulties(List files) + { + var beatmapInfos = new List(); + + foreach (var file in files.Where(f => f.Filename.EndsWith(".osu", StringComparison.OrdinalIgnoreCase))) + { + using (var raw = Files.Store.GetStream(file.FileInfo.StoragePath)) + using (var ms = new MemoryStream()) // we need a memory stream so we can seek + using (var sr = new LineBufferedReader(ms)) + { + raw.CopyTo(ms); + ms.Position = 0; + + var decoder = Decoder.GetDecoder(sr); + IBeatmap beatmap = decoder.Decode(sr); + + string hash = ms.ComputeSHA2Hash(); + + if (beatmapInfos.Any(b => b.Hash == hash)) + continue; + + beatmap.BeatmapInfo.Path = file.Filename; + beatmap.BeatmapInfo.Hash = hash; + beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash(); + + var ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID); + beatmap.BeatmapInfo.Ruleset = ruleset; + + // TODO: this should be done in a better place once we actually need to dynamically update it. + beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating ?? 0; + beatmap.BeatmapInfo.Length = calculateLength(beatmap); + beatmap.BeatmapInfo.BPM = 60000 / beatmap.GetMostCommonBeatLength(); + + beatmapInfos.Add(beatmap.BeatmapInfo); + } + } + + return beatmapInfos; + } + + private double calculateLength(IBeatmap b) + { + if (!b.HitObjects.Any()) + return 0; + + var lastObject = b.HitObjects.Last(); + + //TODO: this isn't always correct (consider mania where a non-last object may last for longer than the last in the list). + double endTime = lastObject.GetEndTime(); + double startTime = b.HitObjects.First().StartTime; + + return endTime - startTime; + } + + /// + /// A dummy WorkingBeatmap for the purpose of retrieving a beatmap for star difficulty calculation. + /// + private class DummyConversionBeatmap : WorkingBeatmap + { + private readonly IBeatmap beatmap; + + public DummyConversionBeatmap(IBeatmap beatmap) + : base(beatmap.BeatmapInfo, null) + { + this.beatmap = beatmap; + } + + protected override IBeatmap GetBeatmap() => beatmap; + protected override Texture GetBackground() => null; + protected override Track GetBeatmapTrack() => null; + protected internal override ISkin GetSkin() => null; + public override Stream GetStream(string storagePath) => null; + } + } + + /// + /// The level of detail to include in database results. + /// + public enum IncludedDetails + { + /// + /// Only include beatmap difficulties and set level metadata. + /// + Minimal, + + /// + /// Include all difficulties, rulesets, difficulty metadata but no files. + /// + AllButFiles, + + /// + /// Include everything except ruleset. Used for cases where we aren't sure the ruleset is present but still want to consume the beatmap. + /// + AllButRuleset, + + /// + /// Include everything. + /// + All + } +} From 5618c9933bfd61b5587160a1821248bf7b1fb214 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Sep 2021 16:44:39 +0900 Subject: [PATCH 2145/2442] Expose more pieces of `ArchiveModelManager` via interfaces --- osu.Game/Database/ArchiveModelManager.cs | 11 +- .../DownloadableArchiveModelManager.cs | 6 -- osu.Game/Database/IModelFileManager.cs | 36 +++++++ osu.Game/Database/IModelManager.cs | 101 +++++++++++++++++- osu.Game/Scoring/ScoreManager.cs | 2 +- 5 files changed, 141 insertions(+), 15 deletions(-) create mode 100644 osu.Game/Database/IModelFileManager.cs diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index ddd2bc5d1e..fc217d3058 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -30,7 +30,7 @@ namespace osu.Game.Database /// /// The model type. /// The associated file join type. - public abstract class ArchiveModelManager : ICanAcceptFiles, IModelManager + public abstract class ArchiveModelManager : ICanAcceptFiles, IModelManager, IModelFileManager where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete where TFileModel : class, INamedFileInfo, new() { @@ -135,7 +135,7 @@ namespace osu.Game.Database return Import(notification, tasks); } - protected async Task> Import(ProgressNotification notification, params ImportTask[] tasks) + public async Task> Import(ProgressNotification notification, params ImportTask[] tasks) { if (tasks.Length == 0) { @@ -227,7 +227,7 @@ namespace osu.Game.Database /// Whether this is a low priority import. /// An optional cancellation token. /// The imported model, if successful. - internal async Task Import(ImportTask task, bool lowPriority = false, CancellationToken cancellationToken = default) + public async Task Import(ImportTask task, bool lowPriority = false, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); @@ -479,7 +479,7 @@ namespace osu.Game.Database /// /// The item to export. /// The output stream to export to. - protected virtual void ExportModelTo(TModel model, Stream outputStream) + public virtual void ExportModelTo(TModel model, Stream outputStream) { using (var archive = ZipArchive.Create()) { @@ -745,9 +745,6 @@ namespace osu.Game.Database /// Whether to perform deletion. protected virtual bool ShouldDeleteArchive(string path) => false; - /// - /// 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(StableStorage stableStorage) { var storage = PrepareStableStorage(stableStorage); diff --git a/osu.Game/Database/DownloadableArchiveModelManager.cs b/osu.Game/Database/DownloadableArchiveModelManager.cs index da3144e8d0..e6d5b44b65 100644 --- a/osu.Game/Database/DownloadableArchiveModelManager.cs +++ b/osu.Game/Database/DownloadableArchiveModelManager.cs @@ -54,12 +54,6 @@ namespace osu.Game.Database /// The request object. protected abstract ArchiveDownloadRequest CreateDownloadRequest(TModel model, bool minimiseDownloadSize); - /// - /// Begin a download for the requested . - /// - /// The to be downloaded. - /// Whether this download should be optimised for slow connections. Generally means extras are not included in the download bundle. - /// Whether the download was started. public bool Download(TModel model, bool minimiseDownloadSize = false) { if (!canDownload(model)) return false; diff --git a/osu.Game/Database/IModelFileManager.cs b/osu.Game/Database/IModelFileManager.cs new file mode 100644 index 0000000000..c74b945eb7 --- /dev/null +++ b/osu.Game/Database/IModelFileManager.cs @@ -0,0 +1,36 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.IO; + +namespace osu.Game.Database +{ + public interface IModelFileManager + where TModel : class + where TFileModel : class + { + /// + /// Replace an existing file with a new version. + /// + /// The item to operate on. + /// The existing file to be replaced. + /// The new file contents. + /// An optional filename for the new file. Will use the previous filename if not specified. + void ReplaceFile(TModel model, TFileModel file, Stream contents, string filename = null); + + /// + /// Delete an existing file. + /// + /// The item to operate on. + /// The existing file to be deleted. + void DeleteFile(TModel model, TFileModel file); + + /// + /// Add a new file. + /// + /// The item to operate on. + /// The new file contents. + /// The filename for the new file. + void AddFile(TModel model, Stream contents, string filename); + } +} diff --git a/osu.Game/Database/IModelManager.cs b/osu.Game/Database/IModelManager.cs index 8c314f1617..8f0c6e1561 100644 --- a/osu.Game/Database/IModelManager.cs +++ b/osu.Game/Database/IModelManager.cs @@ -1,8 +1,15 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// 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.IO; +using System.Threading; +using System.Threading.Tasks; using osu.Framework.Bindables; +using osu.Game.IO; +using osu.Game.IO.Archives; +using osu.Game.Overlays.Notifications; namespace osu.Game.Database { @@ -24,5 +31,97 @@ namespace osu.Game.Database /// This is not thread-safe and should be scheduled locally if consumed from a drawable component. /// IBindable> ItemRemoved { get; } + + /// + /// This is a temporary method and will likely be replaced by a full-fledged (and more correctly placed) migration process in the future. + /// + Task ImportFromStableAsync(StableStorage stableStorage); + + /// + /// Exports an item to a legacy (.zip based) package. + /// + /// The item to export. + void Export(TModel item); + + /// + /// Exports an item to the given output stream. + /// + /// The item to export. + /// The output stream to export to. + void ExportModelTo(TModel model, Stream outputStream); + + /// + /// Perform an update of the specified item. + /// TODO: Support file additions/removals. + /// + /// The item to update. + void Update(TModel item); + + /// + /// Delete an item from the manager. + /// Is a no-op for already deleted items. + /// + /// The item to delete. + /// false if no operation was performed + bool Delete(TModel item); + + /// + /// Delete multiple items. + /// This will post notifications tracking progress. + /// + void Delete(List items, bool silent = false); + + /// + /// Restore multiple items that were previously deleted. + /// This will post notifications tracking progress. + /// + void Undelete(List items, bool silent = false); + + /// + /// Restore an item that was previously deleted. Is a no-op if the item is not in a deleted state, or has its protected flag set. + /// + /// The item to restore + void Undelete(TModel item); + + /// + /// Import one or more items from filesystem . + /// + /// + /// This will be treated as a low priority import if more than one path is specified; use to always import at standard priority. + /// This will post notifications tracking progress. + /// + /// One or more archive locations on disk. + Task Import(params string[] paths); + + Task Import(params ImportTask[] tasks); + + Task> Import(ProgressNotification notification, params ImportTask[] tasks); + + /// + /// Import one from the filesystem and delete the file on success. + /// Note that this bypasses the UI flow and should only be used for special cases or testing. + /// + /// The containing data about the to import. + /// Whether this is a low priority import. + /// An optional cancellation token. + /// The imported model, if successful. + Task Import(ImportTask task, bool lowPriority = false, CancellationToken cancellationToken = default); + + /// + /// Silently import an item from an . + /// + /// The archive to be imported. + /// Whether this is a low priority import. + /// An optional cancellation token. + Task Import(ArchiveReader archive, bool lowPriority = false, CancellationToken cancellationToken = default); + + /// + /// Silently import an item from a . + /// + /// The model to be imported. + /// An optional archive to use for model population. + /// Whether this is a low priority import. + /// An optional cancellation token. + Task Import(TModel item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default); } } diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 81e701f001..56c346d177 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -78,7 +78,7 @@ namespace osu.Game.Scoring protected override Task Populate(ScoreInfo model, ArchiveReader archive, CancellationToken cancellationToken = default) => Task.CompletedTask; - protected override void ExportModelTo(ScoreInfo model, Stream outputStream) + public override void ExportModelTo(ScoreInfo model, Stream outputStream) { var file = model.Files.SingleOrDefault(); if (file == null) From 84bddf0885893d700addf2cbc00900dc408a0f8e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Sep 2021 17:00:15 +0900 Subject: [PATCH 2146/2442] Initial PP counter implementation --- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- .../Difficulty/DifficultyCalculator.cs | 126 +++++++++++++++--- .../HUD/DefaultPerformancePointsCounter.cs | 108 +++++++++++++++ osu.Game/Screens/Play/Player.cs | 8 +- 4 files changed, 218 insertions(+), 26 deletions(-) create mode 100644 osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 61760e69b0..c4c5c89f28 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -81,7 +81,7 @@ namespace osu.Game.Beatmaps /// The applicable . protected virtual IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => ruleset.CreateBeatmapConverter(beatmap); - public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null) + public virtual IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null) { using (var cancellationSource = createCancellationTokenSource(timeout)) { diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 224c9178ae..f38949d982 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -7,6 +7,8 @@ using System.Linq; using osu.Framework.Audio.Track; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; @@ -19,6 +21,10 @@ namespace osu.Game.Rulesets.Difficulty private readonly Ruleset ruleset; private readonly WorkingBeatmap beatmap; + private IBeatmap playableBeatmap; + private Mod[] playableMods; + private double clockRate; + protected DifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) { this.ruleset = ruleset; @@ -32,14 +38,41 @@ namespace osu.Game.Rulesets.Difficulty /// A structure describing the difficulty of the beatmap. public DifficultyAttributes Calculate(params Mod[] mods) { - mods = mods.Select(m => m.DeepClone()).ToArray(); + preProcess(mods); - IBeatmap playableBeatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods); + var skills = CreateSkills(playableBeatmap, playableMods, clockRate); - var track = new TrackVirtual(10000); - mods.OfType().ForEach(m => m.ApplyToTrack(track)); + if (!playableBeatmap.HitObjects.Any()) + return CreateDifficultyAttributes(playableBeatmap, playableMods, skills, clockRate); - return calculate(playableBeatmap, mods, track.Rate); + foreach (var hitObject in getDifficultyHitObjects()) + { + foreach (var skill in skills) + skill.ProcessInternal(hitObject); + } + + return CreateDifficultyAttributes(playableBeatmap, playableMods, skills, clockRate); + } + + public IEnumerable CalculateTimed(params Mod[] mods) + { + preProcess(mods); + + if (!playableBeatmap.HitObjects.Any()) + yield break; + + var skills = CreateSkills(playableBeatmap, playableMods, clockRate); + var progressiveBeatmap = new ProgressiveCalculationBeatmap(playableBeatmap); + + foreach (var hitObject in getDifficultyHitObjects()) + { + progressiveBeatmap.HitObjects.Add(hitObject.BaseObject); + + foreach (var skill in skills) + skill.ProcessInternal(hitObject); + + yield return new TimedDifficultyAttributes(hitObject.EndTime, CreateDifficultyAttributes(progressiveBeatmap, playableMods, skills, clockRate)); + } } /// @@ -57,24 +90,23 @@ namespace osu.Game.Rulesets.Difficulty } } - private DifficultyAttributes calculate(IBeatmap beatmap, Mod[] mods, double clockRate) + /// + /// Retrieves the s to calculate against. + /// + private IEnumerable getDifficultyHitObjects() => SortObjects(CreateDifficultyHitObjects(playableBeatmap, clockRate)); + + /// + /// Performs required tasks before every calculation. + /// + /// The original list of s. + private void preProcess(Mod[] mods) { - var skills = CreateSkills(beatmap, mods, clockRate); + playableMods = mods.Select(m => m.DeepClone()).ToArray(); + playableBeatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods); - if (!beatmap.HitObjects.Any()) - return CreateDifficultyAttributes(beatmap, mods, skills, clockRate); - - var difficultyHitObjects = SortObjects(CreateDifficultyHitObjects(beatmap, clockRate)).ToList(); - - foreach (var hitObject in difficultyHitObjects) - { - foreach (var skill in skills) - { - skill.ProcessInternal(hitObject); - } - } - - return CreateDifficultyAttributes(beatmap, mods, skills, clockRate); + var track = new TrackVirtual(10000); + mods.OfType().ForEach(m => m.ApplyToTrack(track)); + clockRate = track.Rate; } /// @@ -183,5 +215,57 @@ namespace osu.Game.Rulesets.Difficulty /// Clockrate to calculate difficulty with. /// The s. protected abstract Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate); + + public class TimedDifficultyAttributes : IComparable + { + public readonly double Time; + public readonly DifficultyAttributes Attributes; + + public TimedDifficultyAttributes(double time, DifficultyAttributes attributes) + { + Time = time; + Attributes = attributes; + } + + public int CompareTo(TimedDifficultyAttributes other) => Time.CompareTo(other.Time); + } + + private class ProgressiveCalculationBeatmap : IBeatmap + { + private readonly IBeatmap baseBeatmap; + + public ProgressiveCalculationBeatmap(IBeatmap baseBeatmap) + { + this.baseBeatmap = baseBeatmap; + } + + public BeatmapInfo BeatmapInfo + { + get => baseBeatmap.BeatmapInfo; + set => baseBeatmap.BeatmapInfo = value; + } + + public BeatmapMetadata Metadata => baseBeatmap.Metadata; + + public ControlPointInfo ControlPointInfo + { + get => baseBeatmap.ControlPointInfo; + set => baseBeatmap.ControlPointInfo = value; + } + + public List Breaks => baseBeatmap.Breaks; + + public double TotalBreakTime => baseBeatmap.TotalBreakTime; + + public readonly List HitObjects = new List(); + + IReadOnlyList IBeatmap.HitObjects => HitObjects; + + public IEnumerable GetStatistics() => baseBeatmap.GetStatistics(); + + public double GetMostCommonBeatLength() => baseBeatmap.GetMostCommonBeatLength(); + + public IBeatmap Clone() => new ProgressiveCalculationBeatmap(baseBeatmap.Clone()); + } } } diff --git a/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs new file mode 100644 index 0000000000..18bb621dd1 --- /dev/null +++ b/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs @@ -0,0 +1,108 @@ +// 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.IO; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Audio.Track; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Textures; +using osu.Framework.Localisation; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; + +namespace osu.Game.Screens.Play.HUD +{ + public class DefaultPerformancePointsCounter : RollingCounter, ISkinnableDrawable + { + public bool UsesFixedAnchor { get; set; } + + [Resolved] + private ScoreProcessor scoreProcessor { get; set; } + + [Resolved] + private Player player { get; set; } + + private DifficultyCalculator.TimedDifficultyAttributes[] timedAttributes; + private Ruleset gameplayRuleset; + + public DefaultPerformancePointsCounter() + { + Current.Value = DisplayedCount = 0; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Colour = colours.BlueLighter; + + gameplayRuleset = player.GameplayRuleset; + timedAttributes = gameplayRuleset.CreateDifficultyCalculator(new GameplayWorkingBeatmap(player.GameplayBeatmap)).CalculateTimed(player.Mods.Value.ToArray()).ToArray(); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + scoreProcessor.NewJudgement += onNewJudgement; + } + + private void onNewJudgement(JudgementResult judgement) + { + var attribIndex = Array.BinarySearch(timedAttributes, 0, timedAttributes.Length, new DifficultyCalculator.TimedDifficultyAttributes(judgement.HitObject.GetEndTime(), null)); + if (attribIndex < 0) + attribIndex = ~attribIndex - 1; + attribIndex = Math.Clamp(attribIndex, 0, timedAttributes.Length - 1); + + var ppProcessor = gameplayRuleset.CreatePerformanceCalculator(timedAttributes[attribIndex].Attributes, player.Score.ScoreInfo); + Current.Value = (int)(ppProcessor?.Calculate() ?? 0); + } + + protected override LocalisableString FormatCount(int count) => $@"{count}pp"; + + protected override OsuSpriteText CreateSpriteText() + => base.CreateSpriteText().With(s => s.Font = s.Font.With(size: 20f)); + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (scoreProcessor != null) + scoreProcessor.NewJudgement -= onNewJudgement; + } + + private class GameplayWorkingBeatmap : WorkingBeatmap + { + private readonly GameplayBeatmap gameplayBeatmap; + + public GameplayWorkingBeatmap(GameplayBeatmap gameplayBeatmap) + : base(gameplayBeatmap.BeatmapInfo, null) + { + this.gameplayBeatmap = gameplayBeatmap; + } + + public override IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null) + => gameplayBeatmap; + + protected override IBeatmap GetBeatmap() => gameplayBeatmap; + + protected override Texture GetBackground() => throw new NotImplementedException(); + + protected override Track GetBeatmapTrack() => throw new NotImplementedException(); + + protected internal override ISkin GetSkin() => throw new NotImplementedException(); + + public override Stream GetStream(string storagePath) => throw new NotImplementedException(); + } + } +} diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 9927467bd6..4018650093 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -93,9 +93,9 @@ namespace osu.Game.Screens.Play [Resolved] private SpectatorClient spectatorClient { get; set; } - protected Ruleset GameplayRuleset { get; private set; } + public Ruleset GameplayRuleset { get; private set; } - protected GameplayBeatmap GameplayBeatmap { get; private set; } + public GameplayBeatmap GameplayBeatmap { get; private set; } private Sample sampleRestart; @@ -127,7 +127,7 @@ namespace osu.Game.Screens.Play [Cached] [Cached(Type = typeof(IBindable>))] - protected new readonly Bindable> Mods = new Bindable>(Array.Empty()); + public new readonly Bindable> Mods = new Bindable>(Array.Empty()); /// /// Whether failing should be allowed. @@ -137,7 +137,7 @@ namespace osu.Game.Screens.Play public readonly PlayerConfiguration Configuration; - protected Score Score { get; private set; } + public Score Score { get; private set; } /// /// Create a new player instance. From 90225f20820ed74fe6269a7c1c4105d2c3e4866a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Sep 2021 16:45:32 +0900 Subject: [PATCH 2147/2442] Hook up all required interfaces to new `BeatmapManager` --- ...eneOnlinePlayBeatmapAvailabilityTracker.cs | 26 +- osu.Game/Beatmaps/BeatmapManager.cs | 297 +++++++++++++++++- osu.Game/Beatmaps/IWorkingBeatmapCache.cs | 15 + osu.Game/Beatmaps/WorkingBeatmapCache.cs | 42 +-- osu.Game/OsuGameBase.cs | 6 - osu.Game/Tests/Visual/EditorTestScene.cs | 37 ++- 6 files changed, 364 insertions(+), 59 deletions(-) create mode 100644 osu.Game/Beatmaps/IWorkingBeatmapCache.cs diff --git a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs index a7d34fadbe..1a3f9e414d 100644 --- a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs @@ -158,18 +158,34 @@ namespace osu.Game.Tests.Online public Task CurrentImportTask { get; private set; } - protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) - => new TestDownloadRequest(set); + protected override BeatmapModelManager CreateBeatmapModelManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, GameHost host) + { + return new TestBeatmapModelManager(this, storage, contextFactory, rulesets, api, host); + } public TestBeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore resources, GameHost host = null, WorkingBeatmap defaultBeatmap = null) : base(storage, contextFactory, rulesets, api, audioManager, resources, host, defaultBeatmap) { } - public override async Task Import(BeatmapSetInfo item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default) + internal class TestBeatmapModelManager : BeatmapModelManager { - await AllowImport.Task.ConfigureAwait(false); - return await (CurrentImportTask = base.Import(item, archive, lowPriority, cancellationToken)).ConfigureAwait(false); + private readonly TestBeatmapManager testBeatmapManager; + + public TestBeatmapModelManager(TestBeatmapManager testBeatmapManager, Storage storage, IDatabaseContextFactory databaseContextFactory, RulesetStore rulesetStore, IAPIProvider apiProvider, GameHost gameHost) + : base(storage, databaseContextFactory, rulesetStore, apiProvider, gameHost) + { + this.testBeatmapManager = testBeatmapManager; + } + + protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) + => new TestDownloadRequest(set); + + public override async Task Import(BeatmapSetInfo item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default) + { + await testBeatmapManager.AllowImport.Task.ConfigureAwait(false); + return await (testBeatmapManager.CurrentImportTask = base.Import(item, archive, lowPriority, cancellationToken)).ConfigureAwait(false); + } } } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index c445925a90..18513945e5 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -1,7 +1,27 @@ // 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.IO; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using JetBrains.Annotations; +using osu.Framework.Audio; +using osu.Framework.Bindables; +using osu.Framework.IO.Stores; +using osu.Framework.Platform; using osu.Framework.Testing; +using osu.Game.Database; +using osu.Game.IO; +using osu.Game.IO.Archives; +using osu.Game.Online.API; +using osu.Game.Overlays.Notifications; +using osu.Game.Rulesets; +using osu.Game.Skinning; +using osu.Game.Users; namespace osu.Game.Beatmaps { @@ -9,13 +29,282 @@ namespace osu.Game.Beatmaps /// Handles general operations related to global beatmap management. /// [ExcludeFromDynamicCompile] - public class BeatmapManager + public class BeatmapManager : IModelDownloader, IModelFileManager, ICanAcceptFiles, IWorkingBeatmapCache { - public BeatmapManager() + private readonly BeatmapModelManager beatmapModelManager; + private readonly WorkingBeatmapCache workingBeatmapCache; + + public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore resources, GameHost host = null, + WorkingBeatmap defaultBeatmap = null, bool performOnlineLookups = false) { - beatmapModelManager = new BeatmapModelManager() + beatmapModelManager = CreateBeatmapModelManager(storage, contextFactory, rulesets, api, host); + workingBeatmapCache = CreateWorkingBeatmapCache(audioManager, resources, new FileStore(contextFactory, storage).Store, defaultBeatmap, host); + + workingBeatmapCache.BeatmapManager = beatmapModelManager; + + var onlineBeatmapLookupCache = new BeatmapOnlineLookupQueue(api, storage); + + beatmapModelManager.PopulateOnlineInformation = onlineBeatmapLookupCache.UpdateAsync; } - } + protected virtual WorkingBeatmapCache CreateWorkingBeatmapCache(AudioManager audioManager, IResourceStore resources, IResourceStore storage, WorkingBeatmap defaultBeatmap, GameHost host) => + new WorkingBeatmapCache(audioManager, resources, storage, defaultBeatmap, host); + protected virtual BeatmapModelManager CreateBeatmapModelManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, GameHost host) => + new BeatmapModelManager(storage, contextFactory, rulesets, api, host); + + /// + /// Create a new . + /// + public WorkingBeatmap CreateNew(RulesetInfo ruleset, User user) + { + var metadata = new BeatmapMetadata + { + Author = user, + }; + + var set = new BeatmapSetInfo + { + Metadata = metadata, + Beatmaps = new List + { + new BeatmapInfo + { + BaseDifficulty = new BeatmapDifficulty(), + Ruleset = ruleset, + Metadata = metadata, + WidescreenStoryboard = true, + SamplesMatchPlaybackRate = true, + } + } + }; + + var working = beatmapModelManager.Import(set).Result; + return GetWorkingBeatmap(working.Beatmaps.First()); + } + + #region Delegation to BeatmapModelManager (methods which previously existed locally). + + /// + /// Fired when a single difficulty has been hidden. + /// + public IBindable> BeatmapHidden => beatmapModelManager.BeatmapHidden; + + /// + /// Fired when a single difficulty has been restored. + /// + public IBindable> BeatmapRestored => beatmapModelManager.BeatmapRestored; + + /// + /// Saves an file against a given . + /// + /// The to save the content against. The file referenced by will be replaced. + /// The content to write. + /// The beatmap content to write, null if to be omitted. + public virtual void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null) => beatmapModelManager.Save(info, beatmapContent, beatmapSkin); + + /// + /// Returns a list of all usable s. + /// + /// A list of available . + public List GetAllUsableBeatmapSets(IncludedDetails includes = IncludedDetails.All, bool includeProtected = false) => beatmapModelManager.GetAllUsableBeatmapSets(includes, includeProtected); + + /// + /// Returns a list of all usable s. Note that files are not populated. + /// + /// The level of detail to include in the returned objects. + /// Whether to include protected (system) beatmaps. These should not be included for gameplay playable use cases. + /// A list of available . + public IEnumerable GetAllUsableBeatmapSetsEnumerable(IncludedDetails includes, bool includeProtected = false) => beatmapModelManager.GetAllUsableBeatmapSetsEnumerable(includes, includeProtected); + + /// + /// Perform a lookup query on available s. + /// + /// The query. + /// The level of detail to include in the returned objects. + /// Results from the provided query. + public IEnumerable QueryBeatmapSets(Expression> query, IncludedDetails includes = IncludedDetails.All) => beatmapModelManager.QueryBeatmapSets(query, includes); + + /// + /// Perform a lookup query on available s. + /// + /// The query. + /// The first result for the provided query, or null if no results were found. + public BeatmapSetInfo QueryBeatmapSet(Expression> query) => beatmapModelManager.QueryBeatmapSet(query); + + /// + /// Perform a lookup query on available s. + /// + /// The query. + /// Results from the provided query. + public IQueryable QueryBeatmaps(Expression> query) => beatmapModelManager.QueryBeatmaps(query); + + /// + /// Perform a lookup query on available s. + /// + /// The query. + /// The first result for the provided query, or null if no results were found. + public BeatmapInfo QueryBeatmap(Expression> query) => beatmapModelManager.QueryBeatmap(query); + + /// + /// A default representation of a WorkingBeatmap to use when no beatmap is available. + /// + public WorkingBeatmap DefaultBeatmap => workingBeatmapCache.DefaultBeatmap; + + /// + /// Fired when a notification should be presented to the user. + /// + public Action PostNotification { set => beatmapModelManager.PostNotification = value; } + + /// + /// Fired when the user requests to view the resulting import. + /// + public Action> PresentImport { set => beatmapModelManager.PresentImport = value; } + + /// + /// Delete a beatmap difficulty. + /// + /// The beatmap difficulty to hide. + public void Hide(BeatmapInfo beatmap) => beatmapModelManager.Hide(beatmap); + + /// + /// Restore a beatmap difficulty. + /// + /// The beatmap difficulty to restore. + public void Restore(BeatmapInfo beatmap) => beatmapModelManager.Restore(beatmap); + + #endregion + + #region Implementation of IModelManager + + public IBindable> ItemUpdated => beatmapModelManager.ItemUpdated; + + public IBindable> ItemRemoved => beatmapModelManager.ItemRemoved; + + public Task ImportFromStableAsync(StableStorage stableStorage) + { + return beatmapModelManager.ImportFromStableAsync(stableStorage); + } + + public void Export(BeatmapSetInfo item) + { + beatmapModelManager.Export(item); + } + + public void ExportModelTo(BeatmapSetInfo model, Stream outputStream) + { + beatmapModelManager.ExportModelTo(model, outputStream); + } + + public void Update(BeatmapSetInfo item) + { + beatmapModelManager.Update(item); + } + + public bool Delete(BeatmapSetInfo item) + { + return beatmapModelManager.Delete(item); + } + + public void Delete(List items, bool silent = false) + { + beatmapModelManager.Delete(items, silent); + } + + public void Undelete(List items, bool silent = false) + { + beatmapModelManager.Undelete(items, silent); + } + + public void Undelete(BeatmapSetInfo item) + { + beatmapModelManager.Undelete(item); + } + + #endregion + + #region Implementation of IModelDownloader + + public IBindable>> DownloadBegan => beatmapModelManager.DownloadBegan; + + public IBindable>> DownloadFailed => beatmapModelManager.DownloadFailed; + + public bool IsAvailableLocally(BeatmapSetInfo model) + { + return beatmapModelManager.IsAvailableLocally(model); + } + + public bool Download(BeatmapSetInfo model, bool minimiseDownloadSize = false) + { + return beatmapModelManager.Download(model, minimiseDownloadSize); + } + + public ArchiveDownloadRequest GetExistingDownload(BeatmapSetInfo model) + { + return beatmapModelManager.GetExistingDownload(model); + } + + #endregion + + #region Implementation of ICanAcceptFiles + + public Task Import(params string[] paths) + { + return beatmapModelManager.Import(paths); + } + + public Task Import(params ImportTask[] tasks) + { + return beatmapModelManager.Import(tasks); + } + + public Task> Import(ProgressNotification notification, params ImportTask[] tasks) + { + return beatmapModelManager.Import(notification, tasks); + } + + public Task Import(ImportTask task, bool lowPriority = false, CancellationToken cancellationToken = default) + { + return beatmapModelManager.Import(task, lowPriority, cancellationToken); + } + + public Task Import(ArchiveReader archive, bool lowPriority = false, CancellationToken cancellationToken = default) + { + return beatmapModelManager.Import(archive, lowPriority, cancellationToken); + } + + public Task Import(BeatmapSetInfo item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default) + { + return beatmapModelManager.Import(item, archive, lowPriority, cancellationToken); + } + + public IEnumerable HandledExtensions => beatmapModelManager.HandledExtensions; + + #endregion + + #region Implementation of IWorkingBeatmapCache + + public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo importedBeatmap) => workingBeatmapCache.GetWorkingBeatmap(importedBeatmap); + + #endregion + + #region Implementation of IModelFileManager + + public void ReplaceFile(BeatmapSetInfo model, BeatmapSetFileInfo file, Stream contents, string filename = null) + { + beatmapModelManager.ReplaceFile(model, file, contents, filename); + } + + public void DeleteFile(BeatmapSetInfo model, BeatmapSetFileInfo file) + { + beatmapModelManager.DeleteFile(model, file); + } + + public void AddFile(BeatmapSetInfo model, Stream contents, string filename) + { + beatmapModelManager.AddFile(model, contents, filename); + } + + #endregion + } } diff --git a/osu.Game/Beatmaps/IWorkingBeatmapCache.cs b/osu.Game/Beatmaps/IWorkingBeatmapCache.cs new file mode 100644 index 0000000000..881e734292 --- /dev/null +++ b/osu.Game/Beatmaps/IWorkingBeatmapCache.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Beatmaps +{ + public interface IWorkingBeatmapCache + { + /// + /// Retrieve a instance for the provided + /// + /// The beatmap to lookup. + /// A instance correlating to the provided . + WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo); + } +} diff --git a/osu.Game/Beatmaps/WorkingBeatmapCache.cs b/osu.Game/Beatmaps/WorkingBeatmapCache.cs index 9f40eb4898..e117f1b82f 100644 --- a/osu.Game/Beatmaps/WorkingBeatmapCache.cs +++ b/osu.Game/Beatmaps/WorkingBeatmapCache.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using System.IO; using System.Linq; using JetBrains.Annotations; @@ -17,14 +16,12 @@ using osu.Framework.Statistics; using osu.Framework.Testing; using osu.Game.Beatmaps.Formats; using osu.Game.IO; -using osu.Game.Rulesets; using osu.Game.Skinning; using osu.Game.Storyboards; -using osu.Game.Users; namespace osu.Game.Beatmaps { - public class WorkingBeatmapCache : IBeatmapResourceProvider + public class WorkingBeatmapCache : IBeatmapResourceProvider, IWorkingBeatmapCache { private readonly WeakList workingCache = new WeakList(); @@ -33,7 +30,7 @@ namespace osu.Game.Beatmaps /// public readonly WorkingBeatmap DefaultBeatmap; - public BeatmapManager BeatmapManager { private get; set; } + public BeatmapModelManager BeatmapManager { private get; set; } private readonly AudioManager audioManager; private readonly IResourceStore resources; @@ -74,41 +71,6 @@ namespace osu.Game.Beatmaps } } - /// - /// Create a new . - /// - public WorkingBeatmap CreateNew(RulesetInfo ruleset, User user) - { - var metadata = new BeatmapMetadata - { - Author = user, - }; - - var set = new BeatmapSetInfo - { - Metadata = metadata, - Beatmaps = new List - { - new BeatmapInfo - { - BaseDifficulty = new BeatmapDifficulty(), - Ruleset = ruleset, - Metadata = metadata, - WidescreenStoryboard = true, - SamplesMatchPlaybackRate = true, - } - } - }; - - var working = BeatmapManager.Import(set).Result; - return GetWorkingBeatmap(working.Beatmaps.First()); - } - - /// - /// Retrieve a instance for the provided - /// - /// The beatmap to lookup. - /// A instance correlating to the provided . public virtual WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo) { // if there are no files, presume the full beatmap info has not yet been fetched from the database. diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 8263e26dec..dc1cb7a850 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -138,8 +138,6 @@ namespace osu.Game private UserLookupCache userCache; - private BeatmapOnlineLookupQueue onlineBeatmapLookupCache; - private FileStore fileStore; private RulesetConfigCache rulesetConfigCache; @@ -246,10 +244,6 @@ namespace osu.Game dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Scheduler, Host, () => difficultyCache, LocalConfig)); dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Resources, Host, defaultBeatmap)); - onlineBeatmapLookupCache = new BeatmapOnlineLookupQueue(API, Storage); - - BeatmapManager.PopulateOnlineInformation = onlineBeatmapLookupCache.UpdateAsync; - // this should likely be moved to ArchiveModelManager when another case appears where it is necessary // to have inter-dependent model managers. this could be obtained with an IHasForeign interface to // allow lookups to be done on the child (ScoreManager in this case) to perform the cascading delete. diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index 1e26036116..ac8773a840 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -123,11 +123,40 @@ namespace osu.Game.Tests.Visual this.testBeatmap = testBeatmap; } - protected override string ComputeHash(BeatmapSetInfo item, ArchiveReader reader = null) - => string.Empty; + protected override BeatmapModelManager CreateBeatmapModelManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, GameHost host) + { + return new TestBeatmapModelManager(storage, contextFactory, rulesets, api, host); + } - public override WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo) - => testBeatmap; + protected override WorkingBeatmapCache CreateWorkingBeatmapCache(AudioManager audioManager, IResourceStore resources, IResourceStore storage, WorkingBeatmap defaultBeatmap, GameHost host) + { + return new TestWorkingBeatmapCache(this, audioManager, resources, storage, defaultBeatmap, host); + } + + private class TestWorkingBeatmapCache : WorkingBeatmapCache + { + private readonly TestBeatmapManager testBeatmapManager; + + public TestWorkingBeatmapCache(TestBeatmapManager testBeatmapManager, AudioManager audioManager, IResourceStore resourceStore, IResourceStore storage, WorkingBeatmap defaultBeatmap, GameHost gameHost) + : base(audioManager, resourceStore, storage, defaultBeatmap, gameHost) + { + this.testBeatmapManager = testBeatmapManager; + } + + public override WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo) + => testBeatmapManager.testBeatmap; + } + + internal class TestBeatmapModelManager : BeatmapModelManager + { + public TestBeatmapModelManager(Storage storage, IDatabaseContextFactory databaseContextFactory, RulesetStore rulesetStore, IAPIProvider apiProvider, GameHost gameHost) + : base(storage, databaseContextFactory, rulesetStore, apiProvider, gameHost) + { + } + + protected override string ComputeHash(BeatmapSetInfo item, ArchiveReader reader = null) + => string.Empty; + } public override void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null) { From 7a72747d886cc95e70c4abc94867527f2a6002e8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Sep 2021 17:14:35 +0900 Subject: [PATCH 2148/2442] Add back optional online lookups --- osu.Game/Beatmaps/BeatmapManager.cs | 8 +++++--- osu.Game/OsuGameBase.cs | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 18513945e5..6ffdfa24b5 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -42,9 +42,11 @@ namespace osu.Game.Beatmaps workingBeatmapCache.BeatmapManager = beatmapModelManager; - var onlineBeatmapLookupCache = new BeatmapOnlineLookupQueue(api, storage); - - beatmapModelManager.PopulateOnlineInformation = onlineBeatmapLookupCache.UpdateAsync; + if (performOnlineLookups) + { + var onlineBeatmapLookupCache = new BeatmapOnlineLookupQueue(api, storage); + beatmapModelManager.PopulateOnlineInformation = onlineBeatmapLookupCache.UpdateAsync; + } } protected virtual WorkingBeatmapCache CreateWorkingBeatmapCache(AudioManager audioManager, IResourceStore resources, IResourceStore storage, WorkingBeatmap defaultBeatmap, GameHost host) => diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index dc1cb7a850..e76436a75b 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -242,7 +242,7 @@ namespace osu.Game // ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup() dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Scheduler, Host, () => difficultyCache, LocalConfig)); - dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Resources, Host, defaultBeatmap)); + dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Resources, Host, defaultBeatmap, performOnlineLookups: true)); // this should likely be moved to ArchiveModelManager when another case appears where it is necessary // to have inter-dependent model managers. this could be obtained with an IHasForeign interface to From d2a8f35b4c5428118d306174cb72503dd2ff66c1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Sep 2021 17:54:52 +0900 Subject: [PATCH 2149/2442] Update styling --- .../Graphics/UserInterface/RollingCounter.cs | 8 ++-- .../HUD/DefaultPerformancePointsCounter.cs | 43 +++++++++++++++++-- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/RollingCounter.cs b/osu.Game/Graphics/UserInterface/RollingCounter.cs index 244658b75e..f03287e7de 100644 --- a/osu.Game/Graphics/UserInterface/RollingCounter.cs +++ b/osu.Game/Graphics/UserInterface/RollingCounter.cs @@ -25,7 +25,7 @@ namespace osu.Game.Graphics.UserInterface set => current.Current = value; } - private SpriteText displayedCountSpriteText; + private IHasText displayedCountSpriteText; /// /// If true, the roll-up duration will be proportional to change in value. @@ -72,10 +72,10 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load() { - displayedCountSpriteText = CreateSpriteText(); + displayedCountSpriteText = CreateText(); UpdateDisplay(); - Child = displayedCountSpriteText; + Child = (Drawable)displayedCountSpriteText; } protected void UpdateDisplay() @@ -160,6 +160,8 @@ namespace osu.Game.Graphics.UserInterface this.TransformTo(nameof(DisplayedCount), newValue, rollingTotalDuration, RollingEasing); } + protected virtual IHasText CreateText() => CreateSpriteText(); + protected virtual OsuSpriteText CreateSpriteText() => new OsuSpriteText { Font = OsuFont.Numeric.With(size: 40f), diff --git a/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs index 18bb621dd1..a7651187c2 100644 --- a/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs @@ -68,10 +68,9 @@ namespace osu.Game.Screens.Play.HUD Current.Value = (int)(ppProcessor?.Calculate() ?? 0); } - protected override LocalisableString FormatCount(int count) => $@"{count}pp"; + protected override LocalisableString FormatCount(int count) => count.ToString(@"D"); - protected override OsuSpriteText CreateSpriteText() - => base.CreateSpriteText().With(s => s.Font = s.Font.With(size: 20f)); + protected override IHasText CreateText() => new TextComponent(); protected override void Dispose(bool isDisposing) { @@ -81,6 +80,44 @@ namespace osu.Game.Screens.Play.HUD scoreProcessor.NewJudgement -= onNewJudgement; } + private class TextComponent : CompositeDrawable, IHasText + { + public LocalisableString Text + { + get => text.Text; + set => text.Text = value; + } + + private readonly OsuSpriteText text; + + public TextComponent() + { + AutoSizeAxes = Axes.Both; + + InternalChild = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(2), + Children = new Drawable[] + { + text = new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Font = OsuFont.Numeric.With(size: 16) + }, + new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Text = @"pp", + Font = OsuFont.Numeric.With(size: 8) + } + } + }; + } + } + private class GameplayWorkingBeatmap : WorkingBeatmap { private readonly GameplayBeatmap gameplayBeatmap; From 4d8418e0720a43f620381142c6b05a4e1ba95a69 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Sep 2021 17:54:56 +0900 Subject: [PATCH 2150/2442] Fix possible nullrefs --- .../HUD/DefaultPerformancePointsCounter.cs | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs index a7651187c2..563032b4ea 100644 --- a/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs @@ -5,9 +5,12 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio.Track; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Localisation; using osu.Game.Beatmaps; @@ -21,6 +24,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; +using osuTK; namespace osu.Game.Screens.Play.HUD { @@ -28,10 +32,12 @@ namespace osu.Game.Screens.Play.HUD { public bool UsesFixedAnchor { get; set; } - [Resolved] + [CanBeNull] + [Resolved(CanBeNull = true)] private ScoreProcessor scoreProcessor { get; set; } - [Resolved] + [CanBeNull] + [Resolved(CanBeNull = true)] private Player player { get; set; } private DifficultyCalculator.TimedDifficultyAttributes[] timedAttributes; @@ -47,18 +53,26 @@ namespace osu.Game.Screens.Play.HUD { Colour = colours.BlueLighter; - gameplayRuleset = player.GameplayRuleset; - timedAttributes = gameplayRuleset.CreateDifficultyCalculator(new GameplayWorkingBeatmap(player.GameplayBeatmap)).CalculateTimed(player.Mods.Value.ToArray()).ToArray(); + if (player != null) + { + gameplayRuleset = player.GameplayRuleset; + timedAttributes = gameplayRuleset.CreateDifficultyCalculator(new GameplayWorkingBeatmap(player.GameplayBeatmap)).CalculateTimed(player.Mods.Value.ToArray()).ToArray(); + } } protected override void LoadComplete() { base.LoadComplete(); - scoreProcessor.NewJudgement += onNewJudgement; + + if (scoreProcessor != null) + scoreProcessor.NewJudgement += onNewJudgement; } private void onNewJudgement(JudgementResult judgement) { + if (player == null) + return; + var attribIndex = Array.BinarySearch(timedAttributes, 0, timedAttributes.Length, new DifficultyCalculator.TimedDifficultyAttributes(judgement.HitObject.GetEndTime(), null)); if (attribIndex < 0) attribIndex = ~attribIndex - 1; From fab0d531bec9bf64687e927b937f27e861b4b7ea Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Sep 2021 17:55:00 +0900 Subject: [PATCH 2151/2442] Add counter to HUD --- osu.Game/Skinning/DefaultSkin.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index d3adae5c8c..41b7875cd1 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -68,6 +68,7 @@ namespace osu.Game.Skinning var score = container.OfType().FirstOrDefault(); var accuracy = container.OfType().FirstOrDefault(); var combo = container.OfType().FirstOrDefault(); + var ppCounter = container.OfType().FirstOrDefault(); if (score != null) { @@ -81,6 +82,13 @@ namespace osu.Game.Skinning score.Position = new Vector2(0, vertical_offset); + if (ppCounter != null) + { + ppCounter.Y = score.Position.Y + score.ScreenSpaceDrawQuad.Size.Y; + ppCounter.Origin = Anchor.TopCentre; + ppCounter.Anchor = Anchor.TopCentre; + } + if (accuracy != null) { accuracy.Position = new Vector2(-accuracy.ScreenSpaceDeltaToParentSpace(score.ScreenSpaceDrawQuad.Size).X / 2 - horizontal_padding, vertical_offset + 5); @@ -123,6 +131,7 @@ namespace osu.Game.Skinning new SongProgress(), new BarHitErrorMeter(), new BarHitErrorMeter(), + new DefaultPerformancePointsCounter() } }; From fd13142a158b50f89caab9a516617137970e3388 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Sep 2021 18:20:20 +0900 Subject: [PATCH 2152/2442] Add missing interface to `BeatmapManager` --- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 6ffdfa24b5..c72d1e8dec 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -29,7 +29,7 @@ namespace osu.Game.Beatmaps /// Handles general operations related to global beatmap management. /// [ExcludeFromDynamicCompile] - public class BeatmapManager : IModelDownloader, IModelFileManager, ICanAcceptFiles, IWorkingBeatmapCache + public class BeatmapManager : IModelDownloader, IModelManager, IModelFileManager, ICanAcceptFiles, IWorkingBeatmapCache { private readonly BeatmapModelManager beatmapModelManager; private readonly WorkingBeatmapCache workingBeatmapCache; From 0a00bc779542a3ce86c77a9970e14c8cfab06f34 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Sep 2021 17:42:12 +0900 Subject: [PATCH 2153/2442] Split out `IPostNotifications` into an interface --- osu.Game/Collections/CollectionManager.cs | 6 ++---- osu.Game/Database/ArchiveModelManager.cs | 3 --- osu.Game/Database/IModelManager.cs | 2 +- osu.Game/Database/IPostNotifications.cs | 16 ++++++++++++++++ 4 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 osu.Game/Database/IPostNotifications.cs diff --git a/osu.Game/Collections/CollectionManager.cs b/osu.Game/Collections/CollectionManager.cs index fe04c70d62..6f9d9cd8a8 100644 --- a/osu.Game/Collections/CollectionManager.cs +++ b/osu.Game/Collections/CollectionManager.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Beatmaps; +using osu.Game.Database; using osu.Game.IO; using osu.Game.IO.Legacy; using osu.Game.Overlays.Notifications; @@ -27,7 +28,7 @@ namespace osu.Game.Collections /// This is currently reading and writing from the osu-stable file format. This is a temporary arrangement until we refactor the /// database backing the game. Going forward writing should be done in a similar way to other model stores. /// - public class CollectionManager : Component + public class CollectionManager : Component, IPostNotifications { /// /// Database version in stable-compatible YYYYMMDD format. @@ -106,9 +107,6 @@ namespace osu.Game.Collections backgroundSave(); }); - /// - /// Set an endpoint for notifications to be posted to. - /// public Action PostNotification { protected get; set; } /// diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index fc217d3058..018b41ebc1 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -57,9 +57,6 @@ namespace osu.Game.Database /// private static readonly ThreadedTaskScheduler import_scheduler_low_priority = new ThreadedTaskScheduler(import_queue_request_concurrency, nameof(ArchiveModelManager)); - /// - /// Set an endpoint for notifications to be posted to. - /// public Action PostNotification { protected get; set; } /// diff --git a/osu.Game/Database/IModelManager.cs b/osu.Game/Database/IModelManager.cs index 8f0c6e1561..721de8f3a0 100644 --- a/osu.Game/Database/IModelManager.cs +++ b/osu.Game/Database/IModelManager.cs @@ -17,7 +17,7 @@ namespace osu.Game.Database /// Represents a model manager that publishes events when s are added or removed. /// /// The model type. - public interface IModelManager + public interface IModelManager : IPostNotifications where TModel : class { /// diff --git a/osu.Game/Database/IPostNotifications.cs b/osu.Game/Database/IPostNotifications.cs new file mode 100644 index 0000000000..d4fd64e79e --- /dev/null +++ b/osu.Game/Database/IPostNotifications.cs @@ -0,0 +1,16 @@ +// 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 osu.Game.Overlays.Notifications; + +namespace osu.Game.Database +{ + public interface IPostNotifications + { + /// + /// And action which will be fired when a notification should be presented to the user. + /// + public Action PostNotification { set; } + } +} From 3e3b9bc963583fa5f6ad3b822ad3e6e3818aa06c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Sep 2021 18:21:16 +0900 Subject: [PATCH 2154/2442] Split out `IModelDownloader` and also split apart `ScoreManager` --- ...eneOnlinePlayBeatmapAvailabilityTracker.cs | 25 +- osu.Game/Beatmaps/BeatmapManager.cs | 37 ++- osu.Game/Beatmaps/BeatmapModelDownloader.cs | 21 ++ osu.Game/Beatmaps/BeatmapModelManager.cs | 21 +- osu.Game/Database/ArchiveModelManager.cs | 20 +- osu.Game/Database/IModelDownloader.cs | 9 +- osu.Game/Database/IModelManager.cs | 12 + osu.Game/Database/IPresentImports.cs | 17 ++ ...hiveModelManager.cs => ModelDownloader.cs} | 47 ++-- osu.Game/Online/DownloadTrackingComposite.cs | 10 +- osu.Game/Scoring/ScoreManager.cs | 222 ++++++++++++------ osu.Game/Scoring/ScoreModelDownloader.cs | 20 ++ osu.Game/Scoring/ScoreModelManager.cs | 88 +++++++ .../Screens/Ranking/ReplayDownloadButton.cs | 2 +- osu.Game/Tests/Visual/EditorTestScene.cs | 2 +- 15 files changed, 403 insertions(+), 150 deletions(-) create mode 100644 osu.Game/Beatmaps/BeatmapModelDownloader.cs create mode 100644 osu.Game/Database/IPresentImports.cs rename osu.Game/Database/{DownloadableArchiveModelManager.cs => ModelDownloader.cs} (68%) create mode 100644 osu.Game/Scoring/ScoreModelDownloader.cs create mode 100644 osu.Game/Scoring/ScoreModelManager.cs diff --git a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs index 1a3f9e414d..d38294aba9 100644 --- a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs @@ -158,14 +158,30 @@ namespace osu.Game.Tests.Online public Task CurrentImportTask { get; private set; } + public TestBeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore resources, GameHost host = null, WorkingBeatmap defaultBeatmap = null) + : base(storage, contextFactory, rulesets, api, audioManager, resources, host, defaultBeatmap) + { + } + protected override BeatmapModelManager CreateBeatmapModelManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, GameHost host) { return new TestBeatmapModelManager(this, storage, contextFactory, rulesets, api, host); } - public TestBeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore resources, GameHost host = null, WorkingBeatmap defaultBeatmap = null) - : base(storage, contextFactory, rulesets, api, audioManager, resources, host, defaultBeatmap) + protected override BeatmapModelDownloader CreateBeatmapModelDownloader(BeatmapModelManager modelManager, IAPIProvider api, GameHost host) { + return new TestBeatmapModelDownloader(modelManager, api, host); + } + + internal class TestBeatmapModelDownloader : BeatmapModelDownloader + { + public TestBeatmapModelDownloader(BeatmapModelManager modelManager, IAPIProvider apiProvider, GameHost gameHost) + : base(modelManager, apiProvider, gameHost) + { + } + + protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) + => new TestDownloadRequest(set); } internal class TestBeatmapModelManager : BeatmapModelManager @@ -173,14 +189,11 @@ namespace osu.Game.Tests.Online private readonly TestBeatmapManager testBeatmapManager; public TestBeatmapModelManager(TestBeatmapManager testBeatmapManager, Storage storage, IDatabaseContextFactory databaseContextFactory, RulesetStore rulesetStore, IAPIProvider apiProvider, GameHost gameHost) - : base(storage, databaseContextFactory, rulesetStore, apiProvider, gameHost) + : base(storage, databaseContextFactory, rulesetStore, gameHost) { this.testBeatmapManager = testBeatmapManager; } - protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) - => new TestDownloadRequest(set); - public override async Task Import(BeatmapSetInfo item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default) { await testBeatmapManager.AllowImport.Task.ConfigureAwait(false); diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index c72d1e8dec..8dfd895987 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -32,12 +32,15 @@ namespace osu.Game.Beatmaps public class BeatmapManager : IModelDownloader, IModelManager, IModelFileManager, ICanAcceptFiles, IWorkingBeatmapCache { private readonly BeatmapModelManager beatmapModelManager; + private readonly BeatmapModelDownloader beatmapModelDownloader; + private readonly WorkingBeatmapCache workingBeatmapCache; public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore resources, GameHost host = null, WorkingBeatmap defaultBeatmap = null, bool performOnlineLookups = false) { beatmapModelManager = CreateBeatmapModelManager(storage, contextFactory, rulesets, api, host); + beatmapModelDownloader = CreateBeatmapModelDownloader(beatmapModelManager, api, host); workingBeatmapCache = CreateWorkingBeatmapCache(audioManager, resources, new FileStore(contextFactory, storage).Store, defaultBeatmap, host); workingBeatmapCache.BeatmapManager = beatmapModelManager; @@ -49,11 +52,16 @@ namespace osu.Game.Beatmaps } } + protected virtual BeatmapModelDownloader CreateBeatmapModelDownloader(BeatmapModelManager modelManager, IAPIProvider api, GameHost host) + { + return new BeatmapModelDownloader(modelManager, api, host); + } + protected virtual WorkingBeatmapCache CreateWorkingBeatmapCache(AudioManager audioManager, IResourceStore resources, IResourceStore storage, WorkingBeatmap defaultBeatmap, GameHost host) => new WorkingBeatmapCache(audioManager, resources, storage, defaultBeatmap, host); protected virtual BeatmapModelManager CreateBeatmapModelManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, GameHost host) => - new BeatmapModelManager(storage, contextFactory, rulesets, api, host); + new BeatmapModelManager(storage, contextFactory, rulesets, host); /// /// Create a new . @@ -156,7 +164,14 @@ namespace osu.Game.Beatmaps /// /// Fired when a notification should be presented to the user. /// - public Action PostNotification { set => beatmapModelManager.PostNotification = value; } + public Action PostNotification + { + set + { + beatmapModelManager.PostNotification = value; + beatmapModelDownloader.PostNotification = value; + } + } /// /// Fired when the user requests to view the resulting import. @@ -179,6 +194,11 @@ namespace osu.Game.Beatmaps #region Implementation of IModelManager + public bool IsAvailableLocally(BeatmapSetInfo model) + { + return beatmapModelManager.IsAvailableLocally(model); + } + public IBindable> ItemUpdated => beatmapModelManager.ItemUpdated; public IBindable> ItemRemoved => beatmapModelManager.ItemRemoved; @@ -227,23 +247,18 @@ namespace osu.Game.Beatmaps #region Implementation of IModelDownloader - public IBindable>> DownloadBegan => beatmapModelManager.DownloadBegan; + public IBindable>> DownloadBegan => beatmapModelDownloader.DownloadBegan; - public IBindable>> DownloadFailed => beatmapModelManager.DownloadFailed; - - public bool IsAvailableLocally(BeatmapSetInfo model) - { - return beatmapModelManager.IsAvailableLocally(model); - } + public IBindable>> DownloadFailed => beatmapModelDownloader.DownloadFailed; public bool Download(BeatmapSetInfo model, bool minimiseDownloadSize = false) { - return beatmapModelManager.Download(model, minimiseDownloadSize); + return beatmapModelDownloader.Download(model, minimiseDownloadSize); } public ArchiveDownloadRequest GetExistingDownload(BeatmapSetInfo model) { - return beatmapModelManager.GetExistingDownload(model); + return beatmapModelDownloader.GetExistingDownload(model); } #endregion diff --git a/osu.Game/Beatmaps/BeatmapModelDownloader.cs b/osu.Game/Beatmaps/BeatmapModelDownloader.cs new file mode 100644 index 0000000000..ae482eeafd --- /dev/null +++ b/osu.Game/Beatmaps/BeatmapModelDownloader.cs @@ -0,0 +1,21 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Platform; +using osu.Game.Database; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; + +namespace osu.Game.Beatmaps +{ + public class BeatmapModelDownloader : ModelDownloader + { + protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) => + new DownloadBeatmapSetRequest(set, minimiseDownloadSize); + + public BeatmapModelDownloader(BeatmapModelManager beatmapModelManager, IAPIProvider api, GameHost host = null) + : base(beatmapModelManager, api, host) + { + } + } +} diff --git a/osu.Game/Beatmaps/BeatmapModelManager.cs b/osu.Game/Beatmaps/BeatmapModelManager.cs index be3adc412c..1b6694b1b4 100644 --- a/osu.Game/Beatmaps/BeatmapModelManager.cs +++ b/osu.Game/Beatmaps/BeatmapModelManager.cs @@ -21,8 +21,6 @@ using osu.Game.Beatmaps.Formats; using osu.Game.Database; using osu.Game.IO; using osu.Game.IO.Archives; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; using osu.Game.Skinning; @@ -34,7 +32,7 @@ namespace osu.Game.Beatmaps /// Handles ef-core storage of beatmaps. /// [ExcludeFromDynamicCompile] - public class BeatmapModelManager : DownloadableArchiveModelManager + public class BeatmapModelManager : ArchiveModelManager { /// /// Fired when a single difficulty has been hidden. @@ -72,8 +70,8 @@ namespace osu.Game.Beatmaps private readonly BeatmapStore beatmaps; private readonly RulesetStore rulesets; - public BeatmapModelManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, GameHost host = null) - : base(storage, contextFactory, api, new BeatmapStore(contextFactory), host) + public BeatmapModelManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, GameHost host = null) + : base(storage, contextFactory, new BeatmapStore(contextFactory), host) { this.rulesets = rulesets; @@ -84,9 +82,6 @@ namespace osu.Game.Beatmaps beatmaps.ItemUpdated += obj => WorkingBeatmapCache?.Invalidate(obj); } - protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) => - new DownloadBeatmapSetRequest(set, minimiseDownloadSize); - protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == ".osz"; protected override async Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive, CancellationToken cancellationToken = default) @@ -176,10 +171,6 @@ namespace osu.Game.Beatmaps void resetIds() => beatmapSet.Beatmaps.ForEach(b => b.OnlineBeatmapID = null); } - protected override bool CheckLocalAvailability(BeatmapSetInfo model, IQueryable items) - => base.CheckLocalAvailability(model, items) - || (model.OnlineBeatmapSetID != null && items.Any(b => b.OnlineBeatmapSetID == model.OnlineBeatmapSetID)); - /// /// Delete a beatmap difficulty. /// @@ -347,7 +338,11 @@ namespace osu.Game.Beatmaps /// Results from the provided query. public IQueryable QueryBeatmaps(Expression> query) => beatmaps.Beatmaps.AsNoTracking().Where(query); - protected override string HumanisedModelName => "beatmap"; + public override string HumanisedModelName => "beatmap"; + + protected override bool CheckLocalAvailability(BeatmapSetInfo model, IQueryable items) + => base.CheckLocalAvailability(model, items) + || (model.OnlineBeatmapSetID != null && items.Any(b => b.OnlineBeatmapSetID == model.OnlineBeatmapSetID)); protected override BeatmapSetInfo CreateModel(ArchiveReader reader) { diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 018b41ebc1..0c309bbddb 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -30,7 +30,7 @@ namespace osu.Game.Database /// /// The model type. /// The associated file join type. - public abstract class ArchiveModelManager : ICanAcceptFiles, IModelManager, IModelFileManager + public abstract class ArchiveModelManager : ICanAcceptFiles, IModelManager, IModelFileManager, IPresentImports where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete where TFileModel : class, INamedFileInfo, new() { @@ -249,10 +249,7 @@ namespace osu.Game.Database return import; } - /// - /// Fired when the user requests to view the resulting import. - /// - public Action> PresentImport; + public Action> PresentImport { protected get; set; } /// /// Silently import an item from an . @@ -799,6 +796,17 @@ namespace osu.Game.Database /// An existing model which matches the criteria to skip importing, else null. protected TModel CheckForExisting(TModel model) => model.Hash == null ? null : ModelStore.ConsumableItems.FirstOrDefault(b => b.Hash == model.Hash); + public bool IsAvailableLocally(TModel model) => CheckLocalAvailability(model, ModelStore.ConsumableItems.Where(m => !m.DeletePending)); + + /// + /// Performs implementation specific comparisons to determine whether a given model is present in the local store. + /// + /// The whose existence needs to be checked. + /// The usable items present in the store. + /// Whether the exists. + protected virtual bool CheckLocalAvailability(TModel model, IQueryable items) + => model.ID > 0 && items.Any(i => i.ID == model.ID && i.Files.Any()); + /// /// Whether import can be skipped after finding an existing import early in the process. /// Only valid when is not overridden. @@ -835,7 +843,7 @@ namespace osu.Game.Database private DbSet queryModel() => ContextFactory.Get().Set(); - protected virtual string HumanisedModelName => $"{typeof(TModel).Name.Replace(@"Info", "").ToLower()}"; + public virtual string HumanisedModelName => $"{typeof(TModel).Name.Replace(@"Info", "").ToLower()}"; #region Event handling / delaying diff --git a/osu.Game/Database/IModelDownloader.cs b/osu.Game/Database/IModelDownloader.cs index 0cb633280e..a5573b2190 100644 --- a/osu.Game/Database/IModelDownloader.cs +++ b/osu.Game/Database/IModelDownloader.cs @@ -11,7 +11,7 @@ namespace osu.Game.Database /// Represents a that can download new models from an external source. /// /// The model type. - public interface IModelDownloader : IModelManager + public interface IModelDownloader : IPostNotifications where TModel : class { /// @@ -26,13 +26,6 @@ namespace osu.Game.Database /// IBindable>> DownloadFailed { get; } - /// - /// Checks whether a given is already available in the local store. - /// - /// The whose existence needs to be checked. - /// Whether the exists. - bool IsAvailableLocally(TModel model); - /// /// Begin a download for the requested . /// diff --git a/osu.Game/Database/IModelManager.cs b/osu.Game/Database/IModelManager.cs index 721de8f3a0..7bfc8dbee3 100644 --- a/osu.Game/Database/IModelManager.cs +++ b/osu.Game/Database/IModelManager.cs @@ -123,5 +123,17 @@ namespace osu.Game.Database /// Whether this is a low priority import. /// An optional cancellation token. Task Import(TModel item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default); + + /// + /// Checks whether a given is already available in the local store. + /// + /// The whose existence needs to be checked. + /// Whether the exists. + bool IsAvailableLocally(TModel model); + + /// + /// A user displayable name for the model type associated with this manager. + /// + string HumanisedModelName => $"{typeof(TModel).Name.Replace(@"Info", "").ToLower()}"; } } diff --git a/osu.Game/Database/IPresentImports.cs b/osu.Game/Database/IPresentImports.cs new file mode 100644 index 0000000000..39b495ebd5 --- /dev/null +++ b/osu.Game/Database/IPresentImports.cs @@ -0,0 +1,17 @@ +// 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; + +namespace osu.Game.Database +{ + public interface IPresentImports + where TModel : class + { + /// + /// Fired when the user requests to view the resulting import. + /// + public Action> PresentImport { set; } + } +} diff --git a/osu.Game/Database/DownloadableArchiveModelManager.cs b/osu.Game/Database/ModelDownloader.cs similarity index 68% rename from osu.Game/Database/DownloadableArchiveModelManager.cs rename to osu.Game/Database/ModelDownloader.cs index e6d5b44b65..e613b39b6b 100644 --- a/osu.Game/Database/DownloadableArchiveModelManager.cs +++ b/osu.Game/Database/ModelDownloader.cs @@ -1,29 +1,24 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using Humanizer; -using osu.Framework.Logging; -using osu.Framework.Platform; -using osu.Game.Online.API; -using osu.Game.Overlays.Notifications; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Humanizer; using osu.Framework.Bindables; +using osu.Framework.Logging; +using osu.Framework.Platform; +using osu.Game.Online.API; +using osu.Game.Overlays.Notifications; namespace osu.Game.Database { - /// - /// An that has the ability to download models using an and - /// import them into the store. - /// - /// The model type. - /// The associated file join type. - public abstract class DownloadableArchiveModelManager : ArchiveModelManager, IModelDownloader - where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete, IEquatable - where TFileModel : class, INamedFileInfo, new() + public abstract class ModelDownloader : IModelDownloader + where TModel : class, IHasPrimaryKey, ISoftDelete, IEquatable { + public Action PostNotification { protected get; set; } + public IBindable>> DownloadBegan => downloadBegan; private readonly Bindable>> downloadBegan = new Bindable>>(); @@ -32,18 +27,15 @@ namespace osu.Game.Database private readonly Bindable>> downloadFailed = new Bindable>>(); + private readonly IModelManager modelManager; private readonly IAPIProvider api; private readonly List> currentDownloads = new List>(); - private readonly MutableDatabaseBackedStoreWithFileIncludes modelStore; - - protected DownloadableArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, IAPIProvider api, MutableDatabaseBackedStoreWithFileIncludes modelStore, - IIpcHost importHost = null) - : base(storage, contextFactory, modelStore, importHost) + protected ModelDownloader(IModelManager modelManager, IAPIProvider api, IIpcHost importHost = null) { + this.modelManager = modelManager; this.api = api; - this.modelStore = modelStore; } /// @@ -76,7 +68,7 @@ namespace osu.Game.Database Task.Factory.StartNew(async () => { // This gets scheduled back to the update thread, but we want the import to run in the background. - var imported = await Import(notification, new ImportTask(filename)).ConfigureAwait(false); + var imported = await modelManager.Import(notification, new ImportTask(filename)).ConfigureAwait(false); // for now a failed import will be marked as a failed download for simplicity. if (!imported.Any()) @@ -111,21 +103,10 @@ namespace osu.Game.Database notification.State = ProgressNotificationState.Cancelled; if (!(error is OperationCanceledException)) - Logger.Error(error, $"{HumanisedModelName.Titleize()} download failed!"); + Logger.Error(error, $"{modelManager.HumanisedModelName.Titleize()} download failed!"); } } - public bool IsAvailableLocally(TModel model) => CheckLocalAvailability(model, modelStore.ConsumableItems.Where(m => !m.DeletePending)); - - /// - /// Performs implementation specific comparisons to determine whether a given model is present in the local store. - /// - /// The whose existence needs to be checked. - /// The usable items present in the store. - /// Whether the exists. - protected virtual bool CheckLocalAvailability(TModel model, IQueryable items) - => model.ID > 0 && items.Any(i => i.ID == model.ID && i.Files.Any()); - public ArchiveDownloadRequest GetExistingDownload(TModel model) => currentDownloads.Find(r => r.Model.Equals(model)); private bool canDownload(TModel model) => GetExistingDownload(model) == null && api != null; diff --git a/osu.Game/Online/DownloadTrackingComposite.cs b/osu.Game/Online/DownloadTrackingComposite.cs index d9599481e7..2a96051427 100644 --- a/osu.Game/Online/DownloadTrackingComposite.cs +++ b/osu.Game/Online/DownloadTrackingComposite.cs @@ -16,7 +16,7 @@ namespace osu.Game.Online /// public abstract class DownloadTrackingComposite : CompositeDrawable where TModel : class, IEquatable - where TModelManager : class, IModelDownloader + where TModelManager : class, IModelDownloader, IModelManager { protected readonly Bindable Model = new Bindable(); @@ -35,7 +35,7 @@ namespace osu.Game.Online Model.Value = model; } - private IBindable> managedUpdated; + private IBindable> managerUpdated; private IBindable> managerRemoved; private IBindable>> managerDownloadBegan; private IBindable>> managerDownloadFailed; @@ -60,8 +60,8 @@ namespace osu.Game.Online managerDownloadBegan.BindValueChanged(downloadBegan); managerDownloadFailed = Manager.DownloadFailed.GetBoundCopy(); managerDownloadFailed.BindValueChanged(downloadFailed); - managedUpdated = Manager.ItemUpdated.GetBoundCopy(); - managedUpdated.BindValueChanged(itemUpdated); + managerUpdated = Manager.ItemUpdated.GetBoundCopy(); + managerUpdated.BindValueChanged(itemUpdated); managerRemoved = Manager.ItemRemoved.GetBoundCopy(); managerRemoved.BindValueChanged(itemRemoved); } @@ -77,7 +77,7 @@ namespace osu.Game.Online /// /// Whether the given model is available in the database. - /// By default, this calls , + /// By default, this calls , /// but can be overriden to add additional checks for verifying the model in database. /// protected virtual bool IsModelAvailableLocally() => Manager?.IsAvailableLocally(Model.Value) == true; diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 56c346d177..d83b4e3f1d 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -9,102 +9,48 @@ using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore; using osu.Framework.Bindables; -using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Database; +using osu.Game.IO; using osu.Game.IO.Archives; using osu.Game.Online.API; -using osu.Game.Online.API.Requests; +using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; -using osu.Game.Scoring.Legacy; namespace osu.Game.Scoring { - public class ScoreManager : DownloadableArchiveModelManager + public class ScoreManager : IModelManager, IModelFileManager, IModelDownloader, ICanAcceptFiles, IPresentImports { - public override IEnumerable HandledExtensions => new[] { ".osr" }; - - protected override string[] HashableFileTypes => new[] { ".osr" }; - - protected override string ImportFromStablePath => Path.Combine("Data", "r"); - - private readonly RulesetStore rulesets; - private readonly Func beatmaps; private readonly Scheduler scheduler; - - [CanBeNull] private readonly Func difficulties; - - [CanBeNull] private readonly OsuConfigManager configManager; + private readonly ScoreModelManager scoreModelManager; + private readonly ScoreModelDownloader scoreModelDownloader; public ScoreManager(RulesetStore rulesets, Func beatmaps, Storage storage, IAPIProvider api, IDatabaseContextFactory contextFactory, Scheduler scheduler, IIpcHost importHost = null, Func difficulties = null, OsuConfigManager configManager = null) - : base(storage, contextFactory, api, new ScoreStore(contextFactory, storage), importHost) { - this.rulesets = rulesets; - this.beatmaps = beatmaps; this.scheduler = scheduler; this.difficulties = difficulties; this.configManager = configManager; + + scoreModelManager = new ScoreModelManager(rulesets, beatmaps, storage, contextFactory, importHost); + scoreModelDownloader = new ScoreModelDownloader(scoreModelManager, api, importHost); } - protected override ScoreInfo CreateModel(ArchiveReader archive) - { - if (archive == null) - return null; + public Score GetScore(ScoreInfo score) => scoreModelManager.GetScore(score); - using (var stream = archive.GetStream(archive.Filenames.First(f => f.EndsWith(".osr", StringComparison.OrdinalIgnoreCase)))) - { - try - { - return new DatabasedLegacyScoreDecoder(rulesets, beatmaps()).Parse(stream).ScoreInfo; - } - catch (LegacyScoreDecoder.BeatmapNotFoundException e) - { - Logger.Log(e.Message, LoggingTarget.Information, LogLevel.Error); - return null; - } - } - } + public List GetAllUsableScores() => scoreModelManager.GetAllUsableScores(); - protected override Task Populate(ScoreInfo model, ArchiveReader archive, CancellationToken cancellationToken = default) - => Task.CompletedTask; + public IEnumerable QueryScores(Expression> query) => scoreModelManager.QueryScores(query); - public override void ExportModelTo(ScoreInfo model, Stream outputStream) - { - var file = model.Files.SingleOrDefault(); - if (file == null) - return; - - using (var inputStream = Files.Storage.GetStream(file.FileInfo.StoragePath)) - inputStream.CopyTo(outputStream); - } - - protected override IEnumerable GetStableImportPaths(Storage storage) - => storage.GetFiles(ImportFromStablePath).Where(p => HandledExtensions.Any(ext => Path.GetExtension(p)?.Equals(ext, StringComparison.OrdinalIgnoreCase) ?? false)) - .Select(path => storage.GetFullPath(path)); - - public Score GetScore(ScoreInfo score) => new LegacyDatabasedScore(score, rulesets, beatmaps(), Files.Store); - - public List GetAllUsableScores() => ModelStore.ConsumableItems.Where(s => !s.DeletePending).ToList(); - - public IEnumerable QueryScores(Expression> query) => ModelStore.ConsumableItems.AsNoTracking().Where(query); - - public ScoreInfo Query(Expression> query) => ModelStore.ConsumableItems.AsNoTracking().FirstOrDefault(query); - - protected override ArchiveDownloadRequest CreateDownloadRequest(ScoreInfo score, bool minimiseDownload) => new DownloadReplayRequest(score); - - protected override bool CheckLocalAvailability(ScoreInfo model, IQueryable items) - => base.CheckLocalAvailability(model, items) - || (model.OnlineScoreID != null && items.Any(i => i.OnlineScoreID == model.OnlineScoreID)); + public ScoreInfo Query(Expression> query) => scoreModelManager.Query(query); /// /// Orders an array of s by total score. @@ -281,5 +227,149 @@ namespace osu.Game.Scoring this.totalScore.BindValueChanged(v => Value = v.NewValue.ToString("N0"), true); } } + + #region Implementation of IPostNotifications + + public Action PostNotification + { + set + { + scoreModelManager.PostNotification = value; + scoreModelDownloader.PostNotification = value; + } + } + + #endregion + + #region Implementation of IModelManager + + public IBindable> ItemUpdated => scoreModelManager.ItemUpdated; + + public IBindable> ItemRemoved => scoreModelManager.ItemRemoved; + + public Task ImportFromStableAsync(StableStorage stableStorage) + { + return scoreModelManager.ImportFromStableAsync(stableStorage); + } + + public void Export(ScoreInfo item) + { + scoreModelManager.Export(item); + } + + public void ExportModelTo(ScoreInfo model, Stream outputStream) + { + scoreModelManager.ExportModelTo(model, outputStream); + } + + public void Update(ScoreInfo item) + { + scoreModelManager.Update(item); + } + + public bool Delete(ScoreInfo item) + { + return scoreModelManager.Delete(item); + } + + public void Delete(List items, bool silent = false) + { + scoreModelManager.Delete(items, silent); + } + + public void Undelete(List items, bool silent = false) + { + scoreModelManager.Undelete(items, silent); + } + + public void Undelete(ScoreInfo item) + { + scoreModelManager.Undelete(item); + } + + public Task Import(params string[] paths) + { + return scoreModelManager.Import(paths); + } + + public Task Import(params ImportTask[] tasks) + { + return scoreModelManager.Import(tasks); + } + + public IEnumerable HandledExtensions => scoreModelManager.HandledExtensions; + + public Task> Import(ProgressNotification notification, params ImportTask[] tasks) + { + return scoreModelManager.Import(notification, tasks); + } + + public Task Import(ImportTask task, bool lowPriority = false, CancellationToken cancellationToken = default) + { + return scoreModelManager.Import(task, lowPriority, cancellationToken); + } + + public Task Import(ArchiveReader archive, bool lowPriority = false, CancellationToken cancellationToken = default) + { + return scoreModelManager.Import(archive, lowPriority, cancellationToken); + } + + public Task Import(ScoreInfo item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default) + { + return scoreModelManager.Import(item, archive, lowPriority, cancellationToken); + } + + public bool IsAvailableLocally(ScoreInfo model) + { + return scoreModelManager.IsAvailableLocally(model); + } + + #endregion + + #region Implementation of IModelFileManager + + public void ReplaceFile(ScoreInfo model, ScoreFileInfo file, Stream contents, string filename = null) + { + scoreModelManager.ReplaceFile(model, file, contents, filename); + } + + public void DeleteFile(ScoreInfo model, ScoreFileInfo file) + { + scoreModelManager.DeleteFile(model, file); + } + + public void AddFile(ScoreInfo model, Stream contents, string filename) + { + scoreModelManager.AddFile(model, contents, filename); + } + + #endregion + + #region Implementation of IModelDownloader + + public IBindable>> DownloadBegan => scoreModelDownloader.DownloadBegan; + + public IBindable>> DownloadFailed => scoreModelDownloader.DownloadFailed; + + public bool Download(ScoreInfo model, bool minimiseDownloadSize) + { + return scoreModelDownloader.Download(model, minimiseDownloadSize); + } + + public ArchiveDownloadRequest GetExistingDownload(ScoreInfo model) + { + return scoreModelDownloader.GetExistingDownload(model); + } + + #endregion + + #region Implementation of IPresentImports + + public Action> PresentImport + { + set => scoreModelManager.PresentImport = value; + } + + #endregion } } diff --git a/osu.Game/Scoring/ScoreModelDownloader.cs b/osu.Game/Scoring/ScoreModelDownloader.cs new file mode 100644 index 0000000000..b3c1e2928a --- /dev/null +++ b/osu.Game/Scoring/ScoreModelDownloader.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Platform; +using osu.Game.Database; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; + +namespace osu.Game.Scoring +{ + public class ScoreModelDownloader : ModelDownloader + { + public ScoreModelDownloader(ScoreModelManager scoreManager, IAPIProvider api, IIpcHost importHost = null) + : base(scoreManager, api, importHost) + { + } + + protected override ArchiveDownloadRequest CreateDownloadRequest(ScoreInfo score, bool minimiseDownload) => new DownloadReplayRequest(score); + } +} diff --git a/osu.Game/Scoring/ScoreModelManager.cs b/osu.Game/Scoring/ScoreModelManager.cs new file mode 100644 index 0000000000..c65a6acdfb --- /dev/null +++ b/osu.Game/Scoring/ScoreModelManager.cs @@ -0,0 +1,88 @@ +// 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.IO; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using osu.Framework.Logging; +using osu.Framework.Platform; +using osu.Game.Beatmaps; +using osu.Game.Database; +using osu.Game.IO.Archives; +using osu.Game.Rulesets; +using osu.Game.Scoring.Legacy; + +namespace osu.Game.Scoring +{ + public class ScoreModelManager : ArchiveModelManager + { + public override IEnumerable HandledExtensions => new[] { ".osr" }; + + protected override string[] HashableFileTypes => new[] { ".osr" }; + + protected override string ImportFromStablePath => Path.Combine("Data", "r"); + + private readonly RulesetStore rulesets; + private readonly Func beatmaps; + + public ScoreModelManager(RulesetStore rulesets, Func beatmaps, Storage storage, IDatabaseContextFactory contextFactory, IIpcHost importHost = null) + : base(storage, contextFactory, new ScoreStore(contextFactory, storage), importHost) + { + this.rulesets = rulesets; + this.beatmaps = beatmaps; + } + + protected override ScoreInfo CreateModel(ArchiveReader archive) + { + if (archive == null) + return null; + + using (var stream = archive.GetStream(archive.Filenames.First(f => f.EndsWith(".osr", StringComparison.OrdinalIgnoreCase)))) + { + try + { + return new DatabasedLegacyScoreDecoder(rulesets, beatmaps()).Parse(stream).ScoreInfo; + } + catch (LegacyScoreDecoder.BeatmapNotFoundException e) + { + Logger.Log(e.Message, LoggingTarget.Information, LogLevel.Error); + return null; + } + } + } + + public Score GetScore(ScoreInfo score) => new LegacyDatabasedScore(score, rulesets, beatmaps(), Files.Store); + + public List GetAllUsableScores() => ModelStore.ConsumableItems.Where(s => !s.DeletePending).ToList(); + + public IEnumerable QueryScores(Expression> query) => ModelStore.ConsumableItems.AsNoTracking().Where(query); + + public ScoreInfo Query(Expression> query) => ModelStore.ConsumableItems.AsNoTracking().FirstOrDefault(query); + + protected override Task Populate(ScoreInfo model, ArchiveReader archive, CancellationToken cancellationToken = default) + => Task.CompletedTask; + + protected override bool CheckLocalAvailability(ScoreInfo model, IQueryable items) + => base.CheckLocalAvailability(model, items) + || (model.OnlineScoreID != null && items.Any(i => i.OnlineScoreID == model.OnlineScoreID)); + + public override void ExportModelTo(ScoreInfo model, Stream outputStream) + { + var file = model.Files.SingleOrDefault(); + if (file == null) + return; + + using (var inputStream = Files.Storage.GetStream(file.FileInfo.StoragePath)) + inputStream.CopyTo(outputStream); + } + + protected override IEnumerable GetStableImportPaths(Storage storage) + => storage.GetFiles(ImportFromStablePath).Where(p => HandledExtensions.Any(ext => Path.GetExtension(p)?.Equals(ext, StringComparison.OrdinalIgnoreCase) ?? false)) + .Select(path => storage.GetFullPath(path)); + } +} diff --git a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs index 18b8649a59..d96b6989b4 100644 --- a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs +++ b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs @@ -40,7 +40,7 @@ namespace osu.Game.Screens.Ranking } [BackgroundDependencyLoader(true)] - private void load(OsuGame game, ScoreManager scores) + private void load(OsuGame game, ScoreModelDownloader scores) { InternalChild = shakeContainer = new ShakeContainer { diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index ac8773a840..798b0d01ee 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -150,7 +150,7 @@ namespace osu.Game.Tests.Visual internal class TestBeatmapModelManager : BeatmapModelManager { public TestBeatmapModelManager(Storage storage, IDatabaseContextFactory databaseContextFactory, RulesetStore rulesetStore, IAPIProvider apiProvider, GameHost gameHost) - : base(storage, databaseContextFactory, rulesetStore, apiProvider, gameHost) + : base(storage, databaseContextFactory, rulesetStore, gameHost) { } From c05a8fc4a2ec1f598a6e317974d9293c34d780b6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Sep 2021 18:52:09 +0900 Subject: [PATCH 2155/2442] Split importer interface out of `IModelManager` --- osu.Game/Database/IModelImporter.cs | 65 +++++++++++++++++++++++++++++ osu.Game/Database/IModelManager.cs | 51 +--------------------- 2 files changed, 66 insertions(+), 50 deletions(-) create mode 100644 osu.Game/Database/IModelImporter.cs diff --git a/osu.Game/Database/IModelImporter.cs b/osu.Game/Database/IModelImporter.cs new file mode 100644 index 0000000000..fa3b4d9152 --- /dev/null +++ b/osu.Game/Database/IModelImporter.cs @@ -0,0 +1,65 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using osu.Game.IO.Archives; +using osu.Game.Overlays.Notifications; + +namespace osu.Game.Database +{ + /// + /// A class which handles importing of asociated models to the game store. + /// + /// The model type. + public interface IModelImporter : IPostNotifications + where TModel : class + { + /// + /// Import one or more items from filesystem . + /// + /// + /// This will be treated as a low priority import if more than one path is specified; use to always import at standard priority. + /// This will post notifications tracking progress. + /// + /// One or more archive locations on disk. + Task Import(params string[] paths); + + Task Import(params ImportTask[] tasks); + + Task> Import(ProgressNotification notification, params ImportTask[] tasks); + + /// + /// Import one from the filesystem and delete the file on success. + /// Note that this bypasses the UI flow and should only be used for special cases or testing. + /// + /// The containing data about the to import. + /// Whether this is a low priority import. + /// An optional cancellation token. + /// The imported model, if successful. + Task Import(ImportTask task, bool lowPriority = false, CancellationToken cancellationToken = default); + + /// + /// Silently import an item from an . + /// + /// The archive to be imported. + /// Whether this is a low priority import. + /// An optional cancellation token. + Task Import(ArchiveReader archive, bool lowPriority = false, CancellationToken cancellationToken = default); + + /// + /// Silently import an item from a . + /// + /// The model to be imported. + /// An optional archive to use for model population. + /// Whether this is a low priority import. + /// An optional cancellation token. + Task Import(TModel item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default); + + /// + /// A user displayable name for the model type associated with this manager. + /// + string HumanisedModelName => $"{typeof(TModel).Name.Replace(@"Info", "").ToLower()}"; + } +} diff --git a/osu.Game/Database/IModelManager.cs b/osu.Game/Database/IModelManager.cs index 7bfc8dbee3..2b1e574176 100644 --- a/osu.Game/Database/IModelManager.cs +++ b/osu.Game/Database/IModelManager.cs @@ -4,12 +4,9 @@ using System; using System.Collections.Generic; using System.IO; -using System.Threading; using System.Threading.Tasks; using osu.Framework.Bindables; using osu.Game.IO; -using osu.Game.IO.Archives; -using osu.Game.Overlays.Notifications; namespace osu.Game.Database { @@ -17,7 +14,7 @@ namespace osu.Game.Database /// Represents a model manager that publishes events when s are added or removed. /// /// The model type. - public interface IModelManager : IPostNotifications + public interface IModelManager : IModelImporter, IPostNotifications where TModel : class { /// @@ -83,57 +80,11 @@ namespace osu.Game.Database /// The item to restore void Undelete(TModel item); - /// - /// Import one or more items from filesystem . - /// - /// - /// This will be treated as a low priority import if more than one path is specified; use to always import at standard priority. - /// This will post notifications tracking progress. - /// - /// One or more archive locations on disk. - Task Import(params string[] paths); - - Task Import(params ImportTask[] tasks); - - Task> Import(ProgressNotification notification, params ImportTask[] tasks); - - /// - /// Import one from the filesystem and delete the file on success. - /// Note that this bypasses the UI flow and should only be used for special cases or testing. - /// - /// The containing data about the to import. - /// Whether this is a low priority import. - /// An optional cancellation token. - /// The imported model, if successful. - Task Import(ImportTask task, bool lowPriority = false, CancellationToken cancellationToken = default); - - /// - /// Silently import an item from an . - /// - /// The archive to be imported. - /// Whether this is a low priority import. - /// An optional cancellation token. - Task Import(ArchiveReader archive, bool lowPriority = false, CancellationToken cancellationToken = default); - - /// - /// Silently import an item from a . - /// - /// The model to be imported. - /// An optional archive to use for model population. - /// Whether this is a low priority import. - /// An optional cancellation token. - Task Import(TModel item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default); - /// /// Checks whether a given is already available in the local store. /// /// The whose existence needs to be checked. /// Whether the exists. bool IsAvailableLocally(TModel model); - - /// - /// A user displayable name for the model type associated with this manager. - /// - string HumanisedModelName => $"{typeof(TModel).Name.Replace(@"Info", "").ToLower()}"; } } From 66409147dc3ff3cc3eac9afa10841ef35e6eef98 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Sep 2021 19:25:08 +0900 Subject: [PATCH 2156/2442] Remove duplicate interface specification --- osu.Game/Database/IModelManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/IModelManager.cs b/osu.Game/Database/IModelManager.cs index 2b1e574176..f5e401cdfb 100644 --- a/osu.Game/Database/IModelManager.cs +++ b/osu.Game/Database/IModelManager.cs @@ -14,7 +14,7 @@ namespace osu.Game.Database /// Represents a model manager that publishes events when s are added or removed. /// /// The model type. - public interface IModelManager : IModelImporter, IPostNotifications + public interface IModelManager : IModelImporter where TModel : class { /// From a2e61883e3b00dc205ecefc5870d9d2343ced7bd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Sep 2021 19:33:12 +0900 Subject: [PATCH 2157/2442] Initial push to use `ILive` in import process --- .../Beatmaps/IO/ImportBeatmapTest.cs | 34 +++++++-------- ...eneOnlinePlayBeatmapAvailabilityTracker.cs | 4 +- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 2 +- .../Skins/TestSceneBeatmapSkinResources.cs | 2 +- .../Skins/TestSceneSkinResources.cs | 2 +- .../Menus/TestSceneMusicActionHandling.cs | 2 +- .../Navigation/TestScenePresentBeatmap.cs | 2 +- .../Navigation/TestScenePresentScore.cs | 4 +- .../TestScenePlaylistsRoomSubScreen.cs | 2 +- .../TestSceneBeatmapRecommendations.cs | 2 +- .../SongSelect/TestScenePlaySongSelect.cs | 2 +- .../TestSceneDeleteLocalScore.cs | 4 +- osu.Game/Beatmaps/BeatmapManager.cs | 15 +++---- osu.Game/Database/ArchiveModelManager.cs | 26 ++++++------ osu.Game/Database/EntityFrameworkLive.cs | 34 +++++++++++++++ .../Database/EntityFrameworkLiveExtensions.cs | 14 +++++++ osu.Game/Database/ILive.cs | 42 +++++++++++++++++++ osu.Game/Database/IModelImporter.cs | 8 ++-- osu.Game/Database/IPresentImports.cs | 2 +- osu.Game/FodyWeavers.xml | 3 ++ osu.Game/OsuGame.cs | 4 +- osu.Game/Scoring/ScoreManager.cs | 10 ++--- osu.Game/Screens/Menu/IntroScreen.cs | 8 +++- osu.Game/Skinning/SkinManager.cs | 2 +- 24 files changed, 164 insertions(+), 66 deletions(-) create mode 100644 osu.Game/Database/EntityFrameworkLive.cs create mode 100644 osu.Game/Database/EntityFrameworkLiveExtensions.cs create mode 100644 osu.Game/Database/ILive.cs create mode 100644 osu.Game/FodyWeavers.xml diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index cba7f34ede..fc2a3792cb 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -86,7 +86,7 @@ namespace osu.Game.Tests.Beatmaps.IO var manager = osu.Dependencies.Get(); - BeatmapSetInfo importedSet; + ILive importedSet; using (var stream = File.OpenRead(tempPath)) { @@ -97,7 +97,7 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.IsTrue(File.Exists(tempPath), "Stream source file somehow went missing"); File.Delete(tempPath); - var imported = manager.GetAllUsableBeatmapSets().Find(beatmapSet => beatmapSet.ID == importedSet.ID); + var imported = manager.GetAllUsableBeatmapSets().Find(beatmapSet => beatmapSet.ID == importedSet.Value.ID); deleteBeatmapSet(imported, osu); } @@ -172,8 +172,8 @@ namespace osu.Game.Tests.Beatmaps.IO ensureLoaded(osu); // but contents doesn't, so existing should still be used. - Assert.IsTrue(imported.ID == importedSecondTime.ID); - Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID); + Assert.IsTrue(imported.ID == importedSecondTime.Value.ID); + Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Value.Beatmaps.First().ID); } finally { @@ -226,8 +226,8 @@ namespace osu.Game.Tests.Beatmaps.IO ensureLoaded(osu); // check the newly "imported" beatmap is not the original. - Assert.IsTrue(imported.ID != importedSecondTime.ID); - Assert.IsTrue(imported.Beatmaps.First().ID != importedSecondTime.Beatmaps.First().ID); + Assert.IsTrue(imported.ID != importedSecondTime.Value.ID); + Assert.IsTrue(imported.Beatmaps.First().ID != importedSecondTime.Value.Beatmaps.First().ID); } finally { @@ -278,8 +278,8 @@ namespace osu.Game.Tests.Beatmaps.IO ensureLoaded(osu); // check the newly "imported" beatmap is not the original. - Assert.IsTrue(imported.ID != importedSecondTime.ID); - Assert.IsTrue(imported.Beatmaps.First().ID != importedSecondTime.Beatmaps.First().ID); + Assert.IsTrue(imported.ID != importedSecondTime.Value.ID); + Assert.IsTrue(imported.Beatmaps.First().ID != importedSecondTime.Value.Beatmaps.First().ID); } finally { @@ -329,8 +329,8 @@ namespace osu.Game.Tests.Beatmaps.IO ensureLoaded(osu); // check the newly "imported" beatmap is not the original. - Assert.IsTrue(imported.ID != importedSecondTime.ID); - Assert.IsTrue(imported.Beatmaps.First().ID != importedSecondTime.Beatmaps.First().ID); + Assert.IsTrue(imported.ID != importedSecondTime.Value.ID); + Assert.IsTrue(imported.Beatmaps.First().ID != importedSecondTime.Value.Beatmaps.First().ID); } finally { @@ -570,8 +570,8 @@ namespace osu.Game.Tests.Beatmaps.IO var imported = await manager.Import(toImport); Assert.NotNull(imported); - Assert.AreEqual(null, imported.Beatmaps[0].OnlineBeatmapID); - Assert.AreEqual(null, imported.Beatmaps[1].OnlineBeatmapID); + Assert.AreEqual(null, imported.Value.Beatmaps[0].OnlineBeatmapID); + Assert.AreEqual(null, imported.Value.Beatmaps[1].OnlineBeatmapID); } finally { @@ -706,7 +706,7 @@ namespace osu.Game.Tests.Beatmaps.IO ensureLoaded(osu); - Assert.IsFalse(imported.Files.Any(f => f.Filename.Contains("subfolder")), "Files contain common subfolder"); + Assert.IsFalse(imported.Value.Files.Any(f => f.Filename.Contains("subfolder")), "Files contain common subfolder"); } finally { @@ -759,8 +759,8 @@ namespace osu.Game.Tests.Beatmaps.IO ensureLoaded(osu); - Assert.IsFalse(imported.Files.Any(f => f.Filename.Contains("__MACOSX")), "Files contain resource fork folder, which should be ignored"); - Assert.IsFalse(imported.Files.Any(f => f.Filename.Contains("actual_data")), "Files contain common subfolder"); + Assert.IsFalse(imported.Value.Files.Any(f => f.Filename.Contains("__MACOSX")), "Files contain resource fork folder, which should be ignored"); + Assert.IsFalse(imported.Value.Files.Any(f => f.Filename.Contains("actual_data")), "Files contain common subfolder"); } finally { @@ -915,7 +915,7 @@ namespace osu.Game.Tests.Beatmaps.IO waitForOrAssert(() => !File.Exists(temp), "Temporary file still exists after standard import", 5000); - return manager.GetAllUsableBeatmapSets().Find(beatmapSet => beatmapSet.ID == importedSet.ID); + return manager.GetAllUsableBeatmapSets().Find(beatmapSet => beatmapSet.ID == importedSet.Value.ID); } public static async Task LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false) @@ -930,7 +930,7 @@ namespace osu.Game.Tests.Beatmaps.IO waitForOrAssert(() => !File.Exists(temp), "Temporary file still exists after standard import", 5000); - return manager.GetAllUsableBeatmapSets().Find(beatmapSet => beatmapSet.ID == importedSet.ID); + return manager.GetAllUsableBeatmapSets().Find(beatmapSet => beatmapSet.ID == importedSet.Value.ID); } private void deleteBeatmapSet(BeatmapSetInfo imported, OsuGameBase osu) diff --git a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs index d38294aba9..79767bc671 100644 --- a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs @@ -156,7 +156,7 @@ namespace osu.Game.Tests.Online { public TaskCompletionSource AllowImport = new TaskCompletionSource(); - public Task CurrentImportTask { get; private set; } + public Task> CurrentImportTask { get; private set; } public TestBeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore resources, GameHost host = null, WorkingBeatmap defaultBeatmap = null) : base(storage, contextFactory, rulesets, api, audioManager, resources, host, defaultBeatmap) @@ -194,7 +194,7 @@ namespace osu.Game.Tests.Online this.testBeatmapManager = testBeatmapManager; } - public override async Task Import(BeatmapSetInfo item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default) + public override async Task> Import(BeatmapSetInfo item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default) { await testBeatmapManager.AllowImport.Task.ConfigureAwait(false); return await (testBeatmapManager.CurrentImportTask = base.Import(item, archive, lowPriority, cancellationToken)).ConfigureAwait(false); diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index 7a9fc20426..b2600bb887 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -196,7 +196,7 @@ namespace osu.Game.Tests.Skins.IO private async Task loadSkinIntoOsu(OsuGameBase osu, ArchiveReader archive = null) { var skinManager = osu.Dependencies.Get(); - return await skinManager.Import(archive); + return (await skinManager.Import(archive)).Value; } } } diff --git a/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs b/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs index eff430ac25..f03cda1489 100644 --- a/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs +++ b/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Skins private void load() { var imported = beatmaps.Import(new ZipArchiveReader(TestResources.OpenResource("Archives/ogg-beatmap.osz"))).Result; - beatmap = beatmaps.GetWorkingBeatmap(imported.Beatmaps[0]); + beatmap = beatmaps.GetWorkingBeatmap(imported.Value.Beatmaps[0]); beatmap.LoadTrack(); } diff --git a/osu.Game.Tests/Skins/TestSceneSkinResources.cs b/osu.Game.Tests/Skins/TestSceneSkinResources.cs index 107a96292f..10f1ab31df 100644 --- a/osu.Game.Tests/Skins/TestSceneSkinResources.cs +++ b/osu.Game.Tests/Skins/TestSceneSkinResources.cs @@ -24,7 +24,7 @@ namespace osu.Game.Tests.Skins private void load() { var imported = skins.Import(new ZipArchiveReader(TestResources.OpenResource("Archives/ogg-skin.osk"))).Result; - skin = skins.GetSkin(imported); + skin = skins.GetSkin(imported.Value); } [Test] diff --git a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs index 9037338e23..79dfe79299 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs @@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.Menus AddStep("import beatmap with track", () => { var setWithTrack = Game.BeatmapManager.Import(new ImportTask(TestResources.GetTestBeatmapForImport())).Result; - Beatmap.Value = Game.BeatmapManager.GetWorkingBeatmap(setWithTrack.Beatmaps.First()); + Beatmap.Value = Game.BeatmapManager.GetWorkingBeatmap(setWithTrack.Value.Beatmaps.First()); }); AddStep("bind to track change", () => diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs b/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs index f0ddefa51d..5f5ebfccfb 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs @@ -126,7 +126,7 @@ namespace osu.Game.Tests.Visual.Navigation Ruleset = ruleset ?? new OsuRuleset().RulesetInfo }, } - }).Result; + }).Result.Value; }); AddAssert($"import {i} succeeded", () => imported != null); diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs index 52b577b402..2ea765a1a9 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs @@ -58,7 +58,7 @@ namespace osu.Game.Tests.Visual.Navigation Ruleset = new OsuRuleset().RulesetInfo }, } - }).Result; + }).Result.Value; }); } @@ -132,7 +132,7 @@ namespace osu.Game.Tests.Visual.Navigation OnlineScoreID = i, Beatmap = beatmap.Beatmaps.First(), Ruleset = ruleset ?? new OsuRuleset().RulesetInfo - }).Result; + }).Result.Value; }); AddAssert($"import {i} succeeded", () => imported != null); diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs index 9051c71fc6..d8ec89a94e 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs @@ -117,7 +117,7 @@ namespace osu.Game.Tests.Visual.Playlists { beatmap.BeatmapInfo.BaseDifficulty.CircleSize = 1; - importedSet = manager.Import(beatmap.BeatmapInfo.BeatmapSet).Result; + importedSet = manager.Import(beatmap.BeatmapInfo.BeatmapSet).Result.Value; }); AddStep("load room", () => diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs index 53cb628bb3..c22b6a54e9 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -192,7 +192,7 @@ namespace osu.Game.Tests.Visual.SongSelect }).ToList() }; - return Game.BeatmapManager.Import(beatmapSet).Result; + return Game.BeatmapManager.Import(beatmapSet).Result.Value; } private bool ensureAllBeatmapSetsImported(IEnumerable beatmapSets) => beatmapSets.All(set => set != null); diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 102e5ee425..19aa91a38f 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -751,7 +751,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("import huge difficulty count map", () => { var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray(); - imported = manager.Import(createTestBeatmapSet(usableRulesets, 50)).Result; + imported = manager.Import(createTestBeatmapSet(usableRulesets, 50)).Result.Value; }); AddStep("select the first beatmap of import", () => Beatmap.Value = manager.GetWorkingBeatmap(imported.Beatmaps.First())); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index 2e30ed9827..3c69db032e 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -84,7 +84,7 @@ namespace osu.Game.Tests.Visual.UserInterface dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get(), Resources, dependencies.Get(), Beatmap.Default)); dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, null, ContextFactory, Scheduler)); - beatmap = beatmapManager.Import(new ImportTask(TestResources.GetQuickTestBeatmapForImport())).Result.Beatmaps[0]; + beatmap = beatmapManager.Import(new ImportTask(TestResources.GetQuickTestBeatmapForImport())).Result.Value.Beatmaps[0]; for (int i = 0; i < 50; i++) { @@ -100,7 +100,7 @@ namespace osu.Game.Tests.Visual.UserInterface User = new User { Username = "TestUser" }, }; - importedScores.Add(scoreManager.Import(score).Result); + importedScores.Add(scoreManager.Import(score).Result.Value); } return dependencies; diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 8dfd895987..1bf4feb6a3 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -89,8 +89,9 @@ namespace osu.Game.Beatmaps } }; - var working = beatmapModelManager.Import(set).Result; - return GetWorkingBeatmap(working.Beatmaps.First()); + var imported = beatmapModelManager.Import(set).Result.Value; + + return GetWorkingBeatmap(imported.Beatmaps.First()); } #region Delegation to BeatmapModelManager (methods which previously existed locally). @@ -176,7 +177,7 @@ namespace osu.Game.Beatmaps /// /// Fired when the user requests to view the resulting import. /// - public Action> PresentImport { set => beatmapModelManager.PresentImport = value; } + public Action>> PresentImport { set => beatmapModelManager.PresentImport = value; } /// /// Delete a beatmap difficulty. @@ -275,22 +276,22 @@ namespace osu.Game.Beatmaps return beatmapModelManager.Import(tasks); } - public Task> Import(ProgressNotification notification, params ImportTask[] tasks) + public Task>> Import(ProgressNotification notification, params ImportTask[] tasks) { return beatmapModelManager.Import(notification, tasks); } - public Task Import(ImportTask task, bool lowPriority = false, CancellationToken cancellationToken = default) + public Task> Import(ImportTask task, bool lowPriority = false, CancellationToken cancellationToken = default) { return beatmapModelManager.Import(task, lowPriority, cancellationToken); } - public Task Import(ArchiveReader archive, bool lowPriority = false, CancellationToken cancellationToken = default) + public Task> Import(ArchiveReader archive, bool lowPriority = false, CancellationToken cancellationToken = default) { return beatmapModelManager.Import(archive, lowPriority, cancellationToken); } - public Task Import(BeatmapSetInfo item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default) + public Task> Import(BeatmapSetInfo item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default) { return beatmapModelManager.Import(item, archive, lowPriority, cancellationToken); } diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 0c309bbddb..403bfdf621 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -132,13 +132,13 @@ namespace osu.Game.Database return Import(notification, tasks); } - public async Task> Import(ProgressNotification notification, params ImportTask[] tasks) + public async Task>> Import(ProgressNotification notification, params ImportTask[] tasks) { if (tasks.Length == 0) { notification.CompletionText = $"No {HumanisedModelName}s were found to import!"; notification.State = ProgressNotificationState.Completed; - return Enumerable.Empty(); + return Enumerable.Empty>(); } notification.Progress = 0; @@ -146,7 +146,7 @@ namespace osu.Game.Database int current = 0; - var imported = new List(); + var imported = new List>(); bool isLowPriorityImport = tasks.Length > low_priority_import_batch_size; @@ -224,11 +224,11 @@ namespace osu.Game.Database /// Whether this is a low priority import. /// An optional cancellation token. /// The imported model, if successful. - public async Task Import(ImportTask task, bool lowPriority = false, CancellationToken cancellationToken = default) + public async Task> Import(ImportTask task, bool lowPriority = false, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); - TModel import; + ILive import; using (ArchiveReader reader = task.GetReader()) import = await Import(reader, lowPriority, cancellationToken).ConfigureAwait(false); @@ -243,13 +243,13 @@ namespace osu.Game.Database } catch (Exception e) { - LogForModel(import, $@"Could not delete original file after import ({task})", e); + LogForModel(import?.Value, $@"Could not delete original file after import ({task})", e); } return import; } - public Action> PresentImport { protected get; set; } + public Action>> PresentImport { protected get; set; } /// /// Silently import an item from an . @@ -257,7 +257,7 @@ namespace osu.Game.Database /// The archive to be imported. /// Whether this is a low priority import. /// An optional cancellation token. - public Task Import(ArchiveReader archive, bool lowPriority = false, CancellationToken cancellationToken = default) + public Task> Import(ArchiveReader archive, bool lowPriority = false, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); @@ -268,7 +268,7 @@ namespace osu.Game.Database model = CreateModel(archive); if (model == null) - return Task.FromResult(null); + return Task.FromResult>(new EntityFrameworkLive(null)); } catch (TaskCanceledException) { @@ -343,7 +343,7 @@ namespace osu.Game.Database /// An optional archive to use for model population. /// Whether this is a low priority import. /// An optional cancellation token. - public virtual async Task Import(TModel item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default) => await Task.Factory.StartNew(async () => + public virtual async Task> Import(TModel item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default) => await Task.Factory.StartNew(async () => { cancellationToken.ThrowIfCancellationRequested(); @@ -369,7 +369,7 @@ namespace osu.Game.Database { LogForModel(item, @$"Found existing (optimised) {HumanisedModelName} for {item} (ID {existing.ID}) – skipping import."); Undelete(existing); - return existing; + return existing.ToEntityFrameworkLive(); } LogForModel(item, @"Found existing (optimised) but failed pre-check."); @@ -415,7 +415,7 @@ namespace osu.Game.Database // existing item will be used; rollback new import and exit early. rollback(); flushEvents(true); - return existing; + return existing.ToEntityFrameworkLive(); } LogForModel(item, @"Found existing but failed re-use check."); @@ -448,7 +448,7 @@ namespace osu.Game.Database } flushEvents(true); - return item; + return item.ToEntityFrameworkLive(); }, cancellationToken, TaskCreationOptions.HideScheduler, lowPriority ? import_scheduler_low_priority : import_scheduler).Unwrap().ConfigureAwait(false); /// diff --git a/osu.Game/Database/EntityFrameworkLive.cs b/osu.Game/Database/EntityFrameworkLive.cs new file mode 100644 index 0000000000..1d7b53911a --- /dev/null +++ b/osu.Game/Database/EntityFrameworkLive.cs @@ -0,0 +1,34 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; + +namespace osu.Game.Database +{ + public class EntityFrameworkLive : ILive where T : class + { + public EntityFrameworkLive(T item) + { + Value = item; + } + + public Guid ID => throw new InvalidOperationException(); + + public void PerformRead(Action perform) + { + perform(Value); + } + + public TReturn PerformRead(Func perform) + { + return perform(Value); + } + + public void PerformWrite(Action perform) + { + perform(Value); + } + + public T Value { get; } + } +} diff --git a/osu.Game/Database/EntityFrameworkLiveExtensions.cs b/osu.Game/Database/EntityFrameworkLiveExtensions.cs new file mode 100644 index 0000000000..cd0673675e --- /dev/null +++ b/osu.Game/Database/EntityFrameworkLiveExtensions.cs @@ -0,0 +1,14 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Database +{ + public static class EntityFrameworkLiveExtensions + { + public static ILive ToEntityFrameworkLive(this T item) + where T : class + { + return new EntityFrameworkLive(item); + } + } +} diff --git a/osu.Game/Database/ILive.cs b/osu.Game/Database/ILive.cs new file mode 100644 index 0000000000..29e5756dba --- /dev/null +++ b/osu.Game/Database/ILive.cs @@ -0,0 +1,42 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; + +namespace osu.Game.Database +{ + /// + /// A wrapper to provide access to database backed classes in a thread-safe manner. + /// + /// The databased type. + public interface ILive where T : class + { + Guid ID { get; } + + /// + /// Perform a read operation on this live object. + /// + /// The action to perform. + void PerformRead(Action perform); + + /// + /// Perform a read operation on this live object. + /// + /// The action to perform. + TReturn PerformRead(Func perform); + + /// + /// Perform a write operation on this live object. + /// + /// The action to perform. + void PerformWrite(Action perform); + + /// + /// Resolve the value of this instance on the current thread's context. + /// + /// + /// After resolving the data should not be passed between threads. + /// + T Value { get; } + } +} diff --git a/osu.Game/Database/IModelImporter.cs b/osu.Game/Database/IModelImporter.cs index fa3b4d9152..e94af01772 100644 --- a/osu.Game/Database/IModelImporter.cs +++ b/osu.Game/Database/IModelImporter.cs @@ -28,7 +28,7 @@ namespace osu.Game.Database Task Import(params ImportTask[] tasks); - Task> Import(ProgressNotification notification, params ImportTask[] tasks); + Task>> Import(ProgressNotification notification, params ImportTask[] tasks); /// /// Import one from the filesystem and delete the file on success. @@ -38,7 +38,7 @@ namespace osu.Game.Database /// Whether this is a low priority import. /// An optional cancellation token. /// The imported model, if successful. - Task Import(ImportTask task, bool lowPriority = false, CancellationToken cancellationToken = default); + Task> Import(ImportTask task, bool lowPriority = false, CancellationToken cancellationToken = default); /// /// Silently import an item from an . @@ -46,7 +46,7 @@ namespace osu.Game.Database /// The archive to be imported. /// Whether this is a low priority import. /// An optional cancellation token. - Task Import(ArchiveReader archive, bool lowPriority = false, CancellationToken cancellationToken = default); + Task> Import(ArchiveReader archive, bool lowPriority = false, CancellationToken cancellationToken = default); /// /// Silently import an item from a . @@ -55,7 +55,7 @@ namespace osu.Game.Database /// An optional archive to use for model population. /// Whether this is a low priority import. /// An optional cancellation token. - Task Import(TModel item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default); + Task> Import(TModel item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default); /// /// A user displayable name for the model type associated with this manager. diff --git a/osu.Game/Database/IPresentImports.cs b/osu.Game/Database/IPresentImports.cs index 39b495ebd5..6aa29a5083 100644 --- a/osu.Game/Database/IPresentImports.cs +++ b/osu.Game/Database/IPresentImports.cs @@ -12,6 +12,6 @@ namespace osu.Game.Database /// /// Fired when the user requests to view the resulting import. /// - public Action> PresentImport { set; } + public Action>> PresentImport { set; } } } diff --git a/osu.Game/FodyWeavers.xml b/osu.Game/FodyWeavers.xml new file mode 100644 index 0000000000..cc07b89533 --- /dev/null +++ b/osu.Game/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 99925bb1fb..35ec213755 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -624,10 +624,10 @@ namespace osu.Game SkinManager.PostNotification = n => Notifications.Post(n); BeatmapManager.PostNotification = n => Notifications.Post(n); - BeatmapManager.PresentImport = items => PresentBeatmap(items.First()); + BeatmapManager.PresentImport = items => PresentBeatmap(items.First().Value); ScoreManager.PostNotification = n => Notifications.Post(n); - ScoreManager.PresentImport = items => PresentScore(items.First()); + ScoreManager.PresentImport = items => PresentScore(items.First().Value); // make config aware of how to lookup skins for on-screen display purposes. // if this becomes a more common thing, tracked settings should be reconsidered to allow local DI. diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index d83b4e3f1d..aa0ee4bbbb 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -299,22 +299,22 @@ namespace osu.Game.Scoring public IEnumerable HandledExtensions => scoreModelManager.HandledExtensions; - public Task> Import(ProgressNotification notification, params ImportTask[] tasks) + public Task>> Import(ProgressNotification notification, params ImportTask[] tasks) { return scoreModelManager.Import(notification, tasks); } - public Task Import(ImportTask task, bool lowPriority = false, CancellationToken cancellationToken = default) + public Task> Import(ImportTask task, bool lowPriority = false, CancellationToken cancellationToken = default) { return scoreModelManager.Import(task, lowPriority, cancellationToken); } - public Task Import(ArchiveReader archive, bool lowPriority = false, CancellationToken cancellationToken = default) + public Task> Import(ArchiveReader archive, bool lowPriority = false, CancellationToken cancellationToken = default) { return scoreModelManager.Import(archive, lowPriority, cancellationToken); } - public Task Import(ScoreInfo item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default) + public Task> Import(ScoreInfo item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default) { return scoreModelManager.Import(item, archive, lowPriority, cancellationToken); } @@ -365,7 +365,7 @@ namespace osu.Game.Scoring #region Implementation of IPresentImports - public Action> PresentImport + public Action>> PresentImport { set => scoreModelManager.PresentImport = value; } diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index cfe14eab92..fbd33cad67 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -101,8 +101,12 @@ namespace osu.Game.Screens.Menu // if we detect that the theme track or beatmap is unavailable this is either first startup or things are in a bad state. // this could happen if a user has nuked their files store. for now, reimport to repair this. var import = beatmaps.Import(new ZipArchiveReader(game.Resources.GetStream($"Tracks/{BeatmapFile}"), BeatmapFile)).Result; - import.Protected = true; - beatmaps.Update(import); + + import.PerformWrite(b => + { + b.Protected = true; + beatmaps.Update(b); + }); loadThemedIntro(); } diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index edeb17cbad..3842acab74 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -207,7 +207,7 @@ namespace osu.Game.Skinning Name = skin.SkinInfo.Name + " (modified)", Creator = skin.SkinInfo.Creator, InstantiationInfo = skin.SkinInfo.InstantiationInfo, - }).Result; + }).Result.Value; } public void Save(Skin skin) From 9fa901f6aa28feb7183cba972930a99378c40daf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Sep 2021 23:42:40 +0900 Subject: [PATCH 2158/2442] Refine `RealmContext` implementation API --- .../Database/TestRealmKeyBindingStore.cs | 20 +- osu.Game/Database/IRealmFactory.cs | 12 +- osu.Game/Database/RealmContextFactory.cs | 257 ++++++------------ osu.Game/Database/RealmExtensions.cs | 45 +-- osu.Game/Database/RealmObjectExtensions.cs | 51 ++++ osu.Game/Input/RealmKeyBindingStore.cs | 20 +- osu.Game/OsuGameBase.cs | 11 +- .../Settings/Sections/Input/KeyBindingRow.cs | 8 +- .../Sections/Input/KeyBindingsSubsection.cs | 4 +- 9 files changed, 174 insertions(+), 254 deletions(-) create mode 100644 osu.Game/Database/RealmObjectExtensions.cs diff --git a/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs b/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs index 8be74f1a7c..f10b11733e 100644 --- a/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs +++ b/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs @@ -32,7 +32,7 @@ namespace osu.Game.Tests.Database storage = new NativeStorage(directory.FullName); - realmContextFactory = new RealmContextFactory(storage); + realmContextFactory = new RealmContextFactory(storage, "test"); keyBindingStore = new RealmKeyBindingStore(realmContextFactory); } @@ -53,9 +53,9 @@ namespace osu.Game.Tests.Database private int queryCount(GlobalAction? match = null) { - using (var usage = realmContextFactory.GetForRead()) + using (var realm = realmContextFactory.CreateContext()) { - var results = usage.Realm.All(); + var results = realm.All(); if (match.HasValue) results = results.Where(k => k.ActionInt == (int)match.Value); return results.Count(); @@ -69,26 +69,24 @@ namespace osu.Game.Tests.Database keyBindingStore.Register(testContainer, Enumerable.Empty()); - using (var primaryUsage = realmContextFactory.GetForRead()) + using (var primaryRealm = realmContextFactory.CreateContext()) { - var backBinding = primaryUsage.Realm.All().Single(k => k.ActionInt == (int)GlobalAction.Back); + var backBinding = primaryRealm.All().Single(k => k.ActionInt == (int)GlobalAction.Back); Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.Escape })); var tsr = ThreadSafeReference.Create(backBinding); - using (var usage = realmContextFactory.GetForWrite()) + using (var threadedContext = realmContextFactory.CreateContext()) { - var binding = usage.Realm.ResolveReference(tsr); - binding.KeyCombination = new KeyCombination(InputKey.BackSpace); - - usage.Commit(); + var binding = threadedContext.ResolveReference(tsr); + threadedContext.Write(() => binding.KeyCombination = new KeyCombination(InputKey.BackSpace)); } Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.BackSpace })); // check still correct after re-query. - backBinding = primaryUsage.Realm.All().Single(k => k.ActionInt == (int)GlobalAction.Back); + backBinding = primaryRealm.All().Single(k => k.ActionInt == (int)GlobalAction.Back); Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.BackSpace })); } } diff --git a/osu.Game/Database/IRealmFactory.cs b/osu.Game/Database/IRealmFactory.cs index 0e93e5bf4f..3b206d80eb 100644 --- a/osu.Game/Database/IRealmFactory.cs +++ b/osu.Game/Database/IRealmFactory.cs @@ -9,20 +9,12 @@ namespace osu.Game.Database { /// /// The main realm context, bound to the update thread. - /// If querying from a non-update thread is needed, use or to receive a context instead. /// Realm Context { get; } /// - /// Get a fresh context for read usage. + /// Create a new realm context for use on an arbitrary thread. /// - RealmContextFactory.RealmUsage GetForRead(); - - /// - /// Request a context for write usage. - /// This method may block if a write is already active on a different thread. - /// - /// A usage containing a usable context. - RealmContextFactory.RealmWriteUsage GetForWrite(); + Realm CreateContext(); } } diff --git a/osu.Game/Database/RealmContextFactory.cs b/osu.Game/Database/RealmContextFactory.cs index ed3dc01f15..c51ac095bb 100644 --- a/osu.Game/Database/RealmContextFactory.cs +++ b/osu.Game/Database/RealmContextFactory.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Diagnostics; using System.Threading; using osu.Framework.Allocation; using osu.Framework.Development; @@ -10,80 +9,115 @@ using osu.Framework.Graphics; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Statistics; -using osu.Game.Input.Bindings; using Realms; +#nullable enable + namespace osu.Game.Database { + /// + /// A factory which provides both the main (update thread bound) realm context and creates contexts for async usage. + /// public class RealmContextFactory : Component, IRealmFactory { private readonly Storage storage; - private const string database_name = @"client"; + /// + /// The filename of this realm. + /// + public readonly string Filename; private const int schema_version = 6; /// - /// Lock object which is held for the duration of a write operation (via ). + /// Lock object which is held during sections, blocking context creation during blocking periods. /// - private readonly object writeLock = new object(); + private readonly SemaphoreSlim contextCreationLock = new SemaphoreSlim(1); - /// - /// Lock object which is held during sections. - /// - private readonly SemaphoreSlim blockingLock = new SemaphoreSlim(1); - - private static readonly GlobalStatistic reads = GlobalStatistics.Get("Realm", "Get (Read)"); - private static readonly GlobalStatistic writes = GlobalStatistics.Get("Realm", "Get (Write)"); private static readonly GlobalStatistic refreshes = GlobalStatistics.Get("Realm", "Dirty Refreshes"); private static readonly GlobalStatistic contexts_created = GlobalStatistics.Get("Realm", "Contexts (Created)"); - private static readonly GlobalStatistic pending_writes = GlobalStatistics.Get("Realm", "Pending writes"); - private static readonly GlobalStatistic active_usages = GlobalStatistics.Get("Realm", "Active usages"); - private readonly object updateContextLock = new object(); - - private Realm context; + private Realm? context; public Realm Context { get { if (!ThreadSafety.IsUpdateThread) - throw new InvalidOperationException($"Use {nameof(GetForRead)} or {nameof(GetForWrite)} when performing realm operations from a non-update thread"); + throw new InvalidOperationException($"Use {nameof(CreateContext)} when performing realm operations from a non-update thread"); - lock (updateContextLock) + if (context == null) { - if (context == null) - { - context = createContext(); - Logger.Log($"Opened realm \"{context.Config.DatabasePath}\" at version {context.Config.SchemaVersion}"); - } - - // creating a context will ensure our schema is up-to-date and migrated. - - return context; + context = createContext(); + Logger.Log($"Opened realm \"{context.Config.DatabasePath}\" at version {context.Config.SchemaVersion}"); } + + // creating a context will ensure our schema is up-to-date and migrated. + return context; } } - public RealmContextFactory(Storage storage) + public RealmContextFactory(Storage storage, string filename) { this.storage = storage; + + Filename = filename; + + const string realm_extension = ".realm"; + + if (!Filename.EndsWith(realm_extension, StringComparison.Ordinal)) + Filename += realm_extension; } - public RealmUsage GetForRead() + public Realm CreateContext() { - reads.Value++; - return new RealmUsage(createContext()); + if (IsDisposed) + throw new ObjectDisposedException(nameof(RealmContextFactory)); + + return createContext(); } - public RealmWriteUsage GetForWrite() - { - writes.Value++; - pending_writes.Value++; + /// + /// Compact this realm. + /// + /// + public bool Compact() => Realm.Compact(getConfiguration()); - Monitor.Enter(writeLock); - return new RealmWriteUsage(createContext(), writeComplete); + protected override void Update() + { + base.Update(); + + if (context?.Refresh() == true) + refreshes.Value++; + } + + private Realm createContext() + { + try + { + contextCreationLock.Wait(); + + contexts_created.Value++; + + return Realm.GetInstance(getConfiguration()); + } + finally + { + contextCreationLock.Release(); + } + } + + private RealmConfiguration getConfiguration() + { + return new RealmConfiguration(storage.GetFullPath(Filename, true)) + { + SchemaVersion = schema_version, + MigrationCallback = onMigration, + }; + } + + private void onMigration(Migration migration, ulong lastSchemaVersion) + { } /// @@ -101,163 +135,32 @@ namespace osu.Game.Database Logger.Log(@"Blocking realm operations.", LoggingTarget.Database); - blockingLock.Wait(); - flushContexts(); + contextCreationLock.Wait(); + + context?.Dispose(); + context = null; return new InvokeOnDisposal(this, endBlockingSection); static void endBlockingSection(RealmContextFactory factory) { - factory.blockingLock.Release(); + factory.contextCreationLock.Release(); Logger.Log(@"Restoring realm operations.", LoggingTarget.Database); } } - protected override void Update() - { - base.Update(); - - lock (updateContextLock) - { - if (context?.Refresh() == true) - refreshes.Value++; - } - } - - private Realm createContext() - { - try - { - if (IsDisposed) - throw new ObjectDisposedException(nameof(RealmContextFactory)); - - blockingLock.Wait(); - - contexts_created.Value++; - - return Realm.GetInstance(new RealmConfiguration(storage.GetFullPath($"{database_name}.realm", true)) - { - SchemaVersion = schema_version, - MigrationCallback = onMigration, - }); - } - finally - { - blockingLock.Release(); - } - } - - private void writeComplete() - { - Monitor.Exit(writeLock); - pending_writes.Value--; - } - - private void onMigration(Migration migration, ulong lastSchemaVersion) - { - switch (lastSchemaVersion) - { - case 5: - // let's keep things simple. changing the type of the primary key is a bit involved. - migration.NewRealm.RemoveAll(); - break; - } - } - - private void flushContexts() - { - Logger.Log(@"Flushing realm contexts...", LoggingTarget.Database); - Debug.Assert(blockingLock.CurrentCount == 0); - - Realm previousContext; - - lock (updateContextLock) - { - previousContext = context; - context = null; - } - - // wait for all threaded usages to finish - while (active_usages.Value > 0) - Thread.Sleep(50); - - previousContext?.Dispose(); - - Logger.Log(@"Realm contexts flushed.", LoggingTarget.Database); - } - protected override void Dispose(bool isDisposing) { + context?.Dispose(); + if (!IsDisposed) { // intentionally block all operations indefinitely. this ensures that nothing can start consuming a new context after disposal. BlockAllOperations(); - blockingLock?.Dispose(); + contextCreationLock.Dispose(); } base.Dispose(isDisposing); } - - /// - /// A usage of realm from an arbitrary thread. - /// - public class RealmUsage : IDisposable - { - public readonly Realm Realm; - - internal RealmUsage(Realm context) - { - active_usages.Value++; - Realm = context; - } - - /// - /// Disposes this instance, calling the initially captured action. - /// - public virtual void Dispose() - { - Realm?.Dispose(); - active_usages.Value--; - } - } - - /// - /// A transaction used for making changes to realm data. - /// - public class RealmWriteUsage : RealmUsage - { - private readonly Action onWriteComplete; - private readonly Transaction transaction; - - internal RealmWriteUsage(Realm context, Action onWriteComplete) - : base(context) - { - this.onWriteComplete = onWriteComplete; - transaction = Realm.BeginWrite(); - } - - /// - /// Commit all changes made in this transaction. - /// - public void Commit() => transaction.Commit(); - - /// - /// Revert all changes made in this transaction. - /// - public void Rollback() => transaction.Rollback(); - - /// - /// Disposes this instance, calling the initially captured action. - /// - public override void Dispose() - { - // rollback if not explicitly committed. - transaction?.Dispose(); - - base.Dispose(); - - onWriteComplete(); - } - } } } diff --git a/osu.Game/Database/RealmExtensions.cs b/osu.Game/Database/RealmExtensions.cs index aee36e81c5..e6f3dba39f 100644 --- a/osu.Game/Database/RealmExtensions.cs +++ b/osu.Game/Database/RealmExtensions.cs @@ -1,51 +1,26 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; -using AutoMapper; -using osu.Game.Input.Bindings; +using System; using Realms; namespace osu.Game.Database { public static class RealmExtensions { - private static readonly IMapper mapper = new MapperConfiguration(c => + public static void Write(this Realm realm, Action function) { - c.ShouldMapField = fi => false; - c.ShouldMapProperty = pi => pi.SetMethod != null && pi.SetMethod.IsPublic; - - c.CreateMap(); - }).CreateMapper(); - - /// - /// Create a detached copy of the each item in the collection. - /// - /// A list of managed s to detach. - /// The type of object. - /// A list containing non-managed copies of provided items. - public static List Detach(this IEnumerable items) where T : RealmObject - { - var list = new List(); - - foreach (var obj in items) - list.Add(obj.Detach()); - - return list; + using var transaction = realm.BeginWrite(); + function(realm); + transaction.Commit(); } - /// - /// Create a detached copy of the item. - /// - /// The managed to detach. - /// The type of object. - /// A non-managed copy of provided item. Will return the provided item if already detached. - public static T Detach(this T item) where T : RealmObject + public static T Write(this Realm realm, Func function) { - if (!item.IsManaged) - return item; - - return mapper.Map(item); + using var transaction = realm.BeginWrite(); + var result = function(realm); + transaction.Commit(); + return result; } } } diff --git a/osu.Game/Database/RealmObjectExtensions.cs b/osu.Game/Database/RealmObjectExtensions.cs new file mode 100644 index 0000000000..c5aa1399a3 --- /dev/null +++ b/osu.Game/Database/RealmObjectExtensions.cs @@ -0,0 +1,51 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using AutoMapper; +using osu.Game.Input.Bindings; +using Realms; + +namespace osu.Game.Database +{ + public static class RealmObjectExtensions + { + private static readonly IMapper mapper = new MapperConfiguration(c => + { + c.ShouldMapField = fi => false; + c.ShouldMapProperty = pi => pi.SetMethod != null && pi.SetMethod.IsPublic; + + c.CreateMap(); + }).CreateMapper(); + + /// + /// Create a detached copy of the each item in the collection. + /// + /// A list of managed s to detach. + /// The type of object. + /// A list containing non-managed copies of provided items. + public static List Detach(this IEnumerable items) where T : RealmObject + { + var list = new List(); + + foreach (var obj in items) + list.Add(obj.Detach()); + + return list; + } + + /// + /// Create a detached copy of the item. + /// + /// The managed to detach. + /// The type of object. + /// A non-managed copy of provided item. Will return the provided item if already detached. + public static T Detach(this T item) where T : RealmObject + { + if (!item.IsManaged) + return item; + + return mapper.Map(item); + } + } +} diff --git a/osu.Game/Input/RealmKeyBindingStore.cs b/osu.Game/Input/RealmKeyBindingStore.cs index 03cb4031ca..5fa3ccdeb9 100644 --- a/osu.Game/Input/RealmKeyBindingStore.cs +++ b/osu.Game/Input/RealmKeyBindingStore.cs @@ -7,6 +7,7 @@ using osu.Framework.Input.Bindings; using osu.Game.Database; using osu.Game.Input.Bindings; using osu.Game.Rulesets; +using Realms; #nullable enable @@ -30,9 +31,9 @@ namespace osu.Game.Input { List combinations = new List(); - using (var context = realmFactory.GetForRead()) + using (var context = realmFactory.CreateContext()) { - foreach (var action in context.Realm.All().Where(b => b.RulesetID == null && (GlobalAction)b.ActionInt == globalAction)) + foreach (var action in context.All().Where(b => b.RulesetID == null && (GlobalAction)b.ActionInt == globalAction)) { string str = action.KeyCombination.ReadableString(); @@ -52,26 +53,27 @@ namespace osu.Game.Input /// The rulesets to populate defaults from. public void Register(KeyBindingContainer container, IEnumerable rulesets) { - using (var usage = realmFactory.GetForWrite()) + using (var realm = realmFactory.CreateContext()) + using (var transaction = realm.BeginWrite()) { // intentionally flattened to a list rather than querying against the IQueryable, as nullable fields being queried against aren't indexed. // this is much faster as a result. - var existingBindings = usage.Realm.All().ToList(); + var existingBindings = realm.All().ToList(); - insertDefaults(usage, existingBindings, container.DefaultKeyBindings); + insertDefaults(realm, existingBindings, container.DefaultKeyBindings); foreach (var ruleset in rulesets) { var instance = ruleset.CreateInstance(); foreach (var variant in instance.AvailableVariants) - insertDefaults(usage, existingBindings, instance.GetDefaultKeyBindings(variant), ruleset.ID, variant); + insertDefaults(realm, existingBindings, instance.GetDefaultKeyBindings(variant), ruleset.ID, variant); } - usage.Commit(); + transaction.Commit(); } } - private void insertDefaults(RealmContextFactory.RealmUsage usage, List existingBindings, IEnumerable defaults, int? rulesetId = null, int? variant = null) + private void insertDefaults(Realm realm, List existingBindings, IEnumerable defaults, int? rulesetId = null, int? variant = null) { // compare counts in database vs defaults for each action type. foreach (var defaultsForAction in defaults.GroupBy(k => k.Action)) @@ -83,7 +85,7 @@ namespace osu.Game.Input continue; // insert any defaults which are missing. - usage.Realm.Add(defaultsForAction.Skip(existingCount).Select(k => new RealmKeyBinding + realm.Add(defaultsForAction.Skip(existingCount).Select(k => new RealmKeyBinding { KeyCombinationString = k.KeyCombination.ToString(), ActionInt = (int)k.Action, diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 7aa460981a..f8f39029d2 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -187,7 +187,7 @@ namespace osu.Game dependencies.Cache(contextFactory = new DatabaseContextFactory(Storage)); - dependencies.Cache(realmFactory = new RealmContextFactory(Storage)); + dependencies.Cache(realmFactory = new RealmContextFactory(Storage, "client")); updateThreadState = Host.UpdateThread.State.GetBoundCopy(); updateThreadState.BindValueChanged(updateThreadStateChanged); @@ -448,19 +448,20 @@ namespace osu.Game private void migrateDataToRealm() { using (var db = contextFactory.GetForWrite()) - using (var usage = realmFactory.GetForWrite()) + using (var realm = realmFactory.CreateContext()) + using (var transaction = realm.BeginWrite()) { // migrate ruleset settings. can be removed 20220315. var existingSettings = db.Context.DatabasedSetting; // only migrate data if the realm database is empty. - if (!usage.Realm.All().Any()) + if (!realm.All().Any()) { foreach (var dkb in existingSettings) { if (dkb.RulesetID == null) continue; - usage.Realm.Add(new RealmRulesetSetting + realm.Add(new RealmRulesetSetting { Key = dkb.Key, Value = dkb.StringValue, @@ -472,7 +473,7 @@ namespace osu.Game db.Context.RemoveRange(existingSettings); - usage.Commit(); + transaction.Commit(); } } diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs index 85d88c96f8..cf8adf2785 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs @@ -368,12 +368,10 @@ namespace osu.Game.Overlays.Settings.Sections.Input private void updateStoreFromButton(KeyButton button) { - using (var usage = realmFactory.GetForWrite()) + using (var realm = realmFactory.CreateContext()) { - var binding = usage.Realm.Find(((IHasGuidPrimaryKey)button.KeyBinding).ID); - binding.KeyCombinationString = button.KeyBinding.KeyCombinationString; - - usage.Commit(); + var binding = realm.Find(((IHasGuidPrimaryKey)button.KeyBinding).ID); + realm.Write(() => binding.KeyCombinationString = button.KeyBinding.KeyCombinationString); } } diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs index fae0318359..0e8e10c086 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs @@ -38,8 +38,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input List bindings; - using (var usage = realmFactory.GetForRead()) - bindings = usage.Realm.All().Where(b => b.RulesetID == rulesetId && b.Variant == variant).Detach(); + using (var realm = realmFactory.CreateContext()) + bindings = realm.All().Where(b => b.RulesetID == rulesetId && b.Variant == variant).Detach(); foreach (var defaultGroup in Defaults.GroupBy(d => d.Action)) { From 38cd383aaf474f91fa7668718de9923f4fd087cf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 00:27:54 +0900 Subject: [PATCH 2159/2442] Remove local handling of realm when switching thread modes --- osu.Game/OsuGameBase.cs | 37 +++++++------------------------------ 1 file changed, 7 insertions(+), 30 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 7aa460981a..d8cf8c729e 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -13,24 +13,23 @@ using osu.Framework.Development; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.IO.Stores; -using osu.Framework.Platform; -using osu.Game.Beatmaps; -using osu.Game.Configuration; -using osu.Game.Graphics; -using osu.Game.Graphics.Cursor; -using osu.Game.Online.API; using osu.Framework.Graphics.Performance; using osu.Framework.Graphics.Textures; using osu.Framework.Input; +using osu.Framework.IO.Stores; using osu.Framework.Logging; -using osu.Framework.Threading; +using osu.Framework.Platform; using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Database; +using osu.Game.Graphics; +using osu.Game.Graphics.Cursor; using osu.Game.Input; using osu.Game.Input.Bindings; using osu.Game.IO; using osu.Game.Online; +using osu.Game.Online.API; using osu.Game.Online.Chat; using osu.Game.Online.Multiplayer; using osu.Game.Online.Spectator; @@ -160,8 +159,6 @@ namespace osu.Game private readonly BindableNumber globalTrackVolumeAdjust = new BindableNumber(GLOBAL_TRACK_VOLUME_ADJUST); - private IBindable updateThreadState; - public OsuGameBase() { UseDevelopmentServer = DebugUtils.IsDebugBuild; @@ -189,9 +186,6 @@ namespace osu.Game dependencies.Cache(realmFactory = new RealmContextFactory(Storage)); - updateThreadState = Host.UpdateThread.State.GetBoundCopy(); - updateThreadState.BindValueChanged(updateThreadStateChanged); - AddInternal(realmFactory); dependencies.CacheAs(Storage); @@ -367,23 +361,6 @@ namespace osu.Game AddFont(Resources, @"Fonts/Venera/Venera-Black"); } - private IDisposable blocking; - - private void updateThreadStateChanged(ValueChangedEvent state) - { - switch (state.NewValue) - { - case GameThreadState.Running: - blocking?.Dispose(); - blocking = null; - break; - - case GameThreadState.Paused: - blocking = realmFactory.BlockAllOperations(); - break; - } - } - protected override void LoadComplete() { base.LoadComplete(); From ca7346e01fc5280ebe53106146f4434958c75c4d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 00:34:09 +0900 Subject: [PATCH 2160/2442] Add test coverage --- osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs index e2baa82ba0..7327d4053a 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; @@ -123,6 +124,13 @@ namespace osu.Game.Tests.Visual.Navigation AddAssert("ruleset unchanged", () => ReferenceEquals(Ruleset.Value, ruleset)); } + [Test] + public void TestSwitchThreadExecutionMode() + { + AddStep("Change thread mode to multi threaded", () => { game.Dependencies.Get().SetValue(FrameworkSetting.ExecutionMode, ExecutionMode.MultiThreaded); }); + AddStep("Change thread mode to single thread", () => { game.Dependencies.Get().SetValue(FrameworkSetting.ExecutionMode, ExecutionMode.SingleThread); }); + } + [Test] public void TestUnavailableRulesetHandled() { From 9c0abae2b0836dd7cb9e3584be85ccf34ad03615 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Sep 2021 23:59:26 +0900 Subject: [PATCH 2161/2442] Add failing test coverage of realm blocking behaviour --- osu.Game.Tests/Database/GeneralUsageTests.cs | 64 ++++++++++++++++++ osu.Game.Tests/Database/RealmTest.cs | 70 ++++++++++++++++++++ osu.Game.Tests/osu.Game.Tests.csproj | 1 + 3 files changed, 135 insertions(+) create mode 100644 osu.Game.Tests/Database/GeneralUsageTests.cs create mode 100644 osu.Game.Tests/Database/RealmTest.cs diff --git a/osu.Game.Tests/Database/GeneralUsageTests.cs b/osu.Game.Tests/Database/GeneralUsageTests.cs new file mode 100644 index 0000000000..245981cd9b --- /dev/null +++ b/osu.Game.Tests/Database/GeneralUsageTests.cs @@ -0,0 +1,64 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using NUnit.Framework; + +#nullable enable + +namespace osu.Game.Tests.Database +{ + [TestFixture] + public class GeneralUsageTests : RealmTest + { + /// + /// Just test the construction of a new database works. + /// + [Test] + public void TestConstructRealm() + { + RunTestWithRealm((realmFactory, _) => { realmFactory.CreateContext().Refresh(); }); + } + + [Test] + public void TestBlockOperations() + { + RunTestWithRealm((realmFactory, _) => + { + using (realmFactory.BlockAllOperations()) + { + } + }); + } + + [Test] + public void TestBlockOperationsWithContention() + { + RunTestWithRealm((realmFactory, _) => + { + ManualResetEventSlim stopThreadedUsage = new ManualResetEventSlim(); + ManualResetEventSlim hasThreadedUsage = new ManualResetEventSlim(); + + Task.Factory.StartNew(() => + { + using (realmFactory.CreateContext()) + { + hasThreadedUsage.Set(); + + stopThreadedUsage.Wait(); + } + }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler); + + hasThreadedUsage.Wait(); + + Assert.Throws(() => + { + using (realmFactory.BlockAllOperations()) + { + } + }); + + stopThreadedUsage.Set(); + }); + } + } +} diff --git a/osu.Game.Tests/Database/RealmTest.cs b/osu.Game.Tests/Database/RealmTest.cs new file mode 100644 index 0000000000..2f4838cb67 --- /dev/null +++ b/osu.Game.Tests/Database/RealmTest.cs @@ -0,0 +1,70 @@ +// 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.Runtime.CompilerServices; +using System.Threading.Tasks; +using Nito.AsyncEx; +using NUnit.Framework; +using osu.Framework.Logging; +using osu.Framework.Platform; +using osu.Framework.Testing; +using osu.Game.Database; + +#nullable enable + +namespace osu.Game.Tests.Database +{ + [TestFixture] + public abstract class RealmTest + { + private static readonly TemporaryNativeStorage storage; + + static RealmTest() + { + storage = new TemporaryNativeStorage("realm-test"); + storage.DeleteDirectory(string.Empty); + } + + protected void RunTestWithRealm(Action testAction, [CallerMemberName] string caller = "") + { + AsyncContext.Run(() => + { + var testStorage = storage.GetStorageForDirectory(caller); + + using (var realmFactory = new RealmContextFactory(testStorage, caller)) + { + Logger.Log($"Running test using realm file {testStorage.GetFullPath(realmFactory.Filename)}"); + testAction(realmFactory, testStorage); + + realmFactory.Dispose(); + Logger.Log($"Final database size: {testStorage.GetStream(realmFactory.Filename)?.Length ?? 0}"); + + realmFactory.Compact(); + Logger.Log($"Final database size after compact: {testStorage.GetStream(realmFactory.Filename)?.Length ?? 0}"); + } + }); + } + + protected void RunTestWithRealmAsync(Func testAction, [CallerMemberName] string caller = "") + { + AsyncContext.Run(async () => + { + var testStorage = storage.GetStorageForDirectory(caller); + + using (var realmFactory = new RealmContextFactory(testStorage, caller)) + { + Logger.Log($"Running test using realm file {testStorage.GetFullPath(realmFactory.Filename)}"); + + await testAction(realmFactory, testStorage); + + realmFactory.Dispose(); + Logger.Log($"Final database size: {testStorage.GetStream(realmFactory.Filename)?.Length ?? 0}"); + + realmFactory.Compact(); + Logger.Log($"Final database size after compact: {testStorage.GetStream(realmFactory.Filename)?.Length ?? 0}"); + } + }); + } + } +} diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 696f930467..cd56cb51ae 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -4,6 +4,7 @@ + From cfd3bdf888fc24df4bc8eeb0f8def24471352bbb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 01:32:28 +0900 Subject: [PATCH 2162/2442] Ensure realm blocks until all threaded usages are completed --- osu.Game/Database/RealmContextFactory.cs | 39 +++++++++++++++++++----- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/osu.Game/Database/RealmContextFactory.cs b/osu.Game/Database/RealmContextFactory.cs index c51ac095bb..e3b0764721 100644 --- a/osu.Game/Database/RealmContextFactory.cs +++ b/osu.Game/Database/RealmContextFactory.cs @@ -133,20 +133,43 @@ namespace osu.Game.Database if (IsDisposed) throw new ObjectDisposedException(nameof(RealmContextFactory)); + // TODO: this can be added for safety once we figure how to bypass in test + // if (!ThreadSafety.IsUpdateThread) + // throw new InvalidOperationException($"{nameof(BlockAllOperations)} must be called from the update thread."); + Logger.Log(@"Blocking realm operations.", LoggingTarget.Database); - contextCreationLock.Wait(); + try + { + contextCreationLock.Wait(); - context?.Dispose(); - context = null; + const int sleep_length = 200; + int timeout = 5000; - return new InvokeOnDisposal(this, endBlockingSection); + context?.Dispose(); + context = null; - static void endBlockingSection(RealmContextFactory factory) + // see https://github.com/realm/realm-dotnet/discussions/2657 + while (!Compact()) + { + Thread.Sleep(sleep_length); + timeout -= sleep_length; + + if (timeout < 0) + throw new TimeoutException("Took too long to acquire lock"); + } + } + catch + { + contextCreationLock.Release(); + throw; + } + + return new InvokeOnDisposal(this, factory => { factory.contextCreationLock.Release(); Logger.Log(@"Restoring realm operations.", LoggingTarget.Database); - } + }); } protected override void Dispose(bool isDisposing) @@ -155,8 +178,8 @@ namespace osu.Game.Database if (!IsDisposed) { - // intentionally block all operations indefinitely. this ensures that nothing can start consuming a new context after disposal. - BlockAllOperations(); + // intentionally block context creation indefinitely. this ensures that nothing can start consuming a new context after disposal. + contextCreationLock.Wait(); contextCreationLock.Dispose(); } From dde19f2e81ca08df3328799d6d244ae4e62ed9cd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 01:37:51 +0900 Subject: [PATCH 2163/2442] Fix unbalanced brackets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs b/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs index 19f02c82ec..bc86c6be5d 100644 --- a/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs +++ b/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs @@ -211,7 +211,7 @@ namespace osu.Game.Beatmaps } private void logForModel(BeatmapSetInfo set, string message) => - ArchiveModelManager.LogForModel(set, $"{nameof(BeatmapOnlineLookupQueue)}] {message}"); + ArchiveModelManager.LogForModel(set, $"[{nameof(BeatmapOnlineLookupQueue)}] {message}"); public void Dispose() { From 27c4f2b06ee70adab63aeabfa03d42cdcbfbeefc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 01:38:50 +0900 Subject: [PATCH 2164/2442] Add missing disposal --- osu.Game/OsuGameBase.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 8263e26dec..f239119e40 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -531,6 +531,7 @@ namespace osu.Game RulesetStore?.Dispose(); LocalConfig?.Dispose(); + onlineBeatmapLookupCache?.Dispose(); contextFactory?.FlushConnections(); } From 428c7830d958d731398bcb18193160461418a670 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 01:43:57 +0900 Subject: [PATCH 2165/2442] Pass online lookup queue in as a whole, rather than function --- osu.Game/Beatmaps/BeatmapManager.cs | 16 +++++++++++++--- osu.Game/Beatmaps/BeatmapModelManager.cs | 9 ++++----- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index c72d1e8dec..1946e3f93f 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -29,10 +29,11 @@ namespace osu.Game.Beatmaps /// Handles general operations related to global beatmap management. /// [ExcludeFromDynamicCompile] - public class BeatmapManager : IModelDownloader, IModelManager, IModelFileManager, ICanAcceptFiles, IWorkingBeatmapCache + public class BeatmapManager : IModelDownloader, IModelManager, IModelFileManager, ICanAcceptFiles, IWorkingBeatmapCache, IDisposable { private readonly BeatmapModelManager beatmapModelManager; private readonly WorkingBeatmapCache workingBeatmapCache; + private readonly BeatmapOnlineLookupQueue onlineBetamapLookupQueue; public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore resources, GameHost host = null, WorkingBeatmap defaultBeatmap = null, bool performOnlineLookups = false) @@ -44,8 +45,8 @@ namespace osu.Game.Beatmaps if (performOnlineLookups) { - var onlineBeatmapLookupCache = new BeatmapOnlineLookupQueue(api, storage); - beatmapModelManager.PopulateOnlineInformation = onlineBeatmapLookupCache.UpdateAsync; + onlineBetamapLookupQueue = new BeatmapOnlineLookupQueue(api, storage); + beatmapModelManager.OnlineLookupQueue = onlineBetamapLookupQueue; } } @@ -308,5 +309,14 @@ namespace osu.Game.Beatmaps } #endregion + + #region Implementation of IDisposable + + public void Dispose() + { + onlineBetamapLookupQueue?.Dispose(); + } + + #endregion } } diff --git a/osu.Game/Beatmaps/BeatmapModelManager.cs b/osu.Game/Beatmaps/BeatmapModelManager.cs index be3adc412c..72df1f37ee 100644 --- a/osu.Game/Beatmaps/BeatmapModelManager.cs +++ b/osu.Game/Beatmaps/BeatmapModelManager.cs @@ -49,10 +49,9 @@ namespace osu.Game.Beatmaps public IBindable> BeatmapRestored => beatmapRestored; /// - /// A function which populates online information during the import process. - /// It is run as the final step of import. + /// An online lookup queue component which handles populating online beatmap metadata. /// - public Func PopulateOnlineInformation; + public BeatmapOnlineLookupQueue OnlineLookupQueue { private get; set; } /// /// The game working beatmap cache, used to invalidate entries on changes. @@ -107,8 +106,8 @@ namespace osu.Game.Beatmaps bool hadOnlineBeatmapIDs = beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0); - if (PopulateOnlineInformation != null) - await PopulateOnlineInformation(beatmapSet, cancellationToken).ConfigureAwait(false); + if (OnlineLookupQueue != null) + await OnlineLookupQueue.UpdateAsync(beatmapSet, cancellationToken).ConfigureAwait(false); // ensure at least one beatmap was able to retrieve or keep an online ID, else drop the set ID. if (hadOnlineBeatmapIDs && !beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0)) From 2ed28f625a6ce106ad76c86b2772b808daf975dc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 01:46:37 +0900 Subject: [PATCH 2166/2442] Pass whole queue in rather than function --- osu.Game/Beatmaps/BeatmapManager.cs | 9 ++++----- osu.Game/OsuGameBase.cs | 8 ++++---- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index a2f9740779..1fc7aa3146 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -55,10 +55,9 @@ namespace osu.Game.Beatmaps public IBindable> BeatmapRestored => beatmapRestored; /// - /// A function which populates online information during the import process. - /// It is run as the final step of import. + /// An online lookup queue component which handles populating online beatmap metadata. /// - public Func PopulateOnlineInformation; + public BeatmapOnlineLookupQueue OnlineLookupQueue { private get; set; } private readonly Bindable> beatmapRestored = new Bindable>(); @@ -156,8 +155,8 @@ namespace osu.Game.Beatmaps bool hadOnlineBeatmapIDs = beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0); - if (PopulateOnlineInformation != null) - await PopulateOnlineInformation(beatmapSet, cancellationToken).ConfigureAwait(false); + if (OnlineLookupQueue != null) + await OnlineLookupQueue.UpdateAsync(beatmapSet, cancellationToken).ConfigureAwait(false); // ensure at least one beatmap was able to retrieve or keep an online ID, else drop the set ID. if (hadOnlineBeatmapIDs && !beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0)) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index f239119e40..7772d5dfd8 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -138,7 +138,7 @@ namespace osu.Game private UserLookupCache userCache; - private BeatmapOnlineLookupQueue onlineBeatmapLookupCache; + private BeatmapOnlineLookupQueue onlineBeatmapLookupQueue; private FileStore fileStore; @@ -246,9 +246,9 @@ namespace osu.Game dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Scheduler, Host, () => difficultyCache, LocalConfig)); dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Resources, Host, defaultBeatmap)); - onlineBeatmapLookupCache = new BeatmapOnlineLookupQueue(API, Storage); + onlineBeatmapLookupQueue = new BeatmapOnlineLookupQueue(API, Storage); - BeatmapManager.PopulateOnlineInformation = onlineBeatmapLookupCache.UpdateAsync; + BeatmapManager.OnlineLookupQueue = onlineBeatmapLookupQueue; // this should likely be moved to ArchiveModelManager when another case appears where it is necessary // to have inter-dependent model managers. this could be obtained with an IHasForeign interface to @@ -531,7 +531,7 @@ namespace osu.Game RulesetStore?.Dispose(); LocalConfig?.Dispose(); - onlineBeatmapLookupCache?.Dispose(); + onlineBeatmapLookupQueue?.Dispose(); contextFactory?.FlushConnections(); } From c71cf1e2200bcbefb2d71400782f90ba50918bd1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 01:51:29 +0900 Subject: [PATCH 2167/2442] Fix incomplete xmldoc --- osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs b/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs index bc86c6be5d..55164e2442 100644 --- a/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs +++ b/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs @@ -25,7 +25,7 @@ namespace osu.Game.Beatmaps /// A component which handles population of online IDs for beatmaps using a two part lookup procedure. /// /// - /// On creating the component, a copy of a database containing metadata for a large subset of beatmaps (stored to ). + /// On creating the component, a copy of a database containing metadata for a large subset of beatmaps (stored to ) will be downloaded if not already present locally. /// This will always be checked before doing a second online query to get required metadata. /// [ExcludeFromDynamicCompile] From 8557530cd5e744110b13a167ad30306c7224823e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 03:45:00 +0900 Subject: [PATCH 2168/2442] Add back main context locking --- osu.Game/Database/RealmContextFactory.cs | 30 ++++++++++++++++-------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/osu.Game/Database/RealmContextFactory.cs b/osu.Game/Database/RealmContextFactory.cs index c51ac095bb..0e18b68276 100644 --- a/osu.Game/Database/RealmContextFactory.cs +++ b/osu.Game/Database/RealmContextFactory.cs @@ -37,6 +37,7 @@ namespace osu.Game.Database private static readonly GlobalStatistic refreshes = GlobalStatistics.Get("Realm", "Dirty Refreshes"); private static readonly GlobalStatistic contexts_created = GlobalStatistics.Get("Realm", "Contexts (Created)"); + private readonly object contextLock = new object(); private Realm? context; public Realm Context @@ -46,14 +47,17 @@ namespace osu.Game.Database if (!ThreadSafety.IsUpdateThread) throw new InvalidOperationException($"Use {nameof(CreateContext)} when performing realm operations from a non-update thread"); - if (context == null) + lock (contextLock) { - context = createContext(); - Logger.Log($"Opened realm \"{context.Config.DatabasePath}\" at version {context.Config.SchemaVersion}"); - } + if (context == null) + { + context = createContext(); + Logger.Log($"Opened realm \"{context.Config.DatabasePath}\" at version {context.Config.SchemaVersion}"); + } - // creating a context will ensure our schema is up-to-date and migrated. - return context; + // creating a context will ensure our schema is up-to-date and migrated. + return context; + } } } @@ -87,8 +91,11 @@ namespace osu.Game.Database { base.Update(); - if (context?.Refresh() == true) - refreshes.Value++; + lock (contextLock) + { + if (context?.Refresh() == true) + refreshes.Value++; + } } private Realm createContext() @@ -137,8 +144,11 @@ namespace osu.Game.Database contextCreationLock.Wait(); - context?.Dispose(); - context = null; + lock (contextLock) + { + context?.Dispose(); + context = null; + } return new InvokeOnDisposal(this, endBlockingSection); From b51fd00ba34a8c201e79310834ca8e9ded9aeb4e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 03:46:53 +0900 Subject: [PATCH 2169/2442] Guard against disposal in all context retrievals --- osu.Game/Database/RealmContextFactory.cs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/osu.Game/Database/RealmContextFactory.cs b/osu.Game/Database/RealmContextFactory.cs index 0e18b68276..bf7feebdbf 100644 --- a/osu.Game/Database/RealmContextFactory.cs +++ b/osu.Game/Database/RealmContextFactory.cs @@ -51,7 +51,7 @@ namespace osu.Game.Database { if (context == null) { - context = createContext(); + context = CreateContext(); Logger.Log($"Opened realm \"{context.Config.DatabasePath}\" at version {context.Config.SchemaVersion}"); } @@ -73,14 +73,6 @@ namespace osu.Game.Database Filename += realm_extension; } - public Realm CreateContext() - { - if (IsDisposed) - throw new ObjectDisposedException(nameof(RealmContextFactory)); - - return createContext(); - } - /// /// Compact this realm. /// @@ -98,8 +90,11 @@ namespace osu.Game.Database } } - private Realm createContext() + public Realm CreateContext() { + if (IsDisposed) + throw new ObjectDisposedException(nameof(RealmContextFactory)); + try { contextCreationLock.Wait(); @@ -161,7 +156,10 @@ namespace osu.Game.Database protected override void Dispose(bool isDisposing) { - context?.Dispose(); + lock (contextLock) + { + context?.Dispose(); + } if (!IsDisposed) { From b5345235cae7edb8430f31e97139f1bbe016ee95 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 10:40:55 +0900 Subject: [PATCH 2170/2442] Handle window file access errors --- osu.Game.Tests/Database/RealmTest.cs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Database/RealmTest.cs b/osu.Game.Tests/Database/RealmTest.cs index 2f4838cb67..b7658d6408 100644 --- a/osu.Game.Tests/Database/RealmTest.cs +++ b/osu.Game.Tests/Database/RealmTest.cs @@ -38,10 +38,26 @@ namespace osu.Game.Tests.Database testAction(realmFactory, testStorage); realmFactory.Dispose(); - Logger.Log($"Final database size: {testStorage.GetStream(realmFactory.Filename)?.Length ?? 0}"); + + try + { + Logger.Log($"Final database size: {testStorage.GetStream(realmFactory.Filename)?.Length ?? 0}"); + } + catch + { + // windows runs may error due to file still being open. + } realmFactory.Compact(); - Logger.Log($"Final database size after compact: {testStorage.GetStream(realmFactory.Filename)?.Length ?? 0}"); + + try + { + Logger.Log($"Final database size after compact: {testStorage.GetStream(realmFactory.Filename)?.Length ?? 0}"); + } + catch + { + // windows runs may error due to file still being open. + } } }); } From 619dfe06907d74d3054dbdb3f23c9d0444b505d7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 14:34:11 +0900 Subject: [PATCH 2171/2442] Add new interface base types for models --- osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs | 90 +++++++++++++++ osu.Game/Beatmaps/IBeatmapInfo.cs | 74 +++++++++++++ osu.Game/Beatmaps/IBeatmapMetadataInfo.cs | 117 ++++++++++++++++++++ osu.Game/Beatmaps/IBeatmapSetInfo.cs | 57 ++++++++++ osu.Game/Beatmaps/IFileInfo.cs | 18 +++ osu.Game/Beatmaps/IHasOnlineID.cs | 15 +++ osu.Game/Beatmaps/INamedFileUsage.cs | 23 ++++ osu.Game/Beatmaps/IRulesetInfo.cs | 46 ++++++++ 8 files changed, 440 insertions(+) create mode 100644 osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs create mode 100644 osu.Game/Beatmaps/IBeatmapInfo.cs create mode 100644 osu.Game/Beatmaps/IBeatmapMetadataInfo.cs create mode 100644 osu.Game/Beatmaps/IBeatmapSetInfo.cs create mode 100644 osu.Game/Beatmaps/IFileInfo.cs create mode 100644 osu.Game/Beatmaps/IHasOnlineID.cs create mode 100644 osu.Game/Beatmaps/INamedFileUsage.cs create mode 100644 osu.Game/Beatmaps/IRulesetInfo.cs diff --git a/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs new file mode 100644 index 0000000000..6d9fcfcb06 --- /dev/null +++ b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs @@ -0,0 +1,90 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +namespace osu.Game.Beatmaps +{ + /// + /// A representation of all top-level difficulty settings for a beatmap. + /// + public interface IBeatmapDifficultyInfo + { + /// + /// The default value used for all difficulty settings except and . + /// + const float DEFAULT_DIFFICULTY = 5; + + /// + /// The drain rate of the associated beatmap. + /// + float DrainRate { get; } + + /// + /// The circle size of the associated beatmap. + /// + float CircleSize { get; } + + /// + /// The overall difficulty of the associated beatmap. + /// + float OverallDifficulty { get; } + + /// + /// The approach rate of the associated beatmap. + /// + float ApproachRate { get; } + + /// + /// The slider multiplier of the associated beatmap. + /// + double SliderMultiplier { get; } + + /// + /// The slider tick rate of the associated beatmap. + /// + double SliderTickRate { get; } + + /// + /// Maps a difficulty value [0, 10] to a two-piece linear range of values. + /// + /// The difficulty value to be mapped. + /// Minimum of the resulting range which will be achieved by a difficulty value of 0. + /// Midpoint of the resulting range which will be achieved by a difficulty value of 5. + /// Maximum of the resulting range which will be achieved by a difficulty value of 10. + /// Value to which the difficulty value maps in the specified range. + static double DifficultyRange(double difficulty, double min, double mid, double max) + { + if (difficulty > 5) + return mid + (max - mid) * (difficulty - 5) / 5; + if (difficulty < 5) + return mid - (mid - min) * (5 - difficulty) / 5; + + return mid; + } + + /// + /// Maps a difficulty value [0, 10] to a two-piece linear range of values. + /// + /// The difficulty value to be mapped. + /// The values that define the two linear ranges. + /// + /// + /// od0 + /// Minimum of the resulting range which will be achieved by a difficulty value of 0. + /// + /// + /// od5 + /// Midpoint of the resulting range which will be achieved by a difficulty value of 5. + /// + /// + /// od10 + /// Maximum of the resulting range which will be achieved by a difficulty value of 10. + /// + /// + /// + /// Value to which the difficulty value maps in the specified range. + public static double DifficultyRange(double difficulty, (double od0, double od5, double od10) range) + => DifficultyRange(difficulty, range.od0, range.od5, range.od10); + } +} diff --git a/osu.Game/Beatmaps/IBeatmapInfo.cs b/osu.Game/Beatmaps/IBeatmapInfo.cs new file mode 100644 index 0000000000..72a04621f2 --- /dev/null +++ b/osu.Game/Beatmaps/IBeatmapInfo.cs @@ -0,0 +1,74 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +#nullable enable + +namespace osu.Game.Beatmaps +{ + /// + /// A single beatmap difficulty. + /// + public interface IBeatmapInfo : IHasOnlineID + { + /// + /// The user-specified name given to this beatmap. + /// + string DifficultyName { get; } + + /// + /// The metadata representing this beatmap. May be shared between multiple beatmaps. + /// + IBeatmapMetadataInfo Metadata { get; } + + /// + /// The difficulty settings for this beatmap. + /// + IBeatmapDifficultyInfo Difficulty { get; } + + /// + /// The playable length in milliseconds of this beatmap. + /// + double Length { get; } + + /// + /// The most common BPM of this beatmap. + /// + double BPM { get; } + + /// + /// The SHA-256 hash representing this beatmap's contents. + /// + string Hash { get; } + + /// + /// MD5 is kept for legacy support (matching against replays, osu-web-10 etc.). + /// + string MD5Hash { get; } + + /// + /// The ruleset this beatmap was made for. + /// + IRulesetInfo Ruleset { get; } + + /// + /// The basic star rating for this beatmap (with no mods applied). + /// + double StarRating { get; } + + string DisplayTitle => $"{Metadata} {versionString}".Trim(); + + RomanisableString DisplayTitleRomanisable + { + get + { + var metadata = Metadata.DisplayTitleRomanisable; + + return new RomanisableString($"{metadata.GetPreferred(true)} {versionString}".Trim(), $"{metadata.GetPreferred(false)} {versionString}".Trim()); + } + } + + private string versionString => string.IsNullOrEmpty(DifficultyName) ? string.Empty : $"[{DifficultyName}]"; + } +} diff --git a/osu.Game/Beatmaps/IBeatmapMetadataInfo.cs b/osu.Game/Beatmaps/IBeatmapMetadataInfo.cs new file mode 100644 index 0000000000..18dd38b404 --- /dev/null +++ b/osu.Game/Beatmaps/IBeatmapMetadataInfo.cs @@ -0,0 +1,117 @@ +// 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.Linq; +using osu.Framework.Localisation; + +#nullable enable + +namespace osu.Game.Beatmaps +{ + /// + /// Metadata representing a beatmap. May be shared between multiple beatmap difficulties. + /// + public interface IBeatmapMetadataInfo : IEquatable + { + /// + /// The romanised title of this beatmap. + /// + string Title { get; } + + /// + /// The unicode title of this beatmap. + /// + string TitleUnicode { get; } + + /// + /// The romanised artist of this beatmap. + /// + string Artist { get; } + + /// + /// The unicode artist of this beatmap. + /// + string ArtistUnicode { get; } + + /// + /// The author of this beatmap. + /// + string Author { get; } // eventually should be linked to a persisted User. + + /// + /// The source of this beatmap. + /// + string Source { get; } + + /// + /// The tags of this beatmap. + /// + string Tags { get; } + + /// + /// The time in milliseconds to begin playing the track for preview purposes. + /// If -1, the track should begin playing at 40% of its length. + /// + int PreviewTime { get; } + + /// + /// The filename of the audio file consumed by this beatmap. + /// + string AudioFile { get; } + + /// + /// The filename of the background image file consumed by this beatmap. + /// + string BackgroundFile { get; } + + string DisplayTitle + { + get + { + string author = string.IsNullOrEmpty(Author) ? string.Empty : $"({Author})"; + return $"{Artist} - {Title} {author}".Trim(); + } + } + + RomanisableString DisplayTitleRomanisable + { + get + { + string author = string.IsNullOrEmpty(Author) ? string.Empty : $"({Author})"; + var artistUnicode = string.IsNullOrEmpty(ArtistUnicode) ? Artist : ArtistUnicode; + var titleUnicode = string.IsNullOrEmpty(TitleUnicode) ? Title : TitleUnicode; + + return new RomanisableString($"{artistUnicode} - {titleUnicode} {author}".Trim(), $"{Artist} - {Title} {author}".Trim()); + } + } + + string[] SearchableTerms => new[] + { + Author, + Artist, + ArtistUnicode, + Title, + TitleUnicode, + Source, + Tags + }.Where(s => !string.IsNullOrEmpty(s)).ToArray(); + + bool IEquatable.Equals(IBeatmapMetadataInfo? other) + { + if (other == null) + return false; + + return Title == other.Title + && TitleUnicode == other.TitleUnicode + && Artist == other.Artist + && ArtistUnicode == other.ArtistUnicode + && Author == other.Author + && Source == other.Source + && Tags == other.Tags + && PreviewTime == other.PreviewTime + && AudioFile == other.AudioFile + && BackgroundFile == other.BackgroundFile; + } + } +} diff --git a/osu.Game/Beatmaps/IBeatmapSetInfo.cs b/osu.Game/Beatmaps/IBeatmapSetInfo.cs new file mode 100644 index 0000000000..f22115e08f --- /dev/null +++ b/osu.Game/Beatmaps/IBeatmapSetInfo.cs @@ -0,0 +1,57 @@ +// 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; + +#nullable enable + +namespace osu.Game.Beatmaps +{ + /// + /// A representation of a collection of beatmap difficulties, generally packaged as an ".osz" archive. + /// + public interface IBeatmapSetInfo : IHasOnlineID + { + /// + /// The date when this beatmap was imported. + /// + DateTimeOffset DateAdded { get; } + + /// + /// The best-effort metadata representing this set. In the case metadata differs between contained beatmaps, one is arbitrarily chosen. + /// + IBeatmapMetadataInfo? Metadata { get; } + + /// + /// All beatmaps contained in this set. + /// + IEnumerable Beatmaps { get; } + + /// + /// All files used by this set. + /// + IEnumerable Files { get; } + + /// + /// The maximum star difficulty of all beatmaps in this set. + /// + double MaxStarDifficulty { get; } + + /// + /// The maximum playable length in milliseconds of all beatmaps in this set. + /// + double MaxLength { get; } + + /// + /// The maximum BPM of all beatmaps in this set. + /// + double MaxBPM { get; } + + /// + /// The filename for the storyboard. + /// + string StoryboardFile => Files.FirstOrDefault(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename ?? string.Empty; + } +} diff --git a/osu.Game/Beatmaps/IFileInfo.cs b/osu.Game/Beatmaps/IFileInfo.cs new file mode 100644 index 0000000000..50eb223fc4 --- /dev/null +++ b/osu.Game/Beatmaps/IFileInfo.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +namespace osu.Game.Beatmaps +{ + /// + /// A representation of a tracked file. + /// + public interface IFileInfo + { + /// + /// SHA-256 hash of the file content. + /// + string Hash { get; } + } +} diff --git a/osu.Game/Beatmaps/IHasOnlineID.cs b/osu.Game/Beatmaps/IHasOnlineID.cs new file mode 100644 index 0000000000..dc2793afe5 --- /dev/null +++ b/osu.Game/Beatmaps/IHasOnlineID.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +namespace osu.Game.Beatmaps +{ + public interface IHasOnlineID + { + /// + /// The server-side ID representing this instance, if one exists. + /// + int? OnlineID { get; } + } +} diff --git a/osu.Game/Beatmaps/INamedFileUsage.cs b/osu.Game/Beatmaps/INamedFileUsage.cs new file mode 100644 index 0000000000..aa7a3852a7 --- /dev/null +++ b/osu.Game/Beatmaps/INamedFileUsage.cs @@ -0,0 +1,23 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +namespace osu.Game.Beatmaps +{ + /// + /// A usage of a file, with a local filename attached. + /// + public interface INamedFileUsage + { + /// + /// The underlying file on disk. + /// + IFileInfo File { get; } + + /// + /// The filename for this usage. + /// + string Filename { get; } + } +} diff --git a/osu.Game/Beatmaps/IRulesetInfo.cs b/osu.Game/Beatmaps/IRulesetInfo.cs new file mode 100644 index 0000000000..b31ebdfbfd --- /dev/null +++ b/osu.Game/Beatmaps/IRulesetInfo.cs @@ -0,0 +1,46 @@ +// 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 osu.Game.Rulesets; + +#nullable enable + +namespace osu.Game.Beatmaps +{ + /// + /// A representation of a ruleset's metadata. + /// + public interface IRulesetInfo : IHasOnlineID + { + /// + /// The user-exposed name of this ruleset. + /// + string Name { get; } + + /// + /// An acronym defined by the ruleset that can be used as a permanent identifier. + /// + string ShortName { get; } + + /// + /// A string representation of this ruleset, to be used with reflection to instantiate the ruleset represented by this metadata. + /// + string InstantiationInfo { get; } + + public Ruleset? CreateInstance() + { + var type = Type.GetType(InstantiationInfo); + + if (type == null) + return null; + + var ruleset = Activator.CreateInstance(type) as Ruleset; + + // overwrite the pre-populated RulesetInfo with a potentially database attached copy. + // ruleset.RulesetInfo = this; + + return ruleset; + } + } +} From d30963646077df2eb6f1d2377d7bd56585071e8c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 16:31:11 +0900 Subject: [PATCH 2172/2442] Update all EF based models to implement new read only interfaces --- osu.Game/Beatmaps/BeatmapInfo.cs | 19 ++++++++++++++++++- osu.Game/Beatmaps/BeatmapMetadata.cs | 4 +++- osu.Game/Beatmaps/BeatmapSetFileInfo.cs | 4 +++- osu.Game/Beatmaps/BeatmapSetInfo.cs | 16 +++++++++++++++- osu.Game/Beatmaps/IBeatmapInfo.cs | 2 ++ osu.Game/Beatmaps/IBeatmapSetInfo.cs | 1 + .../{Beatmaps => Database}/IHasOnlineID.cs | 2 +- .../{Beatmaps => Database}/INamedFileUsage.cs | 4 +++- osu.Game/IO/FileInfo.cs | 2 +- osu.Game/{Beatmaps => IO}/IFileInfo.cs | 2 +- .../{Beatmaps => Rulesets}/IRulesetInfo.cs | 4 ++-- osu.Game/Rulesets/RulesetInfo.cs | 8 +++++++- 12 files changed, 57 insertions(+), 11 deletions(-) rename osu.Game/{Beatmaps => Database}/IHasOnlineID.cs (92%) rename osu.Game/{Beatmaps => Database}/INamedFileUsage.cs (92%) rename osu.Game/{Beatmaps => IO}/IFileInfo.cs (93%) rename osu.Game/{Beatmaps => Rulesets}/IRulesetInfo.cs (96%) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 8cb5da8083..d2b47ef1a4 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -17,13 +17,14 @@ namespace osu.Game.Beatmaps { [ExcludeFromDynamicCompile] [Serializable] - public class BeatmapInfo : IEquatable, IHasPrimaryKey + public class BeatmapInfo : IEquatable, IHasPrimaryKey, IBeatmapInfo { public int ID { get; set; } public int BeatmapVersion; private int? onlineBeatmapID; + private IRulesetInfo ruleset; [JsonProperty("id")] public int? OnlineBeatmapID @@ -187,5 +188,21 @@ namespace osu.Game.Beatmaps /// Returns a shallow-clone of this . /// public BeatmapInfo Clone() => (BeatmapInfo)MemberwiseClone(); + + #region Implementation of IHasOnlineID + + public int? OnlineID => ID; + + #endregion + + #region Implementation of IBeatmapInfo + + public string DifficultyName => Version; + IBeatmapMetadataInfo IBeatmapInfo.Metadata => Metadata; + public IBeatmapDifficultyInfo Difficulty => BaseDifficulty; + IRulesetInfo IBeatmapInfo.Ruleset => Ruleset; + public double StarRating => StarDifficulty; + + #endregion } } diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index 713f80d1fe..fbd47d2614 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.cs @@ -15,7 +15,7 @@ namespace osu.Game.Beatmaps { [ExcludeFromDynamicCompile] [Serializable] - public class BeatmapMetadata : IEquatable, IHasPrimaryKey + public class BeatmapMetadata : IEquatable, IHasPrimaryKey, IBeatmapMetadataInfo { public int ID { get; set; } @@ -128,5 +128,7 @@ namespace osu.Game.Beatmaps && AudioFile == other.AudioFile && BackgroundFile == other.BackgroundFile; } + + string IBeatmapMetadataInfo.Author => AuthorString; } } diff --git a/osu.Game/Beatmaps/BeatmapSetFileInfo.cs b/osu.Game/Beatmaps/BeatmapSetFileInfo.cs index 3a55dc1577..ce50463f05 100644 --- a/osu.Game/Beatmaps/BeatmapSetFileInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetFileInfo.cs @@ -7,7 +7,7 @@ using osu.Game.IO; namespace osu.Game.Beatmaps { - public class BeatmapSetFileInfo : INamedFileInfo, IHasPrimaryKey + public class BeatmapSetFileInfo : INamedFileInfo, IHasPrimaryKey, INamedFileUsage { public int ID { get; set; } @@ -19,5 +19,7 @@ namespace osu.Game.Beatmaps [Required] public string Filename { get; set; } + + public IFileInfo File => FileInfo; } } diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 3b1ff4ced0..739acb9a8d 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -12,7 +12,7 @@ using osu.Game.Database; namespace osu.Game.Beatmaps { [ExcludeFromDynamicCompile] - public class BeatmapSetInfo : IHasPrimaryKey, IHasFiles, ISoftDelete, IEquatable + public class BeatmapSetInfo : IHasPrimaryKey, IHasFiles, ISoftDelete, IEquatable, IBeatmapSetInfo { public int ID { get; set; } @@ -90,5 +90,19 @@ namespace osu.Game.Beatmaps return ReferenceEquals(this, other); } + + #region Implementation of IHasOnlineID + + public int? OnlineID => ID; + + #endregion + + #region Implementation of IBeatmapSetInfo + + IBeatmapMetadataInfo IBeatmapSetInfo.Metadata => Metadata; + IEnumerable IBeatmapSetInfo.Beatmaps => Beatmaps; + IEnumerable IBeatmapSetInfo.Files => Files; + + #endregion } } diff --git a/osu.Game/Beatmaps/IBeatmapInfo.cs b/osu.Game/Beatmaps/IBeatmapInfo.cs index 72a04621f2..8ba8f316ed 100644 --- a/osu.Game/Beatmaps/IBeatmapInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapInfo.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Localisation; +using osu.Game.Database; +using osu.Game.Rulesets; #nullable enable diff --git a/osu.Game/Beatmaps/IBeatmapSetInfo.cs b/osu.Game/Beatmaps/IBeatmapSetInfo.cs index f22115e08f..548a48367c 100644 --- a/osu.Game/Beatmaps/IBeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapSetInfo.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Game.Database; #nullable enable diff --git a/osu.Game/Beatmaps/IHasOnlineID.cs b/osu.Game/Database/IHasOnlineID.cs similarity index 92% rename from osu.Game/Beatmaps/IHasOnlineID.cs rename to osu.Game/Database/IHasOnlineID.cs index dc2793afe5..c55c461d2d 100644 --- a/osu.Game/Beatmaps/IHasOnlineID.cs +++ b/osu.Game/Database/IHasOnlineID.cs @@ -3,7 +3,7 @@ #nullable enable -namespace osu.Game.Beatmaps +namespace osu.Game.Database { public interface IHasOnlineID { diff --git a/osu.Game/Beatmaps/INamedFileUsage.cs b/osu.Game/Database/INamedFileUsage.cs similarity index 92% rename from osu.Game/Beatmaps/INamedFileUsage.cs rename to osu.Game/Database/INamedFileUsage.cs index aa7a3852a7..e558ffe0fb 100644 --- a/osu.Game/Beatmaps/INamedFileUsage.cs +++ b/osu.Game/Database/INamedFileUsage.cs @@ -1,9 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Game.IO; + #nullable enable -namespace osu.Game.Beatmaps +namespace osu.Game.Database { /// /// A usage of a file, with a local filename attached. diff --git a/osu.Game/IO/FileInfo.cs b/osu.Game/IO/FileInfo.cs index e04bfb46cc..331546f9f8 100644 --- a/osu.Game/IO/FileInfo.cs +++ b/osu.Game/IO/FileInfo.cs @@ -6,7 +6,7 @@ using osu.Game.Database; namespace osu.Game.IO { - public class FileInfo : IHasPrimaryKey + public class FileInfo : IHasPrimaryKey, IFileInfo { public int ID { get; set; } diff --git a/osu.Game/Beatmaps/IFileInfo.cs b/osu.Game/IO/IFileInfo.cs similarity index 93% rename from osu.Game/Beatmaps/IFileInfo.cs rename to osu.Game/IO/IFileInfo.cs index 50eb223fc4..080d8e57f5 100644 --- a/osu.Game/Beatmaps/IFileInfo.cs +++ b/osu.Game/IO/IFileInfo.cs @@ -3,7 +3,7 @@ #nullable enable -namespace osu.Game.Beatmaps +namespace osu.Game.IO { /// /// A representation of a tracked file. diff --git a/osu.Game/Beatmaps/IRulesetInfo.cs b/osu.Game/Rulesets/IRulesetInfo.cs similarity index 96% rename from osu.Game/Beatmaps/IRulesetInfo.cs rename to osu.Game/Rulesets/IRulesetInfo.cs index b31ebdfbfd..d4dec0de64 100644 --- a/osu.Game/Beatmaps/IRulesetInfo.cs +++ b/osu.Game/Rulesets/IRulesetInfo.cs @@ -2,11 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Game.Rulesets; +using osu.Game.Database; #nullable enable -namespace osu.Game.Beatmaps +namespace osu.Game.Rulesets { /// /// A representation of a ruleset's metadata. diff --git a/osu.Game/Rulesets/RulesetInfo.cs b/osu.Game/Rulesets/RulesetInfo.cs index 59ec9cdd7e..ca6a083a58 100644 --- a/osu.Game/Rulesets/RulesetInfo.cs +++ b/osu.Game/Rulesets/RulesetInfo.cs @@ -10,7 +10,7 @@ using osu.Framework.Testing; namespace osu.Game.Rulesets { [ExcludeFromDynamicCompile] - public class RulesetInfo : IEquatable + public class RulesetInfo : IEquatable, IRulesetInfo { public int? ID { get; set; } @@ -54,5 +54,11 @@ namespace osu.Game.Rulesets } public override string ToString() => Name ?? $"{Name} ({ShortName}) ID: {ID}"; + + #region Implementation of IHasOnlineID + + public int? OnlineID => ID; + + #endregion } } From 8595eb2d11d8540b01587efe5bd2ec0494639c34 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 14:38:30 +0900 Subject: [PATCH 2173/2442] Switch `BeatmapDifficulty` usages to use interface type --- osu.Game/Beatmaps/BeatmapDifficulty.cs | 45 +------------------ osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs | 2 +- .../Scoring/DrainingHealthProcessor.cs | 3 +- osu.Game/Rulesets/Scoring/HitWindows.cs | 4 +- 4 files changed, 7 insertions(+), 47 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapDifficulty.cs b/osu.Game/Beatmaps/BeatmapDifficulty.cs index 1844b193f2..0443422b31 100644 --- a/osu.Game/Beatmaps/BeatmapDifficulty.cs +++ b/osu.Game/Beatmaps/BeatmapDifficulty.cs @@ -2,10 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Database; +using osu.Game.Models.Interfaces; namespace osu.Game.Beatmaps { - public class BeatmapDifficulty : IHasPrimaryKey + public class BeatmapDifficulty : IHasPrimaryKey, IBeatmapDifficultyInfo { /// /// The default value used for all difficulty settings except and . @@ -49,47 +50,5 @@ namespace osu.Game.Beatmaps difficulty.SliderMultiplier = SliderMultiplier; difficulty.SliderTickRate = SliderTickRate; } - - /// - /// Maps a difficulty value [0, 10] to a two-piece linear range of values. - /// - /// The difficulty value to be mapped. - /// Minimum of the resulting range which will be achieved by a difficulty value of 0. - /// Midpoint of the resulting range which will be achieved by a difficulty value of 5. - /// Maximum of the resulting range which will be achieved by a difficulty value of 10. - /// Value to which the difficulty value maps in the specified range. - public static double DifficultyRange(double difficulty, double min, double mid, double max) - { - if (difficulty > 5) - return mid + (max - mid) * (difficulty - 5) / 5; - if (difficulty < 5) - return mid - (mid - min) * (5 - difficulty) / 5; - - return mid; - } - - /// - /// Maps a difficulty value [0, 10] to a two-piece linear range of values. - /// - /// The difficulty value to be mapped. - /// The values that define the two linear ranges. - /// - /// - /// od0 - /// Minimum of the resulting range which will be achieved by a difficulty value of 0. - /// - /// - /// od5 - /// Midpoint of the resulting range which will be achieved by a difficulty value of 5. - /// - /// - /// od10 - /// Maximum of the resulting range which will be achieved by a difficulty value of 10. - /// - /// - /// - /// Value to which the difficulty value maps in the specified range. - public static double DifficultyRange(double difficulty, (double od0, double od5, double od10) range) - => DifficultyRange(difficulty, range.od0, range.od5, range.od10); } } diff --git a/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs index 6d9fcfcb06..339364d442 100644 --- a/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs @@ -84,7 +84,7 @@ namespace osu.Game.Beatmaps /// /// /// Value to which the difficulty value maps in the specified range. - public static double DifficultyRange(double difficulty, (double od0, double od5, double od10) range) + static double DifficultyRange(double difficulty, (double od0, double od5, double od10) range) => DifficultyRange(difficulty, range.od0, range.od5, range.od10); } } diff --git a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs index cae41e22f4..785a729c6d 100644 --- a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; +using osu.Game.Models.Interfaces; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Utils; @@ -100,7 +101,7 @@ namespace osu.Game.Rulesets.Scoring .First() ))); - targetMinimumHealth = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, min_health_target, mid_health_target, max_health_target); + targetMinimumHealth = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, min_health_target, mid_health_target, max_health_target); // Add back a portion of the amount of HP to be drained, depending on the lenience requested. targetMinimumHealth += drainLenience * (1 - targetMinimumHealth); diff --git a/osu.Game/Rulesets/Scoring/HitWindows.cs b/osu.Game/Rulesets/Scoring/HitWindows.cs index 410614de07..71a2af03fc 100644 --- a/osu.Game/Rulesets/Scoring/HitWindows.cs +++ b/osu.Game/Rulesets/Scoring/HitWindows.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using osu.Game.Beatmaps; +using osu.Game.Models.Interfaces; using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Scoring @@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Scoring { foreach (var range in GetRanges()) { - var value = BeatmapDifficulty.DifficultyRange(difficulty, (range.Min, range.Average, range.Max)); + var value = IBeatmapDifficultyInfo.DifficultyRange(difficulty, (range.Min, range.Average, range.Max)); switch (range.Result) { From a92d499d7a33f938cbeb91a1d174ff6d669c1cd2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 14:56:42 +0900 Subject: [PATCH 2174/2442] Convert usages of `BeatmapDifficulty` to `IBeatmapDifficultyInfo` --- .../TestSceneCatcher.cs | 4 ++-- .../TestSceneCatcherArea.cs | 4 ++-- .../Difficulty/CatchDifficultyCalculator.cs | 2 +- .../Edit/CatchEditorPlayfield.cs | 2 +- .../Objects/CatchHitObject.cs | 4 ++-- .../Objects/JuiceStream.cs | 2 +- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 4 ++-- osu.Game.Rulesets.Catch/UI/Catcher.cs | 6 +++--- .../UI/DrawableCatchRuleset.cs | 2 +- .../Beatmaps/ManiaBeatmapConverter.cs | 2 +- .../Patterns/Legacy/PatternGenerator.cs | 2 +- osu.Game.Rulesets.Mania/Objects/HoldNote.cs | 2 +- .../Checks/CheckTooShortSpinnersTest.cs | 12 +++++------ .../TestSceneObjectOrderedHitPolicy.cs | 2 +- .../TestSceneStartTimeOrderedHitPolicy.cs | 2 +- .../Difficulty/OsuDifficultyCalculator.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 2 +- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 4 ++-- osu.Game.Rulesets.Osu/Objects/Slider.cs | 2 +- .../Objects/SliderEndCircle.cs | 2 +- osu.Game.Rulesets.Osu/Objects/SliderTick.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Spinner.cs | 4 ++-- .../Beatmaps/TaikoBeatmapConverter.cs | 7 ++++--- osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 2 +- .../Scoring/TaikoHealthProcessor.cs | 4 ++-- osu.Game.Tournament/Components/SongBar.cs | 2 +- osu.Game/Beatmaps/BeatmapDifficulty.cs | 21 ++++++++++++++++++- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 2 +- osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs | 2 +- osu.Game/Rulesets/Mods/ModHardRock.cs | 2 +- osu.Game/Rulesets/Objects/HitObject.cs | 4 ++-- .../Rulesets/Objects/Legacy/ConvertSlider.cs | 2 +- .../Scoring/DrainingHealthProcessor.cs | 1 - osu.Game/Rulesets/Scoring/HitWindows.cs | 2 +- .../Screens/Select/Details/AdvancedStats.cs | 4 ++-- osu.Game/osu.Game.csproj | 3 +++ 36 files changed, 75 insertions(+), 53 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index 540f02580f..f291bfed13 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -290,7 +290,7 @@ namespace osu.Game.Rulesets.Catch.Tests { public IEnumerable CaughtObjects => this.ChildrenOfType(); - public TestCatcher(DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty) + public TestCatcher(DroppedObjectContainer droppedObjectTarget, IBeatmapDifficultyInfo difficulty) : base(droppedObjectTarget, difficulty) { } @@ -298,7 +298,7 @@ namespace osu.Game.Rulesets.Catch.Tests public class TestKiaiFruit : Fruit { - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty) { controlPointInfo.Add(0, new EffectControlPoint { KiaiMode = true }); base.ApplyDefaultsToSelf(controlPointInfo, difficulty); diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs index 6abfbdbe21..7cae9b18b9 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Catch.Tests private ScheduledDelegate addManyFruit; - private BeatmapDifficulty beatmapDifficulty; + private IBeatmapDifficultyInfo beatmapDifficulty; public TestSceneCatcherArea() { @@ -120,7 +120,7 @@ namespace osu.Game.Rulesets.Catch.Tests private class TestCatcherArea : CatcherArea { - public TestCatcherArea(BeatmapDifficulty beatmapDifficulty) + public TestCatcherArea(IBeatmapDifficultyInfo beatmapDifficulty) { var droppedObjectContainer = new DroppedObjectContainer(); Add(droppedObjectContainer); diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 82d76252d2..5b1f613f8d 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty return new CatchDifficultyAttributes { Mods = mods, Skills = skills }; // this is the same as osu!, so there's potential to share the implementation... maybe - double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; + double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; return new CatchDifficultyAttributes { diff --git a/osu.Game.Rulesets.Catch/Edit/CatchEditorPlayfield.cs b/osu.Game.Rulesets.Catch/Edit/CatchEditorPlayfield.cs index 8c9f292aa9..046ba0ebce 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchEditorPlayfield.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchEditorPlayfield.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Catch.Edit public class CatchEditorPlayfield : CatchPlayfield { // TODO fixme: the size of the catcher is not changed when circle size is changed in setup screen. - public CatchEditorPlayfield(BeatmapDifficulty difficulty) + public CatchEditorPlayfield(IBeatmapDifficultyInfo difficulty) : base(difficulty) { } diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index d43e6f1c8b..ee10cf9711 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -128,11 +128,11 @@ namespace osu.Game.Rulesets.Catch.Objects /// public int RandomSeed => (int)StartTime; - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty) { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); - TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450); + TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450); Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2; } diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index a8ad34fcbe..0d6925a83d 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Catch.Objects /// public double SpanDuration => Duration / this.SpanCount(); - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty) { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 1e20643a08..df32d917ce 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -34,9 +34,9 @@ namespace osu.Game.Rulesets.Catch.UI internal CatcherArea CatcherArea { get; private set; } - private readonly BeatmapDifficulty difficulty; + private readonly IBeatmapDifficultyInfo difficulty; - public CatchPlayfield(BeatmapDifficulty difficulty) + public CatchPlayfield(IBeatmapDifficultyInfo difficulty) { this.difficulty = difficulty; } diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 5cd85aac56..3745099010 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -124,7 +124,7 @@ namespace osu.Game.Rulesets.Catch.UI private readonly DrawablePool caughtBananaPool; private readonly DrawablePool caughtDropletPool; - public Catcher([NotNull] DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty = null) + public Catcher([NotNull] DroppedObjectContainer droppedObjectTarget, IBeatmapDifficultyInfo difficulty = null) { this.droppedObjectTarget = droppedObjectTarget; @@ -172,7 +172,7 @@ namespace osu.Game.Rulesets.Catch.UI /// /// Calculates the scale of the catcher based off the provided beatmap difficulty. /// - private static Vector2 calculateScale(BeatmapDifficulty difficulty) => new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5); + private static Vector2 calculateScale(IBeatmapDifficultyInfo difficulty) => new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5); /// /// Calculates the width of the area used for attempting catches in gameplay. @@ -184,7 +184,7 @@ namespace osu.Game.Rulesets.Catch.UI /// Calculates the width of the area used for attempting catches in gameplay. /// /// The beatmap difficulty. - public static float CalculateCatchWidth(BeatmapDifficulty difficulty) => CalculateCatchWidth(calculateScale(difficulty)); + public static float CalculateCatchWidth(IBeatmapDifficultyInfo difficulty) => CalculateCatchWidth(calculateScale(difficulty)); /// /// Determine if this catcher can catch a in the current position. diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs index 8b6a074426..ba6e9224c9 100644 --- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Catch.UI : base(ruleset, beatmap, mods) { Direction.Value = ScrollingDirection.Down; - TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450); + TimeRange.Value = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450); } protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 26393c8edb..9745285b38 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps protected override Beatmap ConvertBeatmap(IBeatmap original, CancellationToken cancellationToken) { - BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty; + IBeatmapDifficultyInfo difficulty = original.BeatmapInfo.BaseDifficulty; int seed = (int)MathF.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)MathF.Round(difficulty.ApproachRate); Random = new FastRandom(seed); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs index e643b82271..d65e78bb49 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs @@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy if (drainTime == 0) drainTime = 10000; - BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty; + IBeatmapDifficultyInfo difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty; conversionDifficulty = ((difficulty.DrainRate + Math.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + (double)OriginalBeatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15; conversionDifficulty = Math.Min(conversionDifficulty.Value, 12); diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs index 43e876b7aa..c1937af7e4 100644 --- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs @@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Mania.Objects /// private double tickSpacing = 50; - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty) { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckTooShortSpinnersTest.cs b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckTooShortSpinnersTest.cs index 6a3f168ee1..787807a8ea 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckTooShortSpinnersTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckTooShortSpinnersTest.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks public class CheckTooShortSpinnersTest { private CheckTooShortSpinners check; - private BeatmapDifficulty difficulty; + private IBeatmapDifficultyInfo difficulty; [SetUp] public void Setup() @@ -81,12 +81,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks assertTooShort(new List { spinnerHighOd }, difficultyHighOd); } - private void assertOk(List hitObjects, BeatmapDifficulty beatmapDifficulty) + private void assertOk(List hitObjects, IBeatmapDifficultyInfo beatmapDifficulty) { Assert.That(check.Run(getContext(hitObjects, beatmapDifficulty)), Is.Empty); } - private void assertVeryShort(List hitObjects, BeatmapDifficulty beatmapDifficulty) + private void assertVeryShort(List hitObjects, IBeatmapDifficultyInfo beatmapDifficulty) { var issues = check.Run(getContext(hitObjects, beatmapDifficulty)).ToList(); @@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks Assert.That(issues.First().Template is CheckTooShortSpinners.IssueTemplateVeryShort); } - private void assertTooShort(List hitObjects, BeatmapDifficulty beatmapDifficulty) + private void assertTooShort(List hitObjects, IBeatmapDifficultyInfo beatmapDifficulty) { var issues = check.Run(getContext(hitObjects, beatmapDifficulty)).ToList(); @@ -102,12 +102,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks Assert.That(issues.First().Template is CheckTooShortSpinners.IssueTemplateTooShort); } - private BeatmapVerifierContext getContext(List hitObjects, BeatmapDifficulty beatmapDifficulty) + private BeatmapVerifierContext getContext(List hitObjects, IBeatmapDifficultyInfo beatmapDifficulty) { var beatmap = new Beatmap { HitObjects = hitObjects, - BeatmapInfo = new BeatmapInfo { BaseDifficulty = beatmapDifficulty } + BeatmapInfo = new BeatmapInfo { BaseDifficulty = new BeatmapDifficulty(beatmapDifficulty) } }; return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap)); diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneObjectOrderedHitPolicy.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneObjectOrderedHitPolicy.cs index 77a68b714b..cfce80a2b2 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneObjectOrderedHitPolicy.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneObjectOrderedHitPolicy.cs @@ -452,7 +452,7 @@ namespace osu.Game.Rulesets.Osu.Tests private class TestSpinner : Spinner { - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty) { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); SpinsRequired = 1; diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneStartTimeOrderedHitPolicy.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneStartTimeOrderedHitPolicy.cs index 177a4f50a1..1b85e0efde 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneStartTimeOrderedHitPolicy.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneStartTimeOrderedHitPolicy.cs @@ -412,7 +412,7 @@ namespace osu.Game.Rulesets.Osu.Tests private class TestSpinner : Spinner { - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty) { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); SpinsRequired = 1; diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 4c8d0b2ce6..a8f10f44dc 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty double starRating = basePerformance > 0.00001 ? Math.Cbrt(1.12) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0; - double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; + double preempt = (int)IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; int maxCombo = beatmap.HitObjects.Count; // Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 210d5e0403..b0c655b106 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -113,7 +113,7 @@ namespace osu.Game.Rulesets.Osu.Mods #region Reduce AR (IApplicableToDifficulty) - public void ReadFromDifficulty(BeatmapDifficulty difficulty) + public void ReadFromDifficulty(IBeatmapDifficultyInfo difficulty) { } diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index 36629fa41e..7c45b2bc07 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -122,11 +122,11 @@ namespace osu.Game.Rulesets.Osu.Objects }); } - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty) { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); - TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, PREEMPT_MIN); + TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, 1800, 1200, PREEMPT_MIN); // Preempt time can go below 450ms. Normally, this is achieved via the DT mod which uniformly speeds up all animations game wide regardless of AR. // This uniform speedup is hard to match 1:1, however we can at least make AR>10 (via mods) feel good by extending the upper linear function above. diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index c4420b1e87..1d2666f46b 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -135,7 +135,7 @@ namespace osu.Game.Rulesets.Osu.Objects Path.Version.ValueChanged += _ => updateNestedPositions(); } - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty) { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); diff --git a/osu.Game.Rulesets.Osu/Objects/SliderEndCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderEndCircle.cs index a6aed2c00e..f893559548 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderEndCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderEndCircle.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Objects public double SpanDuration => slider.SpanDuration; - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty) { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs index 725dbe81fb..e7e64954e9 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Objects public int SpanIndex { get; set; } public double SpanStartTime { get; set; } - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty) { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs index 194aa640f9..f85dc0d391 100644 --- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Objects /// public int MaximumBonusSpins { get; protected set; } = 1; - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty) { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Objects double secondsDuration = Duration / 1000; - double minimumRotationsPerSecond = stable_matching_fudge * BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5); + double minimumRotationsPerSecond = stable_matching_fudge * IBeatmapDifficultyInfo.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5); SpinsRequired = (int)(secondsDuration * minimumRotationsPerSecond); MaximumBonusSpins = (int)((maximum_rotations_per_second - minimumRotationsPerSecond) * secondsDuration); diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 9b73e644c5..3b5b972c01 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -117,7 +117,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps case IHasDuration endTimeData: { - double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier; + double hitMultiplier = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier; yield return new Swell { @@ -193,9 +193,10 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps private class TaikoMutliplierAppliedDifficulty : BeatmapDifficulty { - public TaikoMutliplierAppliedDifficulty(BeatmapDifficulty difficulty) + public TaikoMutliplierAppliedDifficulty(IBeatmapDifficultyInfo difficulty) { - difficulty.CopyTo(this); + CopyFrom(difficulty); + SliderMultiplier *= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; } } diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index b0634295d0..0318e32991 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Taiko.Objects private float overallDifficulty = BeatmapDifficulty.DEFAULT_DIFFICULTY; - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty) { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs index f7a1d130eb..94cd411d7b 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs @@ -40,8 +40,8 @@ namespace osu.Game.Rulesets.Taiko.Scoring { base.ApplyBeatmap(beatmap); - hpMultiplier = 1 / (object_count_factor * Math.Max(1, beatmap.HitObjects.OfType().Count()) * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98)); - hpMissMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120); + hpMultiplier = 1 / (object_count_factor * Math.Max(1, beatmap.HitObjects.OfType().Count()) * IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98)); + hpMissMultiplier = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120); } protected override double GetHealthIncreaseFor(JudgementResult result) diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index 6080f7b636..7bb01ddc6d 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -117,7 +117,7 @@ namespace osu.Game.Tournament.Components if ((mods & LegacyMods.DoubleTime) > 0) { // temporary local calculation (taken from OsuDifficultyCalculator) - double preempt = (int)BeatmapDifficulty.DifficultyRange(ar, 1800, 1200, 450) / 1.5; + double preempt = (int)IBeatmapDifficultyInfo.DifficultyRange(ar, 1800, 1200, 450) / 1.5; ar = (float)(preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5); bpm *= 1.5f; diff --git a/osu.Game/Beatmaps/BeatmapDifficulty.cs b/osu.Game/Beatmaps/BeatmapDifficulty.cs index 0443422b31..2bb0787b4c 100644 --- a/osu.Game/Beatmaps/BeatmapDifficulty.cs +++ b/osu.Game/Beatmaps/BeatmapDifficulty.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Database; -using osu.Game.Models.Interfaces; namespace osu.Game.Beatmaps { @@ -21,6 +20,15 @@ namespace osu.Game.Beatmaps private float? approachRate; + public BeatmapDifficulty() + { + } + + public BeatmapDifficulty(IBeatmapDifficultyInfo source) + { + CopyFrom(source); + } + public float ApproachRate { get => approachRate ?? OverallDifficulty; @@ -40,6 +48,17 @@ namespace osu.Game.Beatmaps return diff; } + public void CopyFrom(IBeatmapDifficultyInfo difficulty) + { + ApproachRate = difficulty.ApproachRate; + DrainRate = difficulty.DrainRate; + CircleSize = difficulty.CircleSize; + OverallDifficulty = difficulty.OverallDifficulty; + + SliderMultiplier = difficulty.SliderMultiplier; + SliderTickRate = difficulty.SliderTickRate; + } + public void CopyTo(BeatmapDifficulty difficulty) { difficulty.ApproachRate = ApproachRate; diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index 82e90399c9..b7529f39ca 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -109,7 +109,7 @@ namespace osu.Game.Rulesets.Edit } /// - /// Invokes , + /// Invokes , /// refreshing and parameters for the . /// protected void ApplyDefaultsToHitObject() => HitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty); diff --git a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs index b78c30e8a5..eefa1531c4 100644 --- a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs @@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Mods } } - public void ReadFromDifficulty(BeatmapDifficulty difficulty) + public void ReadFromDifficulty(IBeatmapDifficultyInfo difficulty) { } diff --git a/osu.Game/Rulesets/Mods/ModHardRock.cs b/osu.Game/Rulesets/Mods/ModHardRock.cs index 4edcb0b074..da838f9ea6 100644 --- a/osu.Game/Rulesets/Mods/ModHardRock.cs +++ b/osu.Game/Rulesets/Mods/ModHardRock.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Mods public override string Description => "Everything just got a bit harder..."; public override Type[] IncompatibleMods => new[] { typeof(ModEasy), typeof(ModDifficultyAdjust) }; - public void ReadFromDifficulty(BeatmapDifficulty difficulty) + public void ReadFromDifficulty(IBeatmapDifficultyInfo difficulty) { } diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index ae0cb895bc..0b159819d4 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Objects /// The control points. /// The difficulty settings to use. /// The cancellation token. - public void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty, CancellationToken cancellationToken = default) + public void ApplyDefaults(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty, CancellationToken cancellationToken = default) { ApplyDefaultsToSelf(controlPointInfo, difficulty); @@ -142,7 +142,7 @@ namespace osu.Game.Rulesets.Objects DefaultsApplied?.Invoke(this); } - protected virtual void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + protected virtual void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty) { Kiai = controlPointInfo.EffectPointAt(StartTime + control_point_leniency).KiaiMode; diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs index df569b91c1..e1de82ade7 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Objects.Legacy public double Velocity = 1; - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty) { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); diff --git a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs index 785a729c6d..85693abb93 100644 --- a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; -using osu.Game.Models.Interfaces; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Utils; diff --git a/osu.Game/Rulesets/Scoring/HitWindows.cs b/osu.Game/Rulesets/Scoring/HitWindows.cs index 71a2af03fc..3ffd1eb66b 100644 --- a/osu.Game/Rulesets/Scoring/HitWindows.cs +++ b/osu.Game/Rulesets/Scoring/HitWindows.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using osu.Game.Models.Interfaces; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Scoring diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 53e30fd9ca..a855322e57 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -106,12 +106,12 @@ namespace osu.Game.Screens.Select.Details private void updateStatistics() { - BeatmapDifficulty baseDifficulty = Beatmap?.BaseDifficulty; + IBeatmapDifficultyInfo baseDifficulty = Beatmap?.BaseDifficulty; BeatmapDifficulty adjustedDifficulty = null; if (baseDifficulty != null && mods.Value.Any(m => m is IApplicableToDifficulty)) { - adjustedDifficulty = baseDifficulty.Clone(); + adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); foreach (var mod in mods.Value.OfType()) mod.ApplyToDifficulty(adjustedDifficulty); diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ba118c5240..c8914353f8 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -43,4 +43,7 @@ + + + From 05996cc2e9232e7c54e1892ca2c3a253e7783513 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 17:03:08 +0900 Subject: [PATCH 2175/2442] Add changes that got forgotted in branch surgery --- .../Difficulty/CatchDifficultyCalculator.cs | 2 +- osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs | 2 +- osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs | 2 +- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 2 +- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Spinner.cs | 2 +- osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs | 2 +- osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs | 4 ++-- osu.Game.Tournament/Components/SongBar.cs | 2 +- osu.Game/Beatmaps/BeatmapDifficulty.cs | 1 - osu.Game/Beatmaps/BeatmapInfo.cs | 1 - osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs | 1 - osu.Game/Rulesets/Scoring/HitWindows.cs | 2 +- 13 files changed, 11 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 82d76252d2..5b1f613f8d 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty return new CatchDifficultyAttributes { Mods = mods, Skills = skills }; // this is the same as osu!, so there's potential to share the implementation... maybe - double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; + double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; return new CatchDifficultyAttributes { diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index d43e6f1c8b..32fdc9f62d 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -132,7 +132,7 @@ namespace osu.Game.Rulesets.Catch.Objects { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); - TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450); + TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450); Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2; } diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs index 8b6a074426..ba6e9224c9 100644 --- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Catch.UI : base(ruleset, beatmap, mods) { Direction.Value = ScrollingDirection.Down; - TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450); + TimeRange.Value = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450); } protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay); diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 4c8d0b2ce6..a8f10f44dc 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty double starRating = basePerformance > 0.00001 ? Math.Cbrt(1.12) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0; - double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; + double preempt = (int)IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; int maxCombo = beatmap.HitObjects.Count; // Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above) diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index 36629fa41e..7015c5bce2 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Osu.Objects { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); - TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, PREEMPT_MIN); + TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, 1800, 1200, PREEMPT_MIN); // Preempt time can go below 450ms. Normally, this is achieved via the DT mod which uniformly speeds up all animations game wide regardless of AR. // This uniform speedup is hard to match 1:1, however we can at least make AR>10 (via mods) feel good by extending the upper linear function above. diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs index 194aa640f9..d5f08f049c 100644 --- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Objects double secondsDuration = Duration / 1000; - double minimumRotationsPerSecond = stable_matching_fudge * BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5); + double minimumRotationsPerSecond = stable_matching_fudge * IBeatmapDifficultyInfo.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5); SpinsRequired = (int)(secondsDuration * minimumRotationsPerSecond); MaximumBonusSpins = (int)((maximum_rotations_per_second - minimumRotationsPerSecond) * secondsDuration); diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 9b73e644c5..7068e469d2 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -117,7 +117,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps case IHasDuration endTimeData: { - double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier; + double hitMultiplier = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier; yield return new Swell { diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs index f7a1d130eb..94cd411d7b 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs @@ -40,8 +40,8 @@ namespace osu.Game.Rulesets.Taiko.Scoring { base.ApplyBeatmap(beatmap); - hpMultiplier = 1 / (object_count_factor * Math.Max(1, beatmap.HitObjects.OfType().Count()) * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98)); - hpMissMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120); + hpMultiplier = 1 / (object_count_factor * Math.Max(1, beatmap.HitObjects.OfType().Count()) * IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98)); + hpMissMultiplier = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120); } protected override double GetHealthIncreaseFor(JudgementResult result) diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index 6080f7b636..7bb01ddc6d 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -117,7 +117,7 @@ namespace osu.Game.Tournament.Components if ((mods & LegacyMods.DoubleTime) > 0) { // temporary local calculation (taken from OsuDifficultyCalculator) - double preempt = (int)BeatmapDifficulty.DifficultyRange(ar, 1800, 1200, 450) / 1.5; + double preempt = (int)IBeatmapDifficultyInfo.DifficultyRange(ar, 1800, 1200, 450) / 1.5; ar = (float)(preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5); bpm *= 1.5f; diff --git a/osu.Game/Beatmaps/BeatmapDifficulty.cs b/osu.Game/Beatmaps/BeatmapDifficulty.cs index 0443422b31..1bb1c86c1f 100644 --- a/osu.Game/Beatmaps/BeatmapDifficulty.cs +++ b/osu.Game/Beatmaps/BeatmapDifficulty.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Database; -using osu.Game.Models.Interfaces; namespace osu.Game.Beatmaps { diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index d2b47ef1a4..d6f3bf0de4 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -24,7 +24,6 @@ namespace osu.Game.Beatmaps public int BeatmapVersion; private int? onlineBeatmapID; - private IRulesetInfo ruleset; [JsonProperty("id")] public int? OnlineBeatmapID diff --git a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs index 785a729c6d..85693abb93 100644 --- a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; -using osu.Game.Models.Interfaces; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Utils; diff --git a/osu.Game/Rulesets/Scoring/HitWindows.cs b/osu.Game/Rulesets/Scoring/HitWindows.cs index 71a2af03fc..3ffd1eb66b 100644 --- a/osu.Game/Rulesets/Scoring/HitWindows.cs +++ b/osu.Game/Rulesets/Scoring/HitWindows.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using osu.Game.Models.Interfaces; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Scoring From 00e33a1da75496db67ccea0269e7f5d174b02f3d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 17:06:09 +0900 Subject: [PATCH 2176/2442] Fix incorrect `OnlineID` mappings --- osu.Game/Beatmaps/BeatmapInfo.cs | 2 +- osu.Game/Beatmaps/BeatmapSetInfo.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index d6f3bf0de4..1b3e67e2e7 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -190,7 +190,7 @@ namespace osu.Game.Beatmaps #region Implementation of IHasOnlineID - public int? OnlineID => ID; + public int? OnlineID => OnlineBeatmapID; #endregion diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 739acb9a8d..7e26b154a9 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -93,7 +93,7 @@ namespace osu.Game.Beatmaps #region Implementation of IHasOnlineID - public int? OnlineID => ID; + public int? OnlineID => OnlineBeatmapSetID; #endregion From 9dae92e78cf9db6caf0b1e659d17be04017630d6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 17:22:25 +0900 Subject: [PATCH 2177/2442] Add missing backlink to `BeatmapSet` from `Beatmap` and fix non-explicit implementations --- osu.Game/Beatmaps/BeatmapInfo.cs | 7 ++++--- osu.Game/Beatmaps/IBeatmapInfo.cs | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 1b3e67e2e7..83e547218b 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -196,11 +196,12 @@ namespace osu.Game.Beatmaps #region Implementation of IBeatmapInfo - public string DifficultyName => Version; + string IBeatmapInfo.DifficultyName => Version; IBeatmapMetadataInfo IBeatmapInfo.Metadata => Metadata; - public IBeatmapDifficultyInfo Difficulty => BaseDifficulty; + IBeatmapDifficultyInfo IBeatmapInfo.Difficulty => BaseDifficulty; + IBeatmapSetInfo IBeatmapInfo.BeatmapSet => BeatmapSet; IRulesetInfo IBeatmapInfo.Ruleset => Ruleset; - public double StarRating => StarDifficulty; + double IBeatmapInfo.StarRating => StarDifficulty; #endregion } diff --git a/osu.Game/Beatmaps/IBeatmapInfo.cs b/osu.Game/Beatmaps/IBeatmapInfo.cs index 8ba8f316ed..fa2ce2949b 100644 --- a/osu.Game/Beatmaps/IBeatmapInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapInfo.cs @@ -29,6 +29,11 @@ namespace osu.Game.Beatmaps /// IBeatmapDifficultyInfo Difficulty { get; } + /// + /// The beatmap set this beatmap is part of. + /// + IBeatmapSetInfo BeatmapSet { get; } + /// /// The playable length in milliseconds of this beatmap. /// From d6618a99a3b885e0163043c76b011d648c5367af Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 17:31:27 +0900 Subject: [PATCH 2178/2442] Redirect more methods to interface implementations --- osu.Game/Beatmaps/BeatmapInfo.cs | 8 ++--- osu.Game/Beatmaps/BeatmapMetadata.cs | 46 +++------------------------- osu.Game/Beatmaps/BeatmapSetInfo.cs | 2 +- 3 files changed, 8 insertions(+), 48 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 83e547218b..09f237a5de 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -157,13 +157,9 @@ namespace osu.Game.Beatmaps Version }.Concat(Metadata?.SearchableTerms ?? Enumerable.Empty()).Where(s => !string.IsNullOrEmpty(s)).ToArray(); - public override string ToString() => $"{Metadata ?? BeatmapSet?.Metadata} {versionString}".Trim(); + public override string ToString() => ((IBeatmapInfo)this).DisplayTitle; - public RomanisableString ToRomanisableString() - { - var metadata = (Metadata ?? BeatmapSet?.Metadata)?.ToRomanisableString() ?? new RomanisableString(null, null); - return new RomanisableString($"{metadata.GetPreferred(true)} {versionString}".Trim(), $"{metadata.GetPreferred(false)} {versionString}".Trim()); - } + public RomanisableString ToRomanisableString() => ((IBeatmapInfo)this).DisplayTitleRomanisable; public bool Equals(BeatmapInfo other) { diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index fbd47d2614..3da80580cb 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; using Newtonsoft.Json; using osu.Framework.Localisation; using osu.Framework.Testing; @@ -83,51 +82,16 @@ namespace osu.Game.Beatmaps public int PreviewTime { get; set; } public string AudioFile { get; set; } + public string BackgroundFile { get; set; } - public override string ToString() - { - string author = Author == null ? string.Empty : $"({Author})"; - return $"{Artist} - {Title} {author}".Trim(); - } + public bool Equals(BeatmapMetadata other) => ((IBeatmapMetadataInfo)this).Equals(other); - public RomanisableString ToRomanisableString() - { - string author = Author == null ? string.Empty : $"({Author})"; - var artistUnicode = string.IsNullOrEmpty(ArtistUnicode) ? Artist : ArtistUnicode; - var titleUnicode = string.IsNullOrEmpty(TitleUnicode) ? Title : TitleUnicode; + public override string ToString() => ((IBeatmapMetadataInfo)this).DisplayTitle; - return new RomanisableString($"{artistUnicode} - {titleUnicode} {author}".Trim(), $"{Artist} - {Title} {author}".Trim()); - } + public RomanisableString ToRomanisableString() => ((IBeatmapMetadataInfo)this).DisplayTitleRomanisable; - [JsonIgnore] - public string[] SearchableTerms => new[] - { - Author?.Username, - Artist, - ArtistUnicode, - Title, - TitleUnicode, - Source, - Tags - }.Where(s => !string.IsNullOrEmpty(s)).ToArray(); - - public bool Equals(BeatmapMetadata other) - { - if (other == null) - return false; - - return Title == other.Title - && TitleUnicode == other.TitleUnicode - && Artist == other.Artist - && ArtistUnicode == other.ArtistUnicode - && AuthorString == other.AuthorString - && Source == other.Source - && Tags == other.Tags - && PreviewTime == other.PreviewTime - && AudioFile == other.AudioFile - && BackgroundFile == other.BackgroundFile; - } + public IEnumerable SearchableTerms => ((IBeatmapMetadataInfo)this).SearchableTerms; string IBeatmapMetadataInfo.Author => AuthorString; } diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 7e26b154a9..4804c7032c 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -61,7 +61,7 @@ namespace osu.Game.Beatmaps public string Hash { get; set; } - public string StoryboardFile => Files.Find(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename; + public string StoryboardFile => ((IBeatmapSetInfo)this).StoryboardFile; /// /// Returns the storage path for the file in this beatmapset with the given filename, if any exists, otherwise null. From 0daf8937e3a954f0ea595e5a2c4131bdb616e0a2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 17:33:57 +0900 Subject: [PATCH 2179/2442] Add missing xmldoc --- osu.Game/Beatmaps/IBeatmapInfo.cs | 6 ++++++ osu.Game/Beatmaps/IBeatmapMetadataInfo.cs | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/osu.Game/Beatmaps/IBeatmapInfo.cs b/osu.Game/Beatmaps/IBeatmapInfo.cs index fa2ce2949b..6153a0af08 100644 --- a/osu.Game/Beatmaps/IBeatmapInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapInfo.cs @@ -64,8 +64,14 @@ namespace osu.Game.Beatmaps /// double StarRating { get; } + /// + /// A user-presentable display title representing this metadata. + /// string DisplayTitle => $"{Metadata} {versionString}".Trim(); + /// + /// A user-presentable display title representing this beatmap, with localisation handling for potentially romanisable fields. + /// RomanisableString DisplayTitleRomanisable { get diff --git a/osu.Game/Beatmaps/IBeatmapMetadataInfo.cs b/osu.Game/Beatmaps/IBeatmapMetadataInfo.cs index 18dd38b404..d0dae296a0 100644 --- a/osu.Game/Beatmaps/IBeatmapMetadataInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapMetadataInfo.cs @@ -65,6 +65,9 @@ namespace osu.Game.Beatmaps /// string BackgroundFile { get; } + /// + /// A user-presentable display title representing this metadata. + /// string DisplayTitle { get @@ -74,6 +77,9 @@ namespace osu.Game.Beatmaps } } + /// + /// A user-presentable display title representing this metadata, with localisation handling for potentially romanisable fields. + /// RomanisableString DisplayTitleRomanisable { get @@ -86,6 +92,9 @@ namespace osu.Game.Beatmaps } } + /// + /// An array of all searchable terms provided in contained metadata. + /// string[] SearchableTerms => new[] { Author, From 3faafd7200576bee1016ba6c75d2643e1d7af751 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 18:24:46 +0900 Subject: [PATCH 2180/2442] Rename parameter to `repeatCount` and add guards --- .../Formats/LegacyStoryboardDecoder.cs | 4 ++-- osu.Game/Storyboards/CommandLoop.cs | 23 ++++++++++++++----- osu.Game/Storyboards/StoryboardSprite.cs | 10 ++++---- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 6301c42deb..5b03212da4 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -176,8 +176,8 @@ namespace osu.Game.Beatmaps.Formats case "L": { var startTime = Parsing.ParseDouble(split[1]); - var loopCount = Parsing.ParseInt(split[2]); - timelineGroup = storyboardSprite?.AddLoop(startTime, loopCount); + var repeatCount = Parsing.ParseInt(split[2]); + timelineGroup = storyboardSprite?.AddLoop(startTime, Math.Max(0, repeatCount)); break; } diff --git a/osu.Game/Storyboards/CommandLoop.cs b/osu.Game/Storyboards/CommandLoop.cs index c17436d813..66db965803 100644 --- a/osu.Game/Storyboards/CommandLoop.cs +++ b/osu.Game/Storyboards/CommandLoop.cs @@ -9,20 +9,31 @@ namespace osu.Game.Storyboards public class CommandLoop : CommandTimelineGroup { public double LoopStartTime; - public int LoopCount; + + /// + /// The total number of times this loop is played back. Always greater than zero. + /// + public readonly int TotalIterations; public override double StartTime => LoopStartTime + CommandsStartTime; - public override double EndTime => StartTime + CommandsDuration * LoopCount; + public override double EndTime => StartTime + CommandsDuration * TotalIterations; - public CommandLoop(double startTime, int loopCount) + /// + /// Construct a new command loop. + /// + /// The start time of the loop. + /// The number of times the loop should repeat. Should be greater than zero. Zero means a single playback. + public CommandLoop(double startTime, int repeatCount) { + if (repeatCount < 0) throw new ArgumentException("Repeat count must be zero or above.", nameof(repeatCount)); + LoopStartTime = startTime; - LoopCount = Math.Max(1, loopCount); + TotalIterations = repeatCount + 1; } public override IEnumerable.TypedCommand> GetCommands(CommandTimelineSelector timelineSelector, double offset = 0) { - for (var loop = 0; loop < LoopCount; loop++) + for (var loop = 0; loop < TotalIterations; loop++) { var loopOffset = LoopStartTime + loop * CommandsDuration; foreach (var command in base.GetCommands(timelineSelector, offset + loopOffset)) @@ -31,6 +42,6 @@ namespace osu.Game.Storyboards } public override string ToString() - => $"{LoopStartTime} x{LoopCount}"; + => $"{LoopStartTime} x{TotalIterations}"; } } diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index bf87e7d10e..6fb2f5994b 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -1,13 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osuTK; -using osu.Framework.Graphics; -using osu.Game.Storyboards.Drawables; using System; using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; +using osu.Framework.Graphics; +using osu.Game.Storyboards.Drawables; +using osuTK; namespace osu.Game.Storyboards { @@ -78,9 +78,9 @@ namespace osu.Game.Storyboards InitialPosition = initialPosition; } - public CommandLoop AddLoop(double startTime, int loopCount) + public CommandLoop AddLoop(double startTime, int repeatCount) { - var loop = new CommandLoop(startTime, loopCount); + var loop = new CommandLoop(startTime, repeatCount); loops.Add(loop); return loop; } From 4c28749d7310a658e9a8329f39064afb03306311 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 19:05:08 +0900 Subject: [PATCH 2181/2442] Fix incorrect legacy decoder usage --- osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 5b03212da4..0f15e28c00 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -177,7 +177,7 @@ namespace osu.Game.Beatmaps.Formats { var startTime = Parsing.ParseDouble(split[1]); var repeatCount = Parsing.ParseInt(split[2]); - timelineGroup = storyboardSprite?.AddLoop(startTime, Math.Max(0, repeatCount)); + timelineGroup = storyboardSprite?.AddLoop(startTime, Math.Max(0, repeatCount - 1)); break; } From 5820a7165268fcd9776e9fd1b33c27648884349d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 1 Oct 2021 19:57:45 +0900 Subject: [PATCH 2182/2442] Fix mania difficulty calculator crashing --- .../Difficulty/ManiaDifficultyCalculator.cs | 8 ++-- .../Difficulty/DifficultyCalculator.cs | 38 +++++++++++-------- .../HUD/DefaultPerformancePointsCounter.cs | 4 +- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index a7a6677b68..fc29eadedc 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty Mods = mods, // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future GreatHitWindow = (int)Math.Ceiling(getHitWindow300(mods) / clockRate), - ScoreMultiplier = getScoreMultiplier(beatmap, mods), + ScoreMultiplier = getScoreMultiplier(mods), MaxCombo = beatmap.HitObjects.Sum(h => h is HoldNote ? 2 : 1), Skills = skills }; @@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[] { - new Strain(mods, ((ManiaBeatmap)beatmap).TotalColumns) + new Strain(mods, ((ManiaBeatmap)Beatmap).TotalColumns) }; protected override Mod[] DifficultyAdjustmentMods @@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty } } - private double getScoreMultiplier(IBeatmap beatmap, Mod[] mods) + private double getScoreMultiplier(Mod[] mods) { double scoreMultiplier = 1; @@ -154,7 +154,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty } } - var maniaBeatmap = (ManiaBeatmap)beatmap; + var maniaBeatmap = (ManiaBeatmap)Beatmap; int diff = maniaBeatmap.TotalColumns - maniaBeatmap.OriginalTotalColumns; if (diff > 0) diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index f38949d982..780c2ad491 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -18,13 +18,17 @@ namespace osu.Game.Rulesets.Difficulty { public abstract class DifficultyCalculator { - private readonly Ruleset ruleset; - private readonly WorkingBeatmap beatmap; + /// + /// The beatmap for which difficulty will be calculated. + /// + protected IBeatmap Beatmap { get; private set; } - private IBeatmap playableBeatmap; private Mod[] playableMods; private double clockRate; + private readonly Ruleset ruleset; + private readonly WorkingBeatmap beatmap; + protected DifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) { this.ruleset = ruleset; @@ -40,10 +44,10 @@ namespace osu.Game.Rulesets.Difficulty { preProcess(mods); - var skills = CreateSkills(playableBeatmap, playableMods, clockRate); + var skills = CreateSkills(Beatmap, playableMods, clockRate); - if (!playableBeatmap.HitObjects.Any()) - return CreateDifficultyAttributes(playableBeatmap, playableMods, skills, clockRate); + if (!Beatmap.HitObjects.Any()) + return CreateDifficultyAttributes(Beatmap, playableMods, skills, clockRate); foreach (var hitObject in getDifficultyHitObjects()) { @@ -51,18 +55,18 @@ namespace osu.Game.Rulesets.Difficulty skill.ProcessInternal(hitObject); } - return CreateDifficultyAttributes(playableBeatmap, playableMods, skills, clockRate); + return CreateDifficultyAttributes(Beatmap, playableMods, skills, clockRate); } public IEnumerable CalculateTimed(params Mod[] mods) { preProcess(mods); - if (!playableBeatmap.HitObjects.Any()) + if (!Beatmap.HitObjects.Any()) yield break; - var skills = CreateSkills(playableBeatmap, playableMods, clockRate); - var progressiveBeatmap = new ProgressiveCalculationBeatmap(playableBeatmap); + var skills = CreateSkills(Beatmap, playableMods, clockRate); + var progressiveBeatmap = new ProgressiveCalculationBeatmap(Beatmap); foreach (var hitObject in getDifficultyHitObjects()) { @@ -93,7 +97,7 @@ namespace osu.Game.Rulesets.Difficulty /// /// Retrieves the s to calculate against. /// - private IEnumerable getDifficultyHitObjects() => SortObjects(CreateDifficultyHitObjects(playableBeatmap, clockRate)); + private IEnumerable getDifficultyHitObjects() => SortObjects(CreateDifficultyHitObjects(Beatmap, clockRate)); /// /// Performs required tasks before every calculation. @@ -102,7 +106,7 @@ namespace osu.Game.Rulesets.Difficulty private void preProcess(Mod[] mods) { playableMods = mods.Select(m => m.DeepClone()).ToArray(); - playableBeatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods); + Beatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods); var track = new TrackVirtual(10000); mods.OfType().ForEach(m => m.ApplyToTrack(track)); @@ -118,7 +122,7 @@ namespace osu.Game.Rulesets.Difficulty => input.OrderBy(h => h.BaseObject.StartTime); /// - /// Creates all combinations which adjust the difficulty. + /// Creates all combinations which adjust the difficulty. /// public Mod[] CreateDifficultyAdjustmentModCombinations() { @@ -186,14 +190,15 @@ namespace osu.Game.Rulesets.Difficulty } /// - /// Retrieves all s which adjust the difficulty. + /// Retrieves all s which adjust the difficulty. /// protected virtual Mod[] DifficultyAdjustmentMods => Array.Empty(); /// /// Creates to describe beatmap's calculated difficulty. /// - /// The whose difficulty was calculated. + /// The whose difficulty was calculated. + /// This may differ from in the case of timed calculation. /// The s that difficulty was calculated with. /// The skills which processed the beatmap. /// The rate at which the gameplay clock is run at. @@ -210,7 +215,8 @@ namespace osu.Game.Rulesets.Difficulty /// /// Creates the s to calculate the difficulty of an . /// - /// The whose difficulty will be calculated. + /// The whose difficulty will be calculated. + /// This may differ from in the case of timed calculation. /// Mods to calculate difficulty with. /// Clockrate to calculate difficulty with. /// The s. diff --git a/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs index 563032b4ea..ff0c628aa6 100644 --- a/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs @@ -143,9 +143,9 @@ namespace osu.Game.Screens.Play.HUD } public override IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null) - => gameplayBeatmap; + => gameplayBeatmap.PlayableBeatmap; - protected override IBeatmap GetBeatmap() => gameplayBeatmap; + protected override IBeatmap GetBeatmap() => gameplayBeatmap.PlayableBeatmap; protected override Texture GetBackground() => throw new NotImplementedException(); From 98badd644ff6b46d26500f48b125c65b2100410a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 1 Oct 2021 20:09:39 +0900 Subject: [PATCH 2183/2442] Add xmldocs --- osu.Game/Graphics/UserInterface/RollingCounter.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Graphics/UserInterface/RollingCounter.cs b/osu.Game/Graphics/UserInterface/RollingCounter.cs index f03287e7de..67b0b6a06b 100644 --- a/osu.Game/Graphics/UserInterface/RollingCounter.cs +++ b/osu.Game/Graphics/UserInterface/RollingCounter.cs @@ -160,8 +160,15 @@ namespace osu.Game.Graphics.UserInterface this.TransformTo(nameof(DisplayedCount), newValue, rollingTotalDuration, RollingEasing); } + /// + /// Creates the text. Delegates to by default. + /// protected virtual IHasText CreateText() => CreateSpriteText(); + /// + /// Creates an which may be used to display this counter's text. + /// May not be called if is overridden. + /// protected virtual OsuSpriteText CreateSpriteText() => new OsuSpriteText { Font = OsuFont.Numeric.With(size: 40f), From d0081908c5af3f22aa3ef532b982e94390704910 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 1 Oct 2021 20:52:48 +0900 Subject: [PATCH 2184/2442] Make Score internal --- osu.Game/Screens/Play/Player.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 4018650093..6b1186c5ce 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -137,7 +137,7 @@ namespace osu.Game.Screens.Play public readonly PlayerConfiguration Configuration; - public Score Score { get; private set; } + internal Score Score { get; private set; } /// /// Create a new player instance. From a1f880a36aede7bb087a9c25c0e618b17813e39a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 1 Oct 2021 20:56:03 +0900 Subject: [PATCH 2185/2442] Split classes --- .../Difficulty/DifficultyCalculator.cs | 17 +++---------- .../Difficulty/TimedDifficultyAttributes.cs | 25 +++++++++++++++++++ .../HUD/DefaultPerformancePointsCounter.cs | 4 +-- 3 files changed, 30 insertions(+), 16 deletions(-) create mode 100644 osu.Game/Rulesets/Difficulty/TimedDifficultyAttributes.cs diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 780c2ad491..3d90cc59f4 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -222,20 +222,9 @@ namespace osu.Game.Rulesets.Difficulty /// The s. protected abstract Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate); - public class TimedDifficultyAttributes : IComparable - { - public readonly double Time; - public readonly DifficultyAttributes Attributes; - - public TimedDifficultyAttributes(double time, DifficultyAttributes attributes) - { - Time = time; - Attributes = attributes; - } - - public int CompareTo(TimedDifficultyAttributes other) => Time.CompareTo(other.Time); - } - + /// + /// Used to calculate timed difficulty attributes, where only a subset of hitobjects should be visible at any point in time. + /// private class ProgressiveCalculationBeatmap : IBeatmap { private readonly IBeatmap baseBeatmap; diff --git a/osu.Game/Rulesets/Difficulty/TimedDifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/TimedDifficultyAttributes.cs new file mode 100644 index 0000000000..973b2dacb2 --- /dev/null +++ b/osu.Game/Rulesets/Difficulty/TimedDifficultyAttributes.cs @@ -0,0 +1,25 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; + +namespace osu.Game.Rulesets.Difficulty +{ + /// + /// Wraps a object and adds a time value for which the attribute is valid. + /// Output by . + /// + public class TimedDifficultyAttributes : IComparable + { + public readonly double Time; + public readonly DifficultyAttributes Attributes; + + public TimedDifficultyAttributes(double time, DifficultyAttributes attributes) + { + Time = time; + Attributes = attributes; + } + + public int CompareTo(TimedDifficultyAttributes other) => Time.CompareTo(other.Time); + } +} diff --git a/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs index ff0c628aa6..2bc9f78548 100644 --- a/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs @@ -40,7 +40,7 @@ namespace osu.Game.Screens.Play.HUD [Resolved(CanBeNull = true)] private Player player { get; set; } - private DifficultyCalculator.TimedDifficultyAttributes[] timedAttributes; + private TimedDifficultyAttributes[] timedAttributes; private Ruleset gameplayRuleset; public DefaultPerformancePointsCounter() @@ -73,7 +73,7 @@ namespace osu.Game.Screens.Play.HUD if (player == null) return; - var attribIndex = Array.BinarySearch(timedAttributes, 0, timedAttributes.Length, new DifficultyCalculator.TimedDifficultyAttributes(judgement.HitObject.GetEndTime(), null)); + var attribIndex = Array.BinarySearch(timedAttributes, 0, timedAttributes.Length, new TimedDifficultyAttributes(judgement.HitObject.GetEndTime(), null)); if (attribIndex < 0) attribIndex = ~attribIndex - 1; attribIndex = Math.Clamp(attribIndex, 0, timedAttributes.Length - 1); From 0ee148b53f4bf72441f879950be6b76d610d9c8a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 1 Oct 2021 21:31:26 +0900 Subject: [PATCH 2186/2442] Extra guard against no attributes --- osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs index 2bc9f78548..d93d626c72 100644 --- a/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs @@ -70,7 +70,7 @@ namespace osu.Game.Screens.Play.HUD private void onNewJudgement(JudgementResult judgement) { - if (player == null) + if (player == null || timedAttributes.Length == 0) return; var attribIndex = Array.BinarySearch(timedAttributes, 0, timedAttributes.Length, new TimedDifficultyAttributes(judgement.HitObject.GetEndTime(), null)); From adff418fd26c7abdff11663eb49389ec9b400268 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 1 Oct 2021 22:15:10 +0900 Subject: [PATCH 2187/2442] Guard against exception in skin deserialisation --- osu.Game/Skinning/Skin.cs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index b6cb8fc7a4..92441f40da 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -11,6 +11,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; +using osu.Framework.Logging; using osu.Game.Audio; using osu.Game.IO; using osu.Game.Screens.Play.HUD; @@ -55,13 +56,20 @@ namespace osu.Game.Skinning if (bytes == null) continue; - string jsonContent = Encoding.UTF8.GetString(bytes); - var deserializedContent = JsonConvert.DeserializeObject>(jsonContent); + try + { + string jsonContent = Encoding.UTF8.GetString(bytes); + var deserializedContent = JsonConvert.DeserializeObject>(jsonContent); - if (deserializedContent == null) - continue; + if (deserializedContent == null) + continue; - DrawableComponentInfo[skinnableTarget] = deserializedContent.ToArray(); + DrawableComponentInfo[skinnableTarget] = deserializedContent.ToArray(); + } + catch (Exception ex) + { + Logger.Error(ex, "Failed to load skin configuration."); + } } } From a32f5d44e279f617ad2933b28f26c0d0250882d6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 22:23:51 +0900 Subject: [PATCH 2188/2442] Improve clarity of xmldoc Co-authored-by: Dan Balasescu --- osu.Game/Database/IRealmFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/IRealmFactory.cs b/osu.Game/Database/IRealmFactory.cs index 3b206d80eb..a957424584 100644 --- a/osu.Game/Database/IRealmFactory.cs +++ b/osu.Game/Database/IRealmFactory.cs @@ -13,7 +13,7 @@ namespace osu.Game.Database Realm Context { get; } /// - /// Create a new realm context for use on an arbitrary thread. + /// Create a new realm context for use on the current thread. /// Realm CreateContext(); } From 4e3d9da22d4422f2eb3b3e1f0c513ac95af45d4a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 1 Oct 2021 22:58:40 +0900 Subject: [PATCH 2189/2442] Increase test lenience --- .../Gameplay/TestSceneBeatmapSkinFallbacks.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs index e560c81fb2..7d4673c901 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs @@ -84,18 +84,18 @@ namespace osu.Game.Tests.Visual.Gameplay Remove(expectedComponentsAdjustmentContainer); return almostEqual(actualInfo, expectedInfo); - - static bool almostEqual(SkinnableInfo info, SkinnableInfo other) => - other != null - && info.Type == other.Type - && info.Anchor == other.Anchor - && info.Origin == other.Origin - && Precision.AlmostEquals(info.Position, other.Position) - && Precision.AlmostEquals(info.Scale, other.Scale) - && Precision.AlmostEquals(info.Rotation, other.Rotation) - && info.Children.SequenceEqual(other.Children, new FuncEqualityComparer(almostEqual)); } + private static bool almostEqual(SkinnableInfo info, SkinnableInfo other) => + other != null + && info.Type == other.Type + && info.Anchor == other.Anchor + && info.Origin == other.Origin + && Precision.AlmostEquals(info.Position, other.Position, 1) + && Precision.AlmostEquals(info.Scale, other.Scale) + && Precision.AlmostEquals(info.Rotation, other.Rotation) + && info.Children.SequenceEqual(other.Children, new FuncEqualityComparer(almostEqual)); + protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) => new CustomSkinWorkingBeatmap(beatmap, storyboard, Clock, Audio, currentBeatmapSkin); From 7884dbbd11225241c59b6a5d3864bc2dbd429460 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Fri, 1 Oct 2021 15:04:44 +0100 Subject: [PATCH 2190/2442] adjust formulae --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 621fb4588e..31d2c01d2e 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -110,7 +110,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty // We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR. if (mods.Any(m => m is OsuModBlinds)) - aimValue *= 1.0 + blindsMultiplier; + aimValue *= 1.0 + (0.14 + totalHits * (0.0016 / (1 + 2 * countMiss)) + * Math.Pow(accuracy, 16)) + * (1 - 0.003 * Attributes.DrainRate * Attributes.DrainRate); else if (mods.Any(h => h is OsuModHidden)) aimValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); @@ -234,6 +236,5 @@ namespace osu.Game.Rulesets.Osu.Difficulty private int totalHits => countGreat + countOk + countMeh + countMiss; private int totalSuccessfulHits => countGreat + countOk + countMeh; - private double blindsMultiplier => (0.12 + totalHits * (0.0008 / (1 + 2 * countMiss)) * Math.Pow(accuracy, 16)) * (1 - 0.003 * Attributes.DrainRate * Attributes.DrainRate); } } From 57b9a91cba80049937ce6b2391e404144945a39c Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Fri, 1 Oct 2021 15:26:59 +0100 Subject: [PATCH 2191/2442] trim whitespace cuz code quality --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 31d2c01d2e..3144418979 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(m => m is OsuModSpunOut)) multiplier *= 1.0 - Math.Pow((double)Attributes.SpinnerCount / totalHits, 0.85); - + double aimValue = computeAimValue(); double speedValue = computeSpeedValue(); double accuracyValue = computeAccuracyValue(); From 310bf3e580a513dafee83f12e61347a61e8fe541 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Fri, 1 Oct 2021 15:29:20 +0100 Subject: [PATCH 2192/2442] more code quality (oops) --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 3144418979..d0ef0e67da 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(m => m is OsuModSpunOut)) multiplier *= 1.0 - Math.Pow((double)Attributes.SpinnerCount / totalHits, 0.85); - + double aimValue = computeAimValue(); double speedValue = computeSpeedValue(); double accuracyValue = computeAccuracyValue(); From 2a4a376b87ef04d7eb25e689159570e42acbd422 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Sat, 2 Oct 2021 01:21:55 +0900 Subject: [PATCH 2193/2442] Refactor Filter to behave closer to a Transformable --- .../Visual/Audio/TestSceneFilter.cs | 108 ++++++++++-------- osu.Game/Audio/Effects/Filter.cs | 97 ++++++++++------ .../Audio/Effects/ITransformableFilter.cs | 54 +++++++++ osu.Game/Overlays/DialogOverlay.cs | 9 +- 4 files changed, 175 insertions(+), 93 deletions(-) create mode 100644 osu.Game/Audio/Effects/ITransformableFilter.cs diff --git a/osu.Game.Tests/Visual/Audio/TestSceneFilter.cs b/osu.Game.Tests/Visual/Audio/TestSceneFilter.cs index 6a7a404d8a..79d8f7da8c 100644 --- a/osu.Game.Tests/Visual/Audio/TestSceneFilter.cs +++ b/osu.Game.Tests/Visual/Audio/TestSceneFilter.cs @@ -6,74 +6,77 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; using osu.Game.Audio.Effects; using osu.Game.Beatmaps; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Tests.Visual.Audio { public class TestSceneFilter : OsuTestScene { - [Resolved] - private AudioManager audio { get; set; } - private WorkingBeatmap testBeatmap; - private Filter lowPassFilter; - private Filter highPassFilter; - private Filter bandPassFilter; + + private OsuSpriteText lowpassText; + private OsuSpriteText highpassText; + private Filter lowpassFilter; + private Filter highpassFilter; [BackgroundDependencyLoader] - private void load() + private void load(AudioManager audio) { testBeatmap = new WaveformTestBeatmap(audio); - AddRange(new Drawable[] + lowpassFilter = new Filter(audio.TrackMixer); + highpassFilter = new Filter(audio.TrackMixer, BQFType.HighPass); + Add(new FillFlowContainer { - lowPassFilter = new Filter(audio.TrackMixer) + Children = new Drawable[] { - FilterType = BQFType.LowPass, - SweepCutoffStart = 2000, - SweepCutoffEnd = 150, - SweepDuration = 1000 - }, - highPassFilter = new Filter(audio.TrackMixer) - { - FilterType = BQFType.HighPass, - SweepCutoffStart = 150, - SweepCutoffEnd = 2000, - SweepDuration = 1000 - }, - bandPassFilter = new Filter(audio.TrackMixer) - { - FilterType = BQFType.BandPass, - SweepCutoffStart = 150, - SweepCutoffEnd = 20000, - SweepDuration = 1000 - }, + lowpassText = new OsuSpriteText + { + Padding = new MarginPadding(20), + Text = $"Low Pass: {lowpassFilter.Cutoff.Value}hz", + Font = new FontUsage(size: 40) + }, + new OsuSliderBar + { + Width = 500, + Height = 50, + Padding = new MarginPadding(20), + Current = { BindTarget = lowpassFilter.Cutoff } + }, + highpassText = new OsuSpriteText + { + Padding = new MarginPadding(20), + Text = $"High Pass: {highpassFilter.Cutoff.Value}hz", + Font = new FontUsage(size: 40) + }, + new OsuSliderBar + { + Width = 500, + Height = 50, + Padding = new MarginPadding(20), + Current = { BindTarget = highpassFilter.Cutoff } + } + } }); + lowpassFilter.Cutoff.ValueChanged += e => lowpassText.Text = $"Low Pass: {e.NewValue}hz"; + highpassFilter.Cutoff.ValueChanged += e => highpassText.Text = $"High Pass: {e.NewValue}hz"; } [Test] - public void TestLowPass() - { - testFilter(lowPassFilter); - } + public void TestLowPass() => testFilter(lowpassFilter, lowpassFilter.MaxCutoff, 0); [Test] - public void TestHighPass() - { - testFilter(highPassFilter); - } + public void TestHighPass() => testFilter(highpassFilter, 0, highpassFilter.MaxCutoff); - [Test] - public void TestBandPass() - { - testFilter(bandPassFilter); - } - - private void testFilter(Filter filter) + private void testFilter(Filter filter, int cutoffFrom, int cutoffTo) { + Add(filter); AddStep("Prepare Track", () => { - testBeatmap = new WaveformTestBeatmap(audio); testBeatmap.LoadTrack(); }); AddStep("Play Track", () => @@ -81,14 +84,19 @@ namespace osu.Game.Tests.Visual.Audio testBeatmap.Track.Start(); }); AddWaitStep("Let track play", 10); - AddStep("Enable Filter", filter.Enable); - AddWaitStep("Let track play", 10); - AddStep("Disable Filter", filter.Disable); - AddWaitStep("Let track play", 10); - AddStep("Stop Track", () => + AddStep("Filter Sweep", () => { - testBeatmap.Track.Stop(); + filter.CutoffTo(cutoffFrom).Then() + .CutoffTo(cutoffTo, 2000, cutoffFrom > cutoffTo ? Easing.OutCubic : Easing.InCubic); }); + AddWaitStep("Let track play", 10); + AddStep("Filter Sweep (reverse)", () => + { + filter.CutoffTo(cutoffTo).Then() + .CutoffTo(cutoffFrom, 2000, cutoffTo > cutoffFrom ? Easing.OutCubic : Easing.InCubic); + }); + AddWaitStep("Let track play", 10); + AddStep("Stop track", () => testBeatmap.Track.Stop()); } } } diff --git a/osu.Game/Audio/Effects/Filter.cs b/osu.Game/Audio/Effects/Filter.cs index 04ad862879..142e6b8fff 100644 --- a/osu.Game/Audio/Effects/Filter.cs +++ b/osu.Game/Audio/Effects/Filter.cs @@ -8,66 +8,87 @@ using osu.Framework.Graphics; namespace osu.Game.Audio.Effects { - public class Filter : Component + public class Filter : Component, ITransformableFilter { - public BQFType FilterType = BQFType.LowPass; - public float SweepCutoffStart = 2000; - public float SweepCutoffEnd = 150; - public float SweepDuration = 100; - public Easing SweepEasing = Easing.None; - - public bool IsActive { get; private set; } - - private readonly Bindable filterFreq = new Bindable(); + public readonly int MaxCutoff; private readonly AudioMixer mixer; - private BQFParameters filter; + private readonly BQFParameters filter; + private readonly BQFType type; + + public BindableNumber Cutoff { get; } /// /// A BiQuad filter that performs a filter-sweep when toggled on or off. /// /// The mixer this effect should be attached to. - public Filter(AudioMixer mixer) + /// The type of filter (e.g. LowPass, HighPass, etc) + public Filter(AudioMixer mixer, BQFType type = BQFType.LowPass) { this.mixer = mixer; - } + this.type = type; - public void Enable() - { - attachFilter(); - this.TransformBindableTo(filterFreq, SweepCutoffEnd, SweepDuration, SweepEasing); - } + var initialCutoff = 1; - public void Disable() - { - this.TransformBindableTo(filterFreq, SweepCutoffStart, SweepDuration, SweepEasing).OnComplete(_ => detachFilter()); - } + // These max cutoff values are a work-around for BASS' BiQuad filters behaving weirdly when approaching nyquist. + // Note that these values assume a sample rate of 44100 (as per BassAudioMixer in osu.Framework) + // See also https://www.un4seen.com/forum/?topic=19542.0 for more information. + switch (type) + { + case BQFType.HighPass: + MaxCutoff = 21968; // beyond this value, the high-pass cuts out + break; - private void attachFilter() - { - if (IsActive) return; + case BQFType.LowPass: + MaxCutoff = initialCutoff = 14000; // beyond (roughly) this value, the low-pass filter audibly wraps/reflects + break; + case BQFType.BandPass: + MaxCutoff = 16000; // beyond (roughly) this value, the band-pass filter audibly wraps/reflects + break; + + default: + MaxCutoff = 22050; // default to nyquist for other filter types, TODO: handle quirks of other filter types + break; + } + + Cutoff = new BindableNumber + { + MinValue = 1, + MaxValue = MaxCutoff + }; filter = new BQFParameters { - lFilter = FilterType, - fCenter = filterFreq.Value = SweepCutoffStart + lFilter = type, + fCenter = initialCutoff }; - mixer.Effects.Add(filter); - filterFreq.ValueChanged += updateFilter; - IsActive = true; + attachFilter(); + + Cutoff.ValueChanged += updateFilter; + Cutoff.Value = initialCutoff; } - private void detachFilter() - { - if (!IsActive) return; + private void attachFilter() => mixer.Effects.Add(filter); - filterFreq.ValueChanged -= updateFilter; - mixer.Effects.Remove(filter); - IsActive = false; - } + private void detachFilter() => mixer.Effects.Remove(filter); - private void updateFilter(ValueChangedEvent cutoff) + private void updateFilter(ValueChangedEvent cutoff) { + // This is another workaround for quirks in BASS' BiQuad filters. + // Because the cutoff can't be set above ~14khz (i.e. outside of human hearing range) without the aforementioned wrapping/reflecting quirk occuring, we instead + // remove the effect from the mixer when the cutoff is at maximum so that a LowPass filter isn't always attenuating high frequencies just by existing. + if (type == BQFType.LowPass) + { + if (cutoff.NewValue >= MaxCutoff) + { + detachFilter(); + return; + } + + if (cutoff.OldValue >= MaxCutoff && cutoff.NewValue < MaxCutoff) + attachFilter(); + } + var filterIndex = mixer.Effects.IndexOf(filter); if (filterIndex < 0) return; diff --git a/osu.Game/Audio/Effects/ITransformableFilter.cs b/osu.Game/Audio/Effects/ITransformableFilter.cs new file mode 100644 index 0000000000..e4de4cf8ff --- /dev/null +++ b/osu.Game/Audio/Effects/ITransformableFilter.cs @@ -0,0 +1,54 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Transforms; + +namespace osu.Game.Audio.Effects +{ + public interface ITransformableFilter + { + /// + /// The filter cutoff. + /// + BindableNumber Cutoff { get; } + } + + public static class FilterableAudioComponentExtensions + { + /// + /// Smoothly adjusts filter cutoff over time. + /// + /// A to which further transforms can be added. + public static TransformSequence CutoffTo(this T component, int newCutoff, double duration = 0, Easing easing = Easing.None) + where T : class, ITransformableFilter, IDrawable + => component.CutoffTo(newCutoff, duration, new DefaultEasingFunction(easing)); + + /// + /// Smoothly adjusts filter cutoff over time. + /// + /// A to which further transforms can be added. + public static TransformSequence CutoffTo(this TransformSequence sequence, int newCutoff, double duration = 0, Easing easing = Easing.None) + where T : class, ITransformableFilter, IDrawable + => sequence.CutoffTo(newCutoff, duration, new DefaultEasingFunction(easing)); + + /// + /// Smoothly adjusts filter cutoff over time. + /// + /// A to which further transforms can be added. + public static TransformSequence CutoffTo(this T component, int newCutoff, double duration, TEasing easing) + where T : class, ITransformableFilter, IDrawable + where TEasing : IEasingFunction + => component.TransformBindableTo(component.Cutoff, newCutoff, duration, easing); + + /// + /// Smoothly adjusts filter cutoff over time. + /// + /// A to which further transforms can be added. + public static TransformSequence CutoffTo(this TransformSequence sequence, int newCutoff, double duration, TEasing easing) + where T : class, ITransformableFilter, IDrawable + where TEasing : IEasingFunction + => sequence.Append(o => o.TransformBindableTo(o.Cutoff, newCutoff, duration, easing)); + } +} diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index 8a274968b7..6016d16d29 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays protected override string PopInSampleName => "UI/dialog-pop-in"; protected override string PopOutSampleName => "UI/dialog-pop-out"; - private Filter filter; + private Filter lpFilter; public PopupDialog CurrentDialog { get; private set; } @@ -42,7 +42,7 @@ namespace osu.Game.Overlays [BackgroundDependencyLoader] private void load(AudioManager audio) { - AddInternal(filter = new Filter(audio.TrackMixer)); + AddInternal(lpFilter = new Filter(audio.TrackMixer)); } public void Push(PopupDialog dialog) @@ -82,15 +82,13 @@ namespace osu.Game.Overlays { base.PopIn(); this.FadeIn(PopupDialog.ENTER_DURATION, Easing.OutQuint); - filter.Enable(); + lpFilter.CutoffTo(2000).Then().CutoffTo(150, 100, Easing.OutCubic); } protected override void PopOut() { base.PopOut(); - filter.Disable(); - if (CurrentDialog?.State.Value == Visibility.Visible) { CurrentDialog.Hide(); @@ -98,6 +96,7 @@ namespace osu.Game.Overlays } this.FadeOut(PopupDialog.EXIT_DURATION, Easing.InSine); + lpFilter.CutoffTo(2000, 100, Easing.InCubic).Then().CutoffTo(lpFilter.MaxCutoff); } public override bool OnPressed(KeyBindingPressEvent e) From 1eb67dc5941322db6b8b9ab5d8bdf87f10f1b579 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Oct 2021 17:01:28 +0000 Subject: [PATCH 2194/2442] Bump Microsoft.AspNetCore.SignalR.Client from 5.0.9 to 5.0.10 Bumps [Microsoft.AspNetCore.SignalR.Client](https://github.com/dotnet/aspnetcore) from 5.0.9 to 5.0.10. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Commits](https://github.com/dotnet/aspnetcore/compare/v5.0.9...v5.0.10) --- updated-dependencies: - dependency-name: Microsoft.AspNetCore.SignalR.Client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- 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 ba118c5240..9087e77a46 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + From 323a9a748dae5f5c133be297f6f4ae33c5cbe07b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Oct 2021 17:01:32 +0000 Subject: [PATCH 2195/2442] Bump HtmlAgilityPack from 1.11.36 to 1.11.37 Bumps [HtmlAgilityPack](https://github.com/zzzprojects/html-agility-pack) from 1.11.36 to 1.11.37. - [Release notes](https://github.com/zzzprojects/html-agility-pack/releases) - [Commits](https://github.com/zzzprojects/html-agility-pack/compare/v1.11.36...v1.11.37) --- updated-dependencies: - dependency-name: HtmlAgilityPack dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- 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 ba118c5240..850cce2d29 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -20,7 +20,7 @@ - + From 6de4e981ddb4d4fb7ef2cd6546a4b518312498f7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Oct 2021 17:01:37 +0000 Subject: [PATCH 2196/2442] Bump Realm from 10.5.0 to 10.6.0 Bumps [Realm](https://github.com/realm/realm-dotnet) from 10.5.0 to 10.6.0. - [Release notes](https://github.com/realm/realm-dotnet/releases) - [Changelog](https://github.com/realm/realm-dotnet/blob/master/CHANGELOG.md) - [Commits](https://github.com/realm/realm-dotnet/compare/10.5.0...10.6.0) --- updated-dependencies: - dependency-name: Realm dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 8fad10d247..b84f1730ac 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -56,6 +56,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ba118c5240..b1654655a2 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 37931d0c38..8597a06c03 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -99,6 +99,6 @@ - + From 05ca3aec4f7ed7f5ce56ab7a0e414bbcfb4d7afa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 2 Oct 2021 02:08:56 +0900 Subject: [PATCH 2197/2442] Rename `GameplayState` to `SpectatorGameplayState` --- .../Spectate/MultiSpectatorScreen.cs | 4 ++-- osu.Game/Screens/Play/SoloSpectator.cs | 22 +++++++++---------- ...playState.cs => SpectatorGameplayState.cs} | 6 ++--- osu.Game/Screens/Spectate/SpectatorScreen.cs | 8 +++---- 4 files changed, 20 insertions(+), 20 deletions(-) rename osu.Game/Screens/Spectate/{GameplayState.cs => SpectatorGameplayState.cs} (81%) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs index c45e3a79da..7bf8ce0e1a 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs @@ -213,8 +213,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate { } - protected override void StartGameplay(int userId, GameplayState gameplayState) - => instances.Single(i => i.UserId == userId).LoadScore(gameplayState.Score); + protected override void StartGameplay(int userId, SpectatorGameplayState spectatorGameplayState) + => instances.Single(i => i.UserId == userId).LoadScore(spectatorGameplayState.Score); protected override void EndGameplay(int userId) { diff --git a/osu.Game/Screens/Play/SoloSpectator.cs b/osu.Game/Screens/Play/SoloSpectator.cs index 4520e2e825..9d4dad8bdc 100644 --- a/osu.Game/Screens/Play/SoloSpectator.cs +++ b/osu.Game/Screens/Play/SoloSpectator.cs @@ -56,7 +56,7 @@ namespace osu.Game.Screens.Play /// The player's immediate online gameplay state. /// This doesn't always reflect the gameplay state being watched. /// - private GameplayState immediateGameplayState; + private SpectatorGameplayState immediateSpectatorGameplayState; private GetBeatmapSetRequest onlineBeatmapRequest; @@ -146,7 +146,7 @@ namespace osu.Game.Screens.Play Width = 250, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Action = () => scheduleStart(immediateGameplayState), + Action = () => scheduleStart(immediateSpectatorGameplayState), Enabled = { Value = false } } } @@ -167,18 +167,18 @@ namespace osu.Game.Screens.Play showBeatmapPanel(spectatorState); } - protected override void StartGameplay(int userId, GameplayState gameplayState) + protected override void StartGameplay(int userId, SpectatorGameplayState spectatorGameplayState) { - immediateGameplayState = gameplayState; + immediateSpectatorGameplayState = spectatorGameplayState; watchButton.Enabled.Value = true; - scheduleStart(gameplayState); + scheduleStart(spectatorGameplayState); } protected override void EndGameplay(int userId) { scheduledStart?.Cancel(); - immediateGameplayState = null; + immediateSpectatorGameplayState = null; watchButton.Enabled.Value = false; clearDisplay(); @@ -194,7 +194,7 @@ namespace osu.Game.Screens.Play private ScheduledDelegate scheduledStart; - private void scheduleStart(GameplayState gameplayState) + private void scheduleStart(SpectatorGameplayState spectatorGameplayState) { // This function may be called multiple times in quick succession once the screen becomes current again. scheduledStart?.Cancel(); @@ -203,15 +203,15 @@ namespace osu.Game.Screens.Play if (this.IsCurrentScreen()) start(); else - scheduleStart(gameplayState); + scheduleStart(spectatorGameplayState); }); void start() { - Beatmap.Value = gameplayState.Beatmap; - Ruleset.Value = gameplayState.Ruleset.RulesetInfo; + Beatmap.Value = spectatorGameplayState.Beatmap; + Ruleset.Value = spectatorGameplayState.Ruleset.RulesetInfo; - this.Push(new SpectatorPlayerLoader(gameplayState.Score, () => new SoloSpectatorPlayer(gameplayState.Score))); + this.Push(new SpectatorPlayerLoader(spectatorGameplayState.Score, () => new SoloSpectatorPlayer(spectatorGameplayState.Score))); } } diff --git a/osu.Game/Screens/Spectate/GameplayState.cs b/osu.Game/Screens/Spectate/SpectatorGameplayState.cs similarity index 81% rename from osu.Game/Screens/Spectate/GameplayState.cs rename to osu.Game/Screens/Spectate/SpectatorGameplayState.cs index 4579b9c07c..6ca1ac9a0a 100644 --- a/osu.Game/Screens/Spectate/GameplayState.cs +++ b/osu.Game/Screens/Spectate/SpectatorGameplayState.cs @@ -8,9 +8,9 @@ using osu.Game.Scoring; namespace osu.Game.Screens.Spectate { /// - /// The gameplay state of a spectated user. This class is immutable. + /// An immutable spectator gameplay state. /// - public class GameplayState + public class SpectatorGameplayState { /// /// The score which the user is playing. @@ -27,7 +27,7 @@ namespace osu.Game.Screens.Spectate /// public readonly WorkingBeatmap Beatmap; - public GameplayState(Score score, Ruleset ruleset, WorkingBeatmap beatmap) + public SpectatorGameplayState(Score score, Ruleset ruleset, WorkingBeatmap beatmap) { Score = score; Ruleset = ruleset; diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs index f0a68ea078..71bcc336f3 100644 --- a/osu.Game/Screens/Spectate/SpectatorScreen.cs +++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs @@ -43,7 +43,7 @@ namespace osu.Game.Screens.Spectate private readonly IBindableDictionary playingUserStates = new BindableDictionary(); private readonly Dictionary userMap = new Dictionary(); - private readonly Dictionary gameplayStates = new Dictionary(); + private readonly Dictionary gameplayStates = new Dictionary(); private IBindable> managerUpdated; @@ -173,7 +173,7 @@ namespace osu.Game.Screens.Spectate Replay = new Replay { HasReceivedAllFrames = false }, }; - var gameplayState = new GameplayState(score, resolvedRuleset, beatmaps.GetWorkingBeatmap(resolvedBeatmap)); + var gameplayState = new SpectatorGameplayState(score, resolvedRuleset, beatmaps.GetWorkingBeatmap(resolvedBeatmap)); gameplayStates[userId] = gameplayState; Schedule(() => StartGameplay(userId, gameplayState)); @@ -190,8 +190,8 @@ namespace osu.Game.Screens.Spectate /// Starts gameplay for a user. /// /// The user to start gameplay for. - /// The gameplay state. - protected abstract void StartGameplay(int userId, [NotNull] GameplayState gameplayState); + /// The gameplay state. + protected abstract void StartGameplay(int userId, [NotNull] SpectatorGameplayState spectatorGameplayState); /// /// Ends gameplay for a user. From 32afd3f4267df99a69b5f11909d37d5e39a78525 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 2 Oct 2021 02:22:23 +0900 Subject: [PATCH 2198/2442] Replace all basic usages --- .../Mods/TestSceneOsuModHidden.cs | 4 +- .../TestSceneGameplayCursor.cs | 10 ++-- .../Skinning/Legacy/LegacyCursorParticles.cs | 6 +- .../UI/Cursor/OsuCursorContainer.cs | 6 +- .../Skinning/Legacy/LegacyTaikoScroller.cs | 6 +- .../UI/DrawableTaikoMascot.cs | 6 +- .../Gameplay/TestSceneReplayRecorder.cs | 7 ++- .../Gameplay/TestSceneReplayRecording.cs | 7 ++- .../Gameplay/TestSceneSpectatorPlayback.cs | 4 +- osu.Game/Online/Spectator/SpectatorClient.cs | 4 +- osu.Game/Rulesets/UI/ReplayRecorder.cs | 4 +- osu.Game/Screens/Play/GameplayBeatmap.cs | 56 ------------------- osu.Game/Screens/Play/GameplayState.cs | 39 +++++++++++++ osu.Game/Screens/Play/Player.cs | 29 +++++----- osu.Game/Screens/Play/ReplayPlayer.cs | 4 +- osu.Game/Screens/Play/SpectatorPlayer.cs | 4 +- osu.Game/Tests/Visual/TestPlayer.cs | 2 +- 17 files changed, 94 insertions(+), 104 deletions(-) delete mode 100644 osu.Game/Screens/Play/GameplayBeatmap.cs create mode 100644 osu.Game/Screens/Play/GameplayState.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs index 1ac3ad9194..af64be78f8 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs @@ -4,13 +4,11 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Screens.Play; using osuTK; namespace osu.Game.Rulesets.Osu.Tests.Mods @@ -122,7 +120,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods private bool checkSomeHit() => Player.ScoreProcessor.JudgedHits >= 4; private bool objectWithIncreasedVisibilityHasIndex(int index) - => Player.Mods.Value.OfType().Single().FirstObject == Player.ChildrenOfType().Single().HitObjects[index]; + => Player.Mods.Value.OfType().Single().FirstObject == Player.GameplayState.Beatmap.HitObjects[index]; private class TestOsuModHidden : OsuModHidden { diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs index f9dc9abd75..41d9bf7132 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs @@ -17,6 +17,7 @@ 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; @@ -29,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Tests public class TestSceneGameplayCursor : OsuSkinnableTestScene { [Cached] - private GameplayBeatmap gameplayBeatmap; + private GameplayState gameplayState; private OsuCursorContainer lastContainer; @@ -40,7 +41,8 @@ namespace osu.Game.Rulesets.Osu.Tests public TestSceneGameplayCursor() { - gameplayBeatmap = new GameplayBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo)); + var ruleset = new OsuRuleset(); + gameplayState = new GameplayState(CreateBeatmap(ruleset.RulesetInfo), ruleset, Array.Empty()); AddStep("change background colour", () => { @@ -57,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Tests AddSliderStep("circle size", 0f, 10f, 0f, val => { config.SetValue(OsuSetting.AutoCursorSize, true); - gameplayBeatmap.BeatmapInfo.BaseDifficulty.CircleSize = val; + gameplayState.Beatmap.BeatmapInfo.BaseDifficulty.CircleSize = val; Scheduler.AddOnce(() => loadContent(false)); }); @@ -73,7 +75,7 @@ namespace osu.Game.Rulesets.Osu.Tests public void TestSizing(int circleSize, float userScale) { AddStep($"set user scale to {userScale}", () => config.SetValue(OsuSetting.GameplayCursorSize, userScale)); - AddStep($"adjust cs to {circleSize}", () => gameplayBeatmap.BeatmapInfo.BaseDifficulty.CircleSize = circleSize); + AddStep($"adjust cs to {circleSize}", () => gameplayState.Beatmap.BeatmapInfo.BaseDifficulty.CircleSize = circleSize); AddStep("turn on autosizing", () => config.SetValue(OsuSetting.AutoCursorSize, true)); AddStep("load content", () => loadContent()); diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index c2db5f3f82..611ddd08eb 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private OsuPlayfield playfield { get; set; } [Resolved(canBeNull: true)] - private GameplayBeatmap gameplayBeatmap { get; set; } + private GameplayState gameplayState { get; set; } [BackgroundDependencyLoader] private void load(ISkinSource skin, OsuColour colours) @@ -75,12 +75,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy protected override void Update() { - if (playfield == null || gameplayBeatmap == null) return; + if (playfield == null || gameplayState == null) return; DrawableHitObject kiaiHitObject = null; // Check whether currently in a kiai section first. This is only done as an optimisation to avoid enumerating AliveObjects when not necessary. - if (gameplayBeatmap.ControlPointInfo.EffectPointAt(Time.Current).KiaiMode) + if (gameplayState.Beatmap.ControlPointInfo.EffectPointAt(Time.Current).KiaiMode) kiaiHitObject = playfield.HitObjectContainer.AliveObjects.FirstOrDefault(isTracking); kiaiSpewer.Active.Value = kiaiHitObject != null; diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs index 83bcc88e5f..cfe83d0106 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs @@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor } [Resolved(canBeNull: true)] - private GameplayBeatmap beatmap { get; set; } + private GameplayState state { get; set; } [Resolved] private OsuConfigManager config { get; set; } @@ -96,10 +96,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { float scale = userCursorScale.Value; - if (autoCursorScale.Value && beatmap != null) + if (autoCursorScale.Value && state != null) { // if we have a beatmap available, let's get its circle size to figure out an automatic cursor scale modifier. - scale *= GetScaleForCircleSize(beatmap.BeatmapInfo.BaseDifficulty.CircleSize); + scale *= GetScaleForCircleSize(state.Beatmap.BeatmapInfo.BaseDifficulty.CircleSize); } cursorScale.Value = scale; diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyTaikoScroller.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyTaikoScroller.cs index 6fc59ea0e8..fa49242675 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyTaikoScroller.cs @@ -25,10 +25,10 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy } [BackgroundDependencyLoader(true)] - private void load(GameplayBeatmap gameplayBeatmap) + private void load(GameplayState gameplayState) { - if (gameplayBeatmap != null) - ((IBindable)LastResult).BindTo(gameplayBeatmap.LastJudgementResult); + if (gameplayState != null) + ((IBindable)LastResult).BindTo(gameplayState.LastJudgementResult); } private bool passing; diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs index 6a16f311bf..e1063e1071 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs @@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Taiko.UI } [BackgroundDependencyLoader(true)] - private void load(TextureStore textures, GameplayBeatmap gameplayBeatmap) + private void load(TextureStore textures, GameplayState gameplayState) { InternalChildren = new[] { @@ -49,8 +49,8 @@ namespace osu.Game.Rulesets.Taiko.UI animations[TaikoMascotAnimationState.Fail] = new TaikoMascotAnimation(TaikoMascotAnimationState.Fail), }; - if (gameplayBeatmap != null) - ((IBindable)LastResult).BindTo(gameplayBeatmap.LastJudgementResult); + if (gameplayState != null) + ((IBindable)LastResult).BindTo(gameplayState.LastJudgementResult); } protected override void LoadComplete() diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs index 0a3fedaf8e..d89fd322d1 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs @@ -1,6 +1,7 @@ // 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; @@ -17,6 +18,8 @@ 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; @@ -38,7 +41,7 @@ namespace osu.Game.Tests.Visual.Gameplay private TestReplayRecorder recorder; [Cached] - private GameplayBeatmap gameplayBeatmap = new GameplayBeatmap(new Beatmap()); + private GameplayState gameplayState = new GameplayState(new Beatmap(), new OsuRuleset(), Array.Empty()); [SetUp] public void SetUp() => Schedule(() => @@ -57,7 +60,7 @@ namespace osu.Game.Tests.Visual.Gameplay Recorder = recorder = new TestReplayRecorder(new Score { Replay = replay, - ScoreInfo = { Beatmap = gameplayBeatmap.BeatmapInfo } + ScoreInfo = { Beatmap = gameplayState.Beatmap.BeatmapInfo } }) { ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos), diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs index dfd5e2dc58..07514ad51a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs @@ -1,6 +1,7 @@ // 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 osu.Framework.Allocation; using osu.Framework.Graphics; @@ -13,6 +14,8 @@ 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; @@ -30,7 +33,7 @@ namespace osu.Game.Tests.Visual.Gameplay private readonly TestRulesetInputManager recordingManager; [Cached] - private GameplayBeatmap gameplayBeatmap = new GameplayBeatmap(new Beatmap()); + private GameplayState gameplayState = new GameplayState(new Beatmap(), new OsuRuleset(), Array.Empty()); public TestSceneReplayRecording() { @@ -48,7 +51,7 @@ namespace osu.Game.Tests.Visual.Gameplay Recorder = new TestReplayRecorder(new Score { Replay = replay, - ScoreInfo = { Beatmap = gameplayBeatmap.BeatmapInfo } + ScoreInfo = { Beatmap = gameplayState.Beatmap.BeatmapInfo } }) { ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs index 6f5f774758..07ff35f77b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs @@ -25,6 +25,8 @@ 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; @@ -62,7 +64,7 @@ namespace osu.Game.Tests.Visual.Gameplay private SpectatorClient spectatorClient { get; set; } [Cached] - private GameplayBeatmap gameplayBeatmap = new GameplayBeatmap(new Beatmap()); + private GameplayState gameplayState = new GameplayState(new Beatmap(), new OsuRuleset(), Array.Empty()); [SetUp] public void SetUp() => Schedule(() => diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index 8c617784b9..d55ad45ff5 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -134,7 +134,7 @@ namespace osu.Game.Online.Spectator return Task.CompletedTask; } - public void BeginPlaying(GameplayBeatmap beatmap, Score score) + public void BeginPlaying(GameplayState state, Score score) { Debug.Assert(ThreadSafety.IsUpdateThread); @@ -148,7 +148,7 @@ namespace osu.Game.Online.Spectator currentState.RulesetID = score.ScoreInfo.RulesetID; currentState.Mods = score.ScoreInfo.Mods.Select(m => new APIMod(m)).ToArray(); - currentBeatmap = beatmap.PlayableBeatmap; + currentBeatmap = state.Beatmap; currentScore = score; BeginPlayingInternal(currentState); diff --git a/osu.Game/Rulesets/UI/ReplayRecorder.cs b/osu.Game/Rulesets/UI/ReplayRecorder.cs index b57c224059..976f95cef8 100644 --- a/osu.Game/Rulesets/UI/ReplayRecorder.cs +++ b/osu.Game/Rulesets/UI/ReplayRecorder.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.UI private SpectatorClient spectatorClient { get; set; } [Resolved] - private GameplayBeatmap gameplayBeatmap { get; set; } + private GameplayState gameplayState { get; set; } protected ReplayRecorder(Score target) { @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.UI inputManager = GetContainingInputManager(); - spectatorClient?.BeginPlaying(gameplayBeatmap, target); + spectatorClient?.BeginPlaying(gameplayState, target); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/Play/GameplayBeatmap.cs b/osu.Game/Screens/Play/GameplayBeatmap.cs deleted file mode 100644 index 74fbe540fa..0000000000 --- a/osu.Game/Screens/Play/GameplayBeatmap.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Beatmaps.Timing; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; - -namespace osu.Game.Screens.Play -{ - public class GameplayBeatmap : Component, IBeatmap - { - public readonly IBeatmap PlayableBeatmap; - - public GameplayBeatmap(IBeatmap playableBeatmap) - { - PlayableBeatmap = playableBeatmap; - } - - public BeatmapInfo BeatmapInfo - { - get => PlayableBeatmap.BeatmapInfo; - set => PlayableBeatmap.BeatmapInfo = value; - } - - public BeatmapMetadata Metadata => PlayableBeatmap.Metadata; - - public ControlPointInfo ControlPointInfo - { - get => PlayableBeatmap.ControlPointInfo; - set => PlayableBeatmap.ControlPointInfo = value; - } - - public List Breaks => PlayableBeatmap.Breaks; - - public double TotalBreakTime => PlayableBeatmap.TotalBreakTime; - - public IReadOnlyList HitObjects => PlayableBeatmap.HitObjects; - - public IEnumerable GetStatistics() => PlayableBeatmap.GetStatistics(); - - public double GetMostCommonBeatLength() => PlayableBeatmap.GetMostCommonBeatLength(); - - public IBeatmap Clone() => PlayableBeatmap.Clone(); - - private readonly Bindable lastJudgementResult = new Bindable(); - - public IBindable LastJudgementResult => lastJudgementResult; - - public void ApplyResult(JudgementResult result) => lastJudgementResult.Value = result; - } -} diff --git a/osu.Game/Screens/Play/GameplayState.cs b/osu.Game/Screens/Play/GameplayState.cs new file mode 100644 index 0000000000..4944d5b8e2 --- /dev/null +++ b/osu.Game/Screens/Play/GameplayState.cs @@ -0,0 +1,39 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Framework.Bindables; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mods; + +#nullable enable + +namespace osu.Game.Screens.Play +{ + public class GameplayState + { + /// + /// The final post-convert post-mod-application beatmap. + /// + public readonly IBeatmap Beatmap; + + public readonly Ruleset Ruleset; + + public IReadOnlyList Mods; + + public GameplayState(IBeatmap beatmap, Ruleset ruleset, IReadOnlyList mods) + { + Beatmap = beatmap; + Ruleset = ruleset; + Mods = mods; + } + + private readonly Bindable lastJudgementResult = new Bindable(); + + public IBindable LastJudgementResult => lastJudgementResult; + + public void ApplyResult(JudgementResult result) => lastJudgementResult.Value = result; + } +} diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 9927467bd6..a05a8f5056 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -93,9 +93,9 @@ namespace osu.Game.Screens.Play [Resolved] private SpectatorClient spectatorClient { get; set; } - protected Ruleset GameplayRuleset { get; private set; } + public GameplayState GameplayState { get; private set; } - protected GameplayBeatmap GameplayBeatmap { get; private set; } + private Ruleset ruleset; private Sample sampleRestart; @@ -165,7 +165,7 @@ namespace osu.Game.Screens.Play // ensure the score is in a consistent state with the current player. Score.ScoreInfo.Beatmap = Beatmap.Value.BeatmapInfo; - Score.ScoreInfo.Ruleset = GameplayRuleset.RulesetInfo; + Score.ScoreInfo.Ruleset = ruleset.RulesetInfo; Score.ScoreInfo.Mods = Mods.Value.ToArray(); PrepareReplay(); @@ -206,16 +206,16 @@ namespace osu.Game.Screens.Play if (game is OsuGame osuGame) LocalUserPlaying.BindTo(osuGame.LocalUserPlaying); - DrawableRuleset = GameplayRuleset.CreateDrawableRulesetWith(playableBeatmap, Mods.Value); + DrawableRuleset = ruleset.CreateDrawableRulesetWith(playableBeatmap, Mods.Value); dependencies.CacheAs(DrawableRuleset); - ScoreProcessor = GameplayRuleset.CreateScoreProcessor(); + ScoreProcessor = ruleset.CreateScoreProcessor(); ScoreProcessor.ApplyBeatmap(playableBeatmap); ScoreProcessor.Mods.BindTo(Mods); dependencies.CacheAs(ScoreProcessor); - HealthProcessor = GameplayRuleset.CreateHealthProcessor(playableBeatmap.HitObjects[0].StartTime); + HealthProcessor = ruleset.CreateHealthProcessor(playableBeatmap.HitObjects[0].StartTime); HealthProcessor.ApplyBeatmap(playableBeatmap); dependencies.CacheAs(HealthProcessor); @@ -225,12 +225,11 @@ namespace osu.Game.Screens.Play InternalChild = GameplayClockContainer = CreateGameplayClockContainer(Beatmap.Value, DrawableRuleset.GameplayStartTime); - AddInternal(GameplayBeatmap = new GameplayBeatmap(playableBeatmap)); + dependencies.CacheAs(GameplayState = new GameplayState(playableBeatmap, ruleset, Mods.Value)); + AddInternal(screenSuspension = new ScreenSuspensionHandler(GameplayClockContainer)); - dependencies.CacheAs(GameplayBeatmap); - - var rulesetSkinProvider = new RulesetSkinProvidingContainer(GameplayRuleset, playableBeatmap, Beatmap.Value.Skin); + var rulesetSkinProvider = new RulesetSkinProvidingContainer(ruleset, playableBeatmap, Beatmap.Value.Skin); // load the skinning hierarchy first. // this is intentionally done in two stages to ensure things are in a loaded state before exposing the ruleset to skin sources. @@ -280,7 +279,7 @@ namespace osu.Game.Screens.Play { HealthProcessor.ApplyResult(r); ScoreProcessor.ApplyResult(r); - GameplayBeatmap.ApplyResult(r); + GameplayState.ApplyResult(r); }; DrawableRuleset.RevertResult += r => @@ -478,17 +477,17 @@ namespace osu.Game.Screens.Play throw new InvalidOperationException("Beatmap was not loaded"); var rulesetInfo = Ruleset.Value ?? Beatmap.Value.BeatmapInfo.Ruleset; - GameplayRuleset = rulesetInfo.CreateInstance(); + ruleset = rulesetInfo.CreateInstance(); try { - playable = Beatmap.Value.GetPlayableBeatmap(GameplayRuleset.RulesetInfo, Mods.Value); + playable = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo, Mods.Value); } catch (BeatmapInvalidForRulesetException) { // A playable beatmap may not be creatable with the user's preferred ruleset, so try using the beatmap's default ruleset rulesetInfo = Beatmap.Value.BeatmapInfo.Ruleset; - GameplayRuleset = rulesetInfo.CreateInstance(); + ruleset = rulesetInfo.CreateInstance(); playable = Beatmap.Value.GetPlayableBeatmap(rulesetInfo, Mods.Value); } @@ -1010,7 +1009,7 @@ namespace osu.Game.Screens.Play using (var stream = new MemoryStream()) { - new LegacyScoreEncoder(score, GameplayBeatmap.PlayableBeatmap).Encode(stream); + new LegacyScoreEncoder(score, GameplayState.Beatmap).Encode(stream); replayReader = new LegacyByteArrayReader(stream.ToArray(), "replay.osr"); } diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index 0c6f1ed911..eefea737cf 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -41,7 +41,7 @@ namespace osu.Game.Screens.Play DrawableRuleset?.SetReplayScore(Score); } - protected override Score CreateScore() => createScore(GameplayBeatmap.PlayableBeatmap, Mods.Value); + protected override Score CreateScore() => createScore(GameplayState.Beatmap, Mods.Value); // Don't re-import replay scores as they're already present in the database. protected override Task ImportScore(Score score) => Task.CompletedTask; @@ -78,7 +78,7 @@ namespace osu.Game.Screens.Play void keyboardSeek(int direction) { - double target = Math.Clamp(GameplayClockContainer.CurrentTime + direction * keyboard_seek_amount, 0, GameplayBeatmap.HitObjects.Last().GetEndTime()); + double target = Math.Clamp(GameplayClockContainer.CurrentTime + direction * keyboard_seek_amount, 0, GameplayState.Beatmap.HitObjects.Last().GetEndTime()); Seek(target); } diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index d7e42a9cd1..fbb4fb5699 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -66,8 +66,8 @@ namespace osu.Game.Screens.Play foreach (var frame in bundle.Frames) { - IConvertibleReplayFrame convertibleFrame = GameplayRuleset.CreateConvertibleReplayFrame(); - convertibleFrame.FromLegacy(frame, GameplayBeatmap.PlayableBeatmap); + IConvertibleReplayFrame convertibleFrame = GameplayState.Ruleset.CreateConvertibleReplayFrame(); + convertibleFrame.FromLegacy(frame, GameplayState.Beatmap); var convertedFrame = (ReplayFrame)convertibleFrame; convertedFrame.Time = frame.Time; diff --git a/osu.Game/Tests/Visual/TestPlayer.cs b/osu.Game/Tests/Visual/TestPlayer.cs index 5e5f20b307..d68984b144 100644 --- a/osu.Game/Tests/Visual/TestPlayer.cs +++ b/osu.Game/Tests/Visual/TestPlayer.cs @@ -84,7 +84,7 @@ namespace osu.Game.Tests.Visual if (autoplayMod != null) { - DrawableRuleset?.SetReplayScore(autoplayMod.CreateReplayScore(GameplayBeatmap.PlayableBeatmap, Mods.Value)); + DrawableRuleset?.SetReplayScore(autoplayMod.CreateReplayScore(GameplayState.Beatmap, Mods.Value)); return; } From 7e009f616845718cc124c68b900ad4c57382a7fe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 2 Oct 2021 02:28:35 +0900 Subject: [PATCH 2199/2442] Add full xmldoc --- osu.Game/Screens/Play/GameplayState.cs | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayState.cs b/osu.Game/Screens/Play/GameplayState.cs index 4944d5b8e2..ba08c946d2 100644 --- a/osu.Game/Screens/Play/GameplayState.cs +++ b/osu.Game/Screens/Play/GameplayState.cs @@ -12,6 +12,9 @@ using osu.Game.Rulesets.Mods; namespace osu.Game.Screens.Play { + /// + /// The state of an active gameplay session, generally constructed and exposed by . + /// public class GameplayState { /// @@ -19,10 +22,23 @@ namespace osu.Game.Screens.Play /// public readonly IBeatmap Beatmap; + /// + /// The ruleset used in gameplay. + /// public readonly Ruleset Ruleset; + /// + /// The mods applied to the gameplay. + /// public IReadOnlyList Mods; + /// + /// A bindable tracking the last judgement result applied to any hit object. + /// + public IBindable LastJudgementResult => lastJudgementResult; + + private readonly Bindable lastJudgementResult = new Bindable(); + public GameplayState(IBeatmap beatmap, Ruleset ruleset, IReadOnlyList mods) { Beatmap = beatmap; @@ -30,10 +46,10 @@ namespace osu.Game.Screens.Play Mods = mods; } - private readonly Bindable lastJudgementResult = new Bindable(); - - public IBindable LastJudgementResult => lastJudgementResult; - + /// + /// Applies the score change of a to this . + /// + /// The to apply. public void ApplyResult(JudgementResult result) => lastJudgementResult.Value = result; } } From 6d6fda833705efad08906e015c564f7a6b621f3f Mon Sep 17 00:00:00 2001 From: Susko3 <16479013+Susko3@users.noreply.github.com> Date: Fri, 1 Oct 2021 19:54:10 +0200 Subject: [PATCH 2200/2442] Fix some usages of `SettingsTextBox` using a bindable with `null` as default --- osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs | 6 +++++- osu.Game.Tournament/Components/DateTextBox.cs | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs index e3dae9c27e..d530e1f796 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs @@ -53,7 +53,11 @@ namespace osu.Game.Tests.Visual.Settings }; [SettingSource("Sample string", "Change something for a mod")] - public Bindable StringBindable { get; } = new Bindable(); + public Bindable StringBindable { get; } = new Bindable + { + Default = string.Empty, + Value = "Sample text" + }; } private enum TestEnum diff --git a/osu.Game.Tournament/Components/DateTextBox.cs b/osu.Game.Tournament/Components/DateTextBox.cs index 5782301a65..2237e389d7 100644 --- a/osu.Game.Tournament/Components/DateTextBox.cs +++ b/osu.Game.Tournament/Components/DateTextBox.cs @@ -26,7 +26,7 @@ namespace osu.Game.Tournament.Components public DateTextBox() { - base.Current = new Bindable(); + base.Current = new Bindable(string.Empty); ((OsuTextBox)Control).OnCommit += (sender, newText) => { From dcd7d7a709beb3b5b837b4c88c9861c764613072 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 2 Oct 2021 03:05:04 +0900 Subject: [PATCH 2201/2442] Add `JsonIgnore` rule for `StoryboardFile` Not sure why this is required, doesn't make much sense. --- osu.Game/Beatmaps/BeatmapSetInfo.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 4804c7032c..9f5a07ec43 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using JetBrains.Annotations; +using Newtonsoft.Json; using osu.Framework.Testing; using osu.Game.Database; @@ -61,6 +62,7 @@ namespace osu.Game.Beatmaps public string Hash { get; set; } + [JsonIgnore] public string StoryboardFile => ((IBeatmapSetInfo)this).StoryboardFile; /// From 5ea51f4a9ff1d001882f41409f2adef11df4e396 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Oct 2021 18:15:07 +0000 Subject: [PATCH 2202/2442] Bump Sentry from 3.9.0 to 3.9.4 Bumps [Sentry](https://github.com/getsentry/sentry-dotnet) from 3.9.0 to 3.9.4. - [Release notes](https://github.com/getsentry/sentry-dotnet/releases) - [Changelog](https://github.com/getsentry/sentry-dotnet/blob/main/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-dotnet/compare/3.9.0...3.9.4) --- updated-dependencies: - dependency-name: Sentry dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- 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 3e0809c359..ff89fadcc3 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -38,7 +38,7 @@ - + From a5025dc8b89ed0fdea683c2f8e0fa682577063c9 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Fri, 1 Oct 2021 22:23:16 +0100 Subject: [PATCH 2203/2442] Buff base multiplier --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index d0ef0e67da..f90b5a679d 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -110,9 +110,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty // We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR. if (mods.Any(m => m is OsuModBlinds)) - aimValue *= 1.0 + (0.14 + totalHits * (0.0016 / (1 + 2 * countMiss)) - * Math.Pow(accuracy, 16)) - * (1 - 0.003 * Attributes.DrainRate * Attributes.DrainRate); + aimValue *= 1.3 + (totalHits * (0.0016 / (1 + 2 * countMiss)) * Math.Pow(accuracy, 16)) * (1 - 0.003 * Attributes.DrainRate * Attributes.DrainRate); else if (mods.Any(h => h is OsuModHidden)) aimValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); From 9517d69f21fdd51ce7539eed07b42f7ff6a9f4ca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 2 Oct 2021 00:59:31 +0000 Subject: [PATCH 2204/2442] Bump MessagePack from 2.3.75 to 2.3.85 Bumps [MessagePack](https://github.com/neuecc/MessagePack-CSharp) from 2.3.75 to 2.3.85. - [Release notes](https://github.com/neuecc/MessagePack-CSharp/releases) - [Changelog](https://github.com/neuecc/MessagePack-CSharp/blob/master/prepare_release.ps1) - [Commits](https://github.com/neuecc/MessagePack-CSharp/compare/v2.3.75...v2.3.85) --- updated-dependencies: - dependency-name: MessagePack dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- 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 ff89fadcc3..c110aadac1 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -22,7 +22,7 @@ - + From f60f712bcc232177f909d98e4ab686478c6c364b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 2 Oct 2021 00:59:32 +0000 Subject: [PATCH 2205/2442] Bump Microsoft.AspNetCore.SignalR.Protocols.MessagePack Bumps [Microsoft.AspNetCore.SignalR.Protocols.MessagePack](https://github.com/dotnet/aspnetcore) from 5.0.9 to 5.0.10. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Commits](https://github.com/dotnet/aspnetcore/compare/v5.0.9...v5.0.10) --- updated-dependencies: - dependency-name: Microsoft.AspNetCore.SignalR.Protocols.MessagePack dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- 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 ff89fadcc3..868074a32f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + From cb8165ca504f426c9b0b2013d6c58a28d73bfcb5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 2 Oct 2021 00:59:32 +0000 Subject: [PATCH 2206/2442] Bump Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson Bumps [Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson](https://github.com/dotnet/aspnetcore) from 5.0.9 to 5.0.10. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Commits](https://github.com/dotnet/aspnetcore/compare/v5.0.9...v5.0.10) --- updated-dependencies: - dependency-name: Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- 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 ff89fadcc3..73b95b60d5 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -25,7 +25,7 @@ - + From 973c31132be80ccfc14cac0621fc871db987f406 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 2 Oct 2021 12:44:22 +0900 Subject: [PATCH 2207/2442] Rename `BeatmapInfo` variables which were named `beatmap` for clarity --- .../Background/TestSceneUserDimBackgrounds.cs | 2 +- .../SongSelect/TestSceneAdvancedStats.cs | 24 ++++---- .../SongSelect/TestSceneBeatmapCarousel.cs | 22 +++---- .../SongSelect/TestScenePlaySongSelect.cs | 60 +++++++++---------- osu.Game.Tournament/Components/SongBar.cs | 32 +++++----- .../Screens/BeatmapInfoScreen.cs | 2 +- osu.Game/Overlays/BeatmapSet/BasicStats.cs | 20 +++---- .../BeatmapSet/BeatmapSetHeaderContent.cs | 2 +- osu.Game/Overlays/BeatmapSet/Details.cs | 10 ++-- osu.Game/Screens/Select/BeatmapCarousel.cs | 14 ++--- osu.Game/Screens/Select/BeatmapDetails.cs | 2 +- .../Select/Carousel/CarouselBeatmap.cs | 54 ++++++++--------- .../Select/Carousel/CarouselBeatmapSet.cs | 6 +- .../Carousel/DrawableCarouselBeatmap.cs | 2 +- .../Carousel/FilterableDifficultyIcon.cs | 2 +- .../FilterableGroupedDifficultyIcon.cs | 2 +- .../Select/Carousel/SetPanelContent.cs | 2 +- .../Screens/Select/Details/AdvancedStats.cs | 20 +++---- osu.Game/Screens/Select/SongSelect.cs | 2 +- 19 files changed, 140 insertions(+), 140 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index 1670d86545..12a85c3f26 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -286,7 +286,7 @@ namespace osu.Game.Tests.Visual.Background private void setupUserSettings() { AddUntilStep("Song select is current", () => songSelect.IsCurrentScreen()); - AddUntilStep("Song select has selection", () => songSelect.Carousel?.SelectedBeatmap != null); + AddUntilStep("Song select has selection", () => songSelect.Carousel?.SelectedBeatmapInfo != null); AddStep("Set default user settings", () => { SelectedMods.Value = SelectedMods.Value.Concat(new[] { new OsuModNoFail() }).ToArray(); diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs index dcc2111ad3..4538e36c5e 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs @@ -51,7 +51,7 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestNoMod() { - AddStep("set beatmap", () => advancedStats.Beatmap = exampleBeatmapInfo); + AddStep("set beatmap", () => advancedStats.BeatmapInfo = exampleBeatmapInfo); AddStep("no mods selected", () => SelectedMods.Value = Array.Empty()); @@ -65,7 +65,7 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestManiaFirstBarText() { - AddStep("set beatmap", () => advancedStats.Beatmap = new BeatmapInfo + AddStep("set beatmap", () => advancedStats.BeatmapInfo = new BeatmapInfo { Ruleset = rulesets.GetRuleset(3), BaseDifficulty = new BeatmapDifficulty @@ -84,11 +84,11 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestEasyMod() { - AddStep("set beatmap", () => advancedStats.Beatmap = exampleBeatmapInfo); + AddStep("set beatmap", () => advancedStats.BeatmapInfo = exampleBeatmapInfo); AddStep("select EZ mod", () => { - var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance(); + var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance(); SelectedMods.Value = new[] { ruleset.CreateMod() }; }); @@ -101,11 +101,11 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestHardRockMod() { - AddStep("set beatmap", () => advancedStats.Beatmap = exampleBeatmapInfo); + AddStep("set beatmap", () => advancedStats.BeatmapInfo = exampleBeatmapInfo); AddStep("select HR mod", () => { - var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance(); + var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance(); SelectedMods.Value = new[] { ruleset.CreateMod() }; }); @@ -118,13 +118,13 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestUnchangedDifficultyAdjustMod() { - AddStep("set beatmap", () => advancedStats.Beatmap = exampleBeatmapInfo); + AddStep("set beatmap", () => advancedStats.BeatmapInfo = exampleBeatmapInfo); AddStep("select unchanged Difficulty Adjust mod", () => { - var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance(); + var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance(); var difficultyAdjustMod = ruleset.CreateMod(); - difficultyAdjustMod.ReadFromDifficulty(advancedStats.Beatmap.BaseDifficulty); + difficultyAdjustMod.ReadFromDifficulty(advancedStats.BeatmapInfo.BaseDifficulty); SelectedMods.Value = new[] { difficultyAdjustMod }; }); @@ -137,13 +137,13 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestChangedDifficultyAdjustMod() { - AddStep("set beatmap", () => advancedStats.Beatmap = exampleBeatmapInfo); + AddStep("set beatmap", () => advancedStats.BeatmapInfo = exampleBeatmapInfo); AddStep("select changed Difficulty Adjust mod", () => { - var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance(); + var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance(); var difficultyAdjustMod = ruleset.CreateMod(); - var originalDifficulty = advancedStats.Beatmap.BaseDifficulty; + var originalDifficulty = advancedStats.BeatmapInfo.BaseDifficulty; difficultyAdjustMod.ReadFromDifficulty(originalDifficulty); difficultyAdjustMod.DrainRate.Value = originalDifficulty.DrainRate - 0.5f; diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index 78ddfa9ed2..66f15670f5 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.SongSelect private readonly Stack selectedSets = new Stack(); private readonly HashSet eagerSelectedIDs = new HashSet(); - private BeatmapInfo currentSelection => carousel.SelectedBeatmap; + private BeatmapInfo currentSelection => carousel.SelectedBeatmapInfo; private const int set_count = 5; @@ -75,11 +75,11 @@ namespace osu.Game.Tests.Visual.SongSelect { for (int i = 0; i < 3; i++) { - AddStep("store selection", () => selection = carousel.SelectedBeatmap); + AddStep("store selection", () => selection = carousel.SelectedBeatmapInfo); if (isIterating) - AddUntilStep("selection changed", () => carousel.SelectedBeatmap != selection); + AddUntilStep("selection changed", () => carousel.SelectedBeatmapInfo != selection); else - AddUntilStep("selection not changed", () => carousel.SelectedBeatmap == selection); + AddUntilStep("selection not changed", () => carousel.SelectedBeatmapInfo == selection); } } } @@ -387,7 +387,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("Set non-empty mode filter", () => carousel.Filter(new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(1) }, false)); - AddAssert("Something is selected", () => carousel.SelectedBeatmap != null); + AddAssert("Something is selected", () => carousel.SelectedBeatmapInfo != null); } /// @@ -562,7 +562,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("filter to ruleset 0", () => carousel.Filter(new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(0) }, false)); AddStep("select filtered map skipping filtered", () => carousel.SelectBeatmap(testMixed.Beatmaps[1], false)); - AddAssert("unfiltered beatmap not selected", () => carousel.SelectedBeatmap.RulesetID == 0); + AddAssert("unfiltered beatmap not selected", () => carousel.SelectedBeatmapInfo.RulesetID == 0); AddStep("remove mixed set", () => { @@ -653,7 +653,7 @@ namespace osu.Game.Tests.Visual.SongSelect carousel.Filter(new FilterCriteria { SearchText = Guid.NewGuid().ToString() }, false); }); - AddAssert("selection lost", () => carousel.SelectedBeatmap == null); + AddAssert("selection lost", () => carousel.SelectedBeatmapInfo == null); AddStep("Restore different ruleset filter", () => { @@ -661,7 +661,7 @@ namespace osu.Game.Tests.Visual.SongSelect eagerSelectedIDs.Add(carousel.SelectedBeatmapSet.ID); }); - AddAssert("selection changed", () => carousel.SelectedBeatmap != manySets.First().Beatmaps.First()); + AddAssert("selection changed", () => carousel.SelectedBeatmapInfo != manySets.First().Beatmaps.First()); } AddAssert("Selection was random", () => eagerSelectedIDs.Count > 2); @@ -763,9 +763,9 @@ namespace osu.Game.Tests.Visual.SongSelect AddUntilStep($"selected is set{set}{(diff.HasValue ? $" diff{diff.Value}" : "")}", () => { if (diff != null) - return carousel.SelectedBeatmap == carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Skip(diff.Value - 1).First(); + return carousel.SelectedBeatmapInfo == carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Skip(diff.Value - 1).First(); - return carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Contains(carousel.SelectedBeatmap); + return carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Contains(carousel.SelectedBeatmapInfo); }); private void setSelected(int set, int diff) => @@ -800,7 +800,7 @@ namespace osu.Game.Tests.Visual.SongSelect { carousel.RandomAlgorithm.Value = RandomSelectAlgorithm.RandomPermutation; - if (!selectedSets.Any() && carousel.SelectedBeatmap != null) + if (!selectedSets.Any() && carousel.SelectedBeatmapInfo != null) selectedSets.Push(carousel.SelectedBeatmapSet); carousel.SelectNextRandom(); diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 102e5ee425..f9e81d3da6 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -145,7 +145,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("select next and enter", () => { InputManager.MoveMouseTo(songSelect.Carousel.ChildrenOfType() - .First(b => ((CarouselBeatmap)b.Item).Beatmap != songSelect.Carousel.SelectedBeatmap)); + .First(b => ((CarouselBeatmap)b.Item).BeatmapInfo != songSelect.Carousel.SelectedBeatmapInfo)); InputManager.Click(MouseButton.Left); @@ -172,7 +172,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("select next and enter", () => { InputManager.MoveMouseTo(songSelect.Carousel.ChildrenOfType() - .First(b => ((CarouselBeatmap)b.Item).Beatmap != songSelect.Carousel.SelectedBeatmap)); + .First(b => ((CarouselBeatmap)b.Item).BeatmapInfo != songSelect.Carousel.SelectedBeatmapInfo)); InputManager.PressButton(MouseButton.Left); @@ -312,7 +312,7 @@ namespace osu.Game.Tests.Visual.SongSelect { createSongSelect(); addRulesetImportStep(2); - AddUntilStep("no selection", () => songSelect.Carousel.SelectedBeatmap == null); + AddUntilStep("no selection", () => songSelect.Carousel.SelectedBeatmapInfo == null); } [Test] @@ -322,13 +322,13 @@ namespace osu.Game.Tests.Visual.SongSelect changeRuleset(2); addRulesetImportStep(2); addRulesetImportStep(1); - AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap.RulesetID == 2); + AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo.RulesetID == 2); changeRuleset(1); - AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap.RulesetID == 1); + AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo.RulesetID == 1); changeRuleset(0); - AddUntilStep("no selection", () => songSelect.Carousel.SelectedBeatmap == null); + AddUntilStep("no selection", () => songSelect.Carousel.SelectedBeatmapInfo == null); } [Test] @@ -338,7 +338,7 @@ namespace osu.Game.Tests.Visual.SongSelect changeRuleset(2); addRulesetImportStep(2); - AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap.RulesetID == 2); + AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo.RulesetID == 2); addRulesetImportStep(0); addRulesetImportStep(0); @@ -355,7 +355,7 @@ namespace osu.Game.Tests.Visual.SongSelect Beatmap.Value = manager.GetWorkingBeatmap(target); }); - AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap.Equals(target)); + AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo.Equals(target)); // this is an important check, to make sure updateComponentFromBeatmap() was actually run AddUntilStep("selection shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap.BeatmapInfo.Equals(target)); @@ -368,7 +368,7 @@ namespace osu.Game.Tests.Visual.SongSelect changeRuleset(2); addRulesetImportStep(2); - AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap.RulesetID == 2); + AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo.RulesetID == 2); addRulesetImportStep(0); addRulesetImportStep(0); @@ -385,7 +385,7 @@ namespace osu.Game.Tests.Visual.SongSelect Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == 0); }); - AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap.Equals(target)); + AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo.Equals(target)); AddUntilStep("has correct ruleset", () => Ruleset.Value.ID == 0); @@ -444,7 +444,7 @@ namespace osu.Game.Tests.Visual.SongSelect { createSongSelect(); addManyTestMaps(); - AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap != null); + AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo != null); bool startRequested = false; @@ -473,13 +473,13 @@ namespace osu.Game.Tests.Visual.SongSelect // used for filter check below AddStep("allow convert display", () => config.SetValue(OsuSetting.ShowConvertedBeatmaps, true)); - AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap != null); + AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo != null); AddStep("set filter text", () => songSelect.FilterControl.ChildrenOfType().First().Text = "nonono"); AddUntilStep("dummy selected", () => Beatmap.Value is DummyWorkingBeatmap); - AddUntilStep("has no selection", () => songSelect.Carousel.SelectedBeatmap == null); + AddUntilStep("has no selection", () => songSelect.Carousel.SelectedBeatmapInfo == null); BeatmapInfo target = null; @@ -494,7 +494,7 @@ namespace osu.Game.Tests.Visual.SongSelect Beatmap.Value = manager.GetWorkingBeatmap(target); }); - AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap != null); + AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo != null); AddAssert("selected only shows expected ruleset (plus converts)", () => { @@ -502,16 +502,16 @@ namespace osu.Game.Tests.Visual.SongSelect // special case for converts checked here. return selectedPanel.ChildrenOfType().All(i => - i.IsFiltered || i.Item.Beatmap.Ruleset.ID == targetRuleset || i.Item.Beatmap.Ruleset.ID == 0); + i.IsFiltered || i.Item.BeatmapInfo.Ruleset.ID == targetRuleset || i.Item.BeatmapInfo.Ruleset.ID == 0); }); - AddUntilStep("carousel has correct", () => songSelect.Carousel.SelectedBeatmap?.OnlineBeatmapID == target.OnlineBeatmapID); + AddUntilStep("carousel has correct", () => songSelect.Carousel.SelectedBeatmapInfo?.OnlineBeatmapID == target.OnlineBeatmapID); AddUntilStep("game has correct", () => Beatmap.Value.BeatmapInfo.OnlineBeatmapID == target.OnlineBeatmapID); AddStep("reset filter text", () => songSelect.FilterControl.ChildrenOfType().First().Text = string.Empty); AddAssert("game still correct", () => Beatmap.Value?.BeatmapInfo.OnlineBeatmapID == target.OnlineBeatmapID); - AddAssert("carousel still correct", () => songSelect.Carousel.SelectedBeatmap.OnlineBeatmapID == target.OnlineBeatmapID); + AddAssert("carousel still correct", () => songSelect.Carousel.SelectedBeatmapInfo.OnlineBeatmapID == target.OnlineBeatmapID); } [Test] @@ -522,13 +522,13 @@ namespace osu.Game.Tests.Visual.SongSelect changeRuleset(0); - AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap != null); + AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo != null); AddStep("set filter text", () => songSelect.FilterControl.ChildrenOfType().First().Text = "nonono"); AddUntilStep("dummy selected", () => Beatmap.Value is DummyWorkingBeatmap); - AddUntilStep("has no selection", () => songSelect.Carousel.SelectedBeatmap == null); + AddUntilStep("has no selection", () => songSelect.Carousel.SelectedBeatmapInfo == null); BeatmapInfo target = null; @@ -540,15 +540,15 @@ namespace osu.Game.Tests.Visual.SongSelect Beatmap.Value = manager.GetWorkingBeatmap(target); }); - AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap != null); + AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo != null); - AddUntilStep("carousel has correct", () => songSelect.Carousel.SelectedBeatmap?.OnlineBeatmapID == target.OnlineBeatmapID); + AddUntilStep("carousel has correct", () => songSelect.Carousel.SelectedBeatmapInfo?.OnlineBeatmapID == target.OnlineBeatmapID); AddUntilStep("game has correct", () => Beatmap.Value.BeatmapInfo.OnlineBeatmapID == target.OnlineBeatmapID); AddStep("set filter text", () => songSelect.FilterControl.ChildrenOfType().First().Text = "nononoo"); AddUntilStep("game lost selection", () => Beatmap.Value is DummyWorkingBeatmap); - AddAssert("carousel lost selection", () => songSelect.Carousel.SelectedBeatmap == null); + AddAssert("carousel lost selection", () => songSelect.Carousel.SelectedBeatmapInfo == null); } [Test] @@ -581,9 +581,9 @@ namespace osu.Game.Tests.Visual.SongSelect createSongSelect(); addRulesetImportStep(0); AddStep("Move to last difficulty", () => songSelect.Carousel.SelectBeatmap(songSelect.Carousel.BeatmapSets.First().Beatmaps.Last())); - AddStep("Store current ID", () => previousID = songSelect.Carousel.SelectedBeatmap.ID); + AddStep("Store current ID", () => previousID = songSelect.Carousel.SelectedBeatmapInfo.ID); AddStep("Hide first beatmap", () => manager.Hide(songSelect.Carousel.SelectedBeatmapSet.Beatmaps.First())); - AddAssert("Selected beatmap has not changed", () => songSelect.Carousel.SelectedBeatmap.ID == previousID); + AddAssert("Selected beatmap has not changed", () => songSelect.Carousel.SelectedBeatmapInfo.ID == previousID); } [Test] @@ -641,7 +641,7 @@ namespace osu.Game.Tests.Visual.SongSelect InputManager.Click(MouseButton.Left); }); - AddAssert("Selected beatmap correct", () => songSelect.Carousel.SelectedBeatmap == filteredBeatmap); + AddAssert("Selected beatmap correct", () => songSelect.Carousel.SelectedBeatmapInfo == filteredBeatmap); } [Test] @@ -717,7 +717,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("Find an icon for different ruleset", () => { difficultyIcon = set.ChildrenOfType() - .First(icon => icon.Item.Beatmap.Ruleset.ID == 3); + .First(icon => icon.Item.BeatmapInfo.Ruleset.ID == 3); }); AddAssert("Check ruleset is osu!", () => Ruleset.Value.ID == 0); @@ -735,7 +735,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.ID == 3); - AddAssert("Selected beatmap still same set", () => songSelect.Carousel.SelectedBeatmap.BeatmapSet.ID == previousSetID); + AddAssert("Selected beatmap still same set", () => songSelect.Carousel.SelectedBeatmapInfo.BeatmapSet.ID == previousSetID); AddAssert("Selected beatmap is mania", () => Beatmap.Value.BeatmapInfo.Ruleset.ID == 3); } @@ -767,7 +767,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("Find group icon for different ruleset", () => { groupIcon = set.ChildrenOfType() - .First(icon => icon.Items.First().Beatmap.Ruleset.ID == 3); + .First(icon => icon.Items.First().BeatmapInfo.Ruleset.ID == 3); }); AddAssert("Check ruleset is osu!", () => Ruleset.Value.ID == 0); @@ -781,7 +781,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.ID == 3); - AddAssert("Check first item in group selected", () => Beatmap.Value.BeatmapInfo.Equals(groupIcon.Items.First().Beatmap)); + AddAssert("Check first item in group selected", () => Beatmap.Value.BeatmapInfo.Equals(groupIcon.Items.First().BeatmapInfo)); } [Test] @@ -856,7 +856,7 @@ namespace osu.Game.Tests.Visual.SongSelect private int getBeatmapIndex(BeatmapSetInfo set, BeatmapInfo info) => set.Beatmaps.FindIndex(b => b == info); - private int getCurrentBeatmapIndex() => getBeatmapIndex(songSelect.Carousel.SelectedBeatmapSet, songSelect.Carousel.SelectedBeatmap); + private int getCurrentBeatmapIndex() => getBeatmapIndex(songSelect.Carousel.SelectedBeatmapSet, songSelect.Carousel.SelectedBeatmapInfo); private int getDifficultyIconIndex(DrawableCarouselBeatmapSet set, FilterableDifficultyIcon icon) { diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index 6080f7b636..357c82df61 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -21,22 +21,22 @@ namespace osu.Game.Tournament.Components { public class SongBar : CompositeDrawable { - private BeatmapInfo beatmap; + private BeatmapInfo beatmapInfo; public const float HEIGHT = 145 / 2f; [Resolved] private IBindable ruleset { get; set; } - public BeatmapInfo Beatmap + public BeatmapInfo BeatmapInfo { - get => beatmap; + get => beatmapInfo; set { - if (beatmap == value) + if (beatmapInfo == value) return; - beatmap = value; + beatmapInfo = value; update(); } } @@ -95,18 +95,18 @@ namespace osu.Game.Tournament.Components private void update() { - if (beatmap == null) + if (beatmapInfo == null) { flow.Clear(); return; } - var bpm = beatmap.BeatmapSet.OnlineInfo.BPM; - var length = beatmap.Length; + var bpm = beatmapInfo.BeatmapSet.OnlineInfo.BPM; + var length = beatmapInfo.Length; string hardRockExtra = ""; string srExtra = ""; - var ar = beatmap.BaseDifficulty.ApproachRate; + var ar = beatmapInfo.BaseDifficulty.ApproachRate; if ((mods & LegacyMods.HardRock) > 0) { @@ -132,9 +132,9 @@ namespace osu.Game.Tournament.Components default: stats = new (string heading, string content)[] { - ("CS", $"{beatmap.BaseDifficulty.CircleSize:0.#}{hardRockExtra}"), + ("CS", $"{beatmapInfo.BaseDifficulty.CircleSize:0.#}{hardRockExtra}"), ("AR", $"{ar:0.#}{hardRockExtra}"), - ("OD", $"{beatmap.BaseDifficulty.OverallDifficulty:0.#}{hardRockExtra}"), + ("OD", $"{beatmapInfo.BaseDifficulty.OverallDifficulty:0.#}{hardRockExtra}"), }; break; @@ -142,15 +142,15 @@ namespace osu.Game.Tournament.Components case 3: stats = new (string heading, string content)[] { - ("OD", $"{beatmap.BaseDifficulty.OverallDifficulty:0.#}{hardRockExtra}"), - ("HP", $"{beatmap.BaseDifficulty.DrainRate:0.#}{hardRockExtra}") + ("OD", $"{beatmapInfo.BaseDifficulty.OverallDifficulty:0.#}{hardRockExtra}"), + ("HP", $"{beatmapInfo.BaseDifficulty.DrainRate:0.#}{hardRockExtra}") }; break; case 2: stats = new (string heading, string content)[] { - ("CS", $"{beatmap.BaseDifficulty.CircleSize:0.#}{hardRockExtra}"), + ("CS", $"{beatmapInfo.BaseDifficulty.CircleSize:0.#}{hardRockExtra}"), ("AR", $"{ar:0.#}"), }; break; @@ -186,7 +186,7 @@ namespace osu.Game.Tournament.Components Children = new Drawable[] { new DiffPiece(stats), - new DiffPiece(("Star Rating", $"{beatmap.StarDifficulty:0.#}{srExtra}")) + new DiffPiece(("Star Rating", $"{beatmapInfo.StarDifficulty:0.#}{srExtra}")) } }, new FillFlowContainer @@ -229,7 +229,7 @@ namespace osu.Game.Tournament.Components } } }, - new TournamentBeatmapPanel(beatmap) + new TournamentBeatmapPanel(beatmapInfo) { RelativeSizeAxes = Axes.X, Width = 0.5f, diff --git a/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs index 50498304ca..b94b164116 100644 --- a/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs +++ b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs @@ -40,7 +40,7 @@ namespace osu.Game.Tournament.Screens private void beatmapChanged(ValueChangedEvent beatmap) { SongBar.FadeInFromZero(300, Easing.OutQuint); - SongBar.Beatmap = beatmap.NewValue; + SongBar.BeatmapInfo = beatmap.NewValue; } } } diff --git a/osu.Game/Overlays/BeatmapSet/BasicStats.cs b/osu.Game/Overlays/BeatmapSet/BasicStats.cs index 5a6cde8229..683f4f0c49 100644 --- a/osu.Game/Overlays/BeatmapSet/BasicStats.cs +++ b/osu.Game/Overlays/BeatmapSet/BasicStats.cs @@ -38,16 +38,16 @@ namespace osu.Game.Overlays.BeatmapSet } } - private BeatmapInfo beatmap; + private BeatmapInfo beatmapInfo; - public BeatmapInfo Beatmap + public BeatmapInfo BeatmapInfo { - get => beatmap; + get => beatmapInfo; set { - if (value == beatmap) return; + if (value == beatmapInfo) return; - beatmap = value; + beatmapInfo = value; updateDisplay(); } @@ -57,7 +57,7 @@ namespace osu.Game.Overlays.BeatmapSet { bpm.Value = BeatmapSet?.OnlineInfo?.BPM.ToLocalisableString(@"0.##") ?? (LocalisableString)"-"; - if (beatmap == null) + if (beatmapInfo == null) { length.Value = string.Empty; circleCount.Value = string.Empty; @@ -65,11 +65,11 @@ namespace osu.Game.Overlays.BeatmapSet } else { - length.TooltipText = BeatmapsetsStrings.ShowStatsTotalLength(TimeSpan.FromMilliseconds(beatmap.Length).ToFormattedDuration()); - length.Value = TimeSpan.FromMilliseconds(beatmap.Length).ToFormattedDuration(); + length.TooltipText = BeatmapsetsStrings.ShowStatsTotalLength(TimeSpan.FromMilliseconds(beatmapInfo.Length).ToFormattedDuration()); + length.Value = TimeSpan.FromMilliseconds(beatmapInfo.Length).ToFormattedDuration(); - circleCount.Value = beatmap.OnlineInfo.CircleCount.ToLocalisableString(@"N0"); - sliderCount.Value = beatmap.OnlineInfo.SliderCount.ToLocalisableString(@"N0"); + circleCount.Value = beatmapInfo.OnlineInfo.CircleCount.ToLocalisableString(@"N0"); + sliderCount.Value = beatmapInfo.OnlineInfo.SliderCount.ToLocalisableString(@"N0"); } } diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs index c3b6444a24..dcf06ac7fb 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs @@ -211,7 +211,7 @@ namespace osu.Game.Overlays.BeatmapSet Picker.Beatmap.ValueChanged += b => { - Details.Beatmap = b.NewValue; + Details.BeatmapInfo = b.NewValue; externalLink.Link = $@"{api.WebsiteRootUrl}/beatmapsets/{BeatmapSet.Value?.OnlineBeatmapSetID}#{b.NewValue?.Ruleset.ShortName}/{b.NewValue?.OnlineBeatmapID}"; }; } diff --git a/osu.Game/Overlays/BeatmapSet/Details.cs b/osu.Game/Overlays/BeatmapSet/Details.cs index 680487ffbb..92361ae4f8 100644 --- a/osu.Game/Overlays/BeatmapSet/Details.cs +++ b/osu.Game/Overlays/BeatmapSet/Details.cs @@ -37,16 +37,16 @@ namespace osu.Game.Overlays.BeatmapSet } } - private BeatmapInfo beatmap; + private BeatmapInfo beatmapInfo; - public BeatmapInfo Beatmap + public BeatmapInfo BeatmapInfo { - get => beatmap; + get => beatmapInfo; set { - if (value == beatmap) return; + if (value == beatmapInfo) return; - basic.Beatmap = advanced.Beatmap = beatmap = value; + basic.BeatmapInfo = advanced.BeatmapInfo = beatmapInfo = value; } } diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 5eceae3c6e..f424587e22 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -48,7 +48,7 @@ namespace osu.Game.Screens.Select /// /// The currently selected beatmap. /// - public BeatmapInfo SelectedBeatmap => selectedBeatmap?.Beatmap; + public BeatmapInfo SelectedBeatmapInfo => selectedBeatmap?.BeatmapInfo; private CarouselBeatmap selectedBeatmap => selectedBeatmapSet?.Beatmaps.FirstOrDefault(s => s.State.Value == CarouselItemState.Selected); @@ -65,7 +65,7 @@ namespace osu.Game.Screens.Select private CarouselBeatmapSet selectedBeatmapSet; /// - /// Raised when the is changed. + /// Raised when the is changed. /// public Action SelectionChanged; @@ -212,7 +212,7 @@ namespace osu.Game.Screens.Select // If the selected beatmap is about to be removed, store its ID so it can be re-selected if required if (existingSet?.State?.Value == CarouselItemState.Selected) - previouslySelectedID = selectedBeatmap?.Beatmap.ID; + previouslySelectedID = selectedBeatmap?.BeatmapInfo.ID; var newSet = createCarouselSet(beatmapSet); @@ -233,7 +233,7 @@ namespace osu.Game.Screens.Select // check if we can/need to maintain our current selection. if (previouslySelectedID != null) - select((CarouselItem)newSet.Beatmaps.FirstOrDefault(b => b.Beatmap.ID == previouslySelectedID) ?? newSet); + select((CarouselItem)newSet.Beatmaps.FirstOrDefault(b => b.BeatmapInfo.ID == previouslySelectedID) ?? newSet); itemsCache.Invalidate(); Schedule(() => BeatmapSetsChanged?.Invoke()); @@ -258,7 +258,7 @@ namespace osu.Game.Screens.Select if (!bypassFilters && set.Filtered.Value) continue; - var item = set.Beatmaps.FirstOrDefault(p => p.Beatmap.Equals(beatmap)); + var item = set.Beatmaps.FirstOrDefault(p => p.BeatmapInfo.Equals(beatmap)); if (item == null) // The beatmap that needs to be selected doesn't exist in this set @@ -472,7 +472,7 @@ namespace osu.Game.Screens.Select private float? scrollTarget; /// - /// Scroll to the current . + /// Scroll to the current . /// /// /// Whether the scroll position should immediately be shifted to the target, delegating animation to visible panels. @@ -720,7 +720,7 @@ namespace osu.Game.Screens.Select if (state.NewValue == CarouselItemState.Selected) { selectedBeatmapSet = set; - SelectionChanged?.Invoke(c.Beatmap); + SelectionChanged?.Invoke(c.BeatmapInfo); itemsCache.Invalidate(); ScrollToSelected(); diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 973f54c038..d59d76300a 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -170,7 +170,7 @@ namespace osu.Game.Screens.Select private void updateStatistics() { - advanced.Beatmap = Beatmap; + advanced.BeatmapInfo = Beatmap; description.Text = Beatmap?.Version; source.Text = Beatmap?.Metadata?.Source; tags.Text = Beatmap?.Metadata?.Tags; diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index f95ddfee41..3f729d9477 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -12,11 +12,11 @@ namespace osu.Game.Screens.Select.Carousel { public override float TotalHeight => DrawableCarouselBeatmap.HEIGHT; - public readonly BeatmapInfo Beatmap; + public readonly BeatmapInfo BeatmapInfo; - public CarouselBeatmap(BeatmapInfo beatmap) + public CarouselBeatmap(BeatmapInfo beatmapInfo) { - Beatmap = beatmap; + BeatmapInfo = beatmapInfo; State.Value = CarouselItemState.Collapsed; } @@ -28,36 +28,36 @@ namespace osu.Game.Screens.Select.Carousel bool match = criteria.Ruleset == null || - Beatmap.RulesetID == criteria.Ruleset.ID || - (Beatmap.RulesetID == 0 && criteria.Ruleset.ID > 0 && criteria.AllowConvertedBeatmaps); + BeatmapInfo.RulesetID == criteria.Ruleset.ID || + (BeatmapInfo.RulesetID == 0 && criteria.Ruleset.ID > 0 && criteria.AllowConvertedBeatmaps); - if (Beatmap.BeatmapSet?.Equals(criteria.SelectedBeatmapSet) == true) + if (BeatmapInfo.BeatmapSet?.Equals(criteria.SelectedBeatmapSet) == true) { // only check ruleset equality or convertability for selected beatmap Filtered.Value = !match; return; } - match &= !criteria.StarDifficulty.HasFilter || criteria.StarDifficulty.IsInRange(Beatmap.StarDifficulty); - match &= !criteria.ApproachRate.HasFilter || criteria.ApproachRate.IsInRange(Beatmap.BaseDifficulty.ApproachRate); - match &= !criteria.DrainRate.HasFilter || criteria.DrainRate.IsInRange(Beatmap.BaseDifficulty.DrainRate); - match &= !criteria.CircleSize.HasFilter || criteria.CircleSize.IsInRange(Beatmap.BaseDifficulty.CircleSize); - match &= !criteria.OverallDifficulty.HasFilter || criteria.OverallDifficulty.IsInRange(Beatmap.BaseDifficulty.OverallDifficulty); - match &= !criteria.Length.HasFilter || criteria.Length.IsInRange(Beatmap.Length); - match &= !criteria.BPM.HasFilter || criteria.BPM.IsInRange(Beatmap.BPM); + match &= !criteria.StarDifficulty.HasFilter || criteria.StarDifficulty.IsInRange(BeatmapInfo.StarDifficulty); + match &= !criteria.ApproachRate.HasFilter || criteria.ApproachRate.IsInRange(BeatmapInfo.BaseDifficulty.ApproachRate); + match &= !criteria.DrainRate.HasFilter || criteria.DrainRate.IsInRange(BeatmapInfo.BaseDifficulty.DrainRate); + match &= !criteria.CircleSize.HasFilter || criteria.CircleSize.IsInRange(BeatmapInfo.BaseDifficulty.CircleSize); + match &= !criteria.OverallDifficulty.HasFilter || criteria.OverallDifficulty.IsInRange(BeatmapInfo.BaseDifficulty.OverallDifficulty); + match &= !criteria.Length.HasFilter || criteria.Length.IsInRange(BeatmapInfo.Length); + match &= !criteria.BPM.HasFilter || criteria.BPM.IsInRange(BeatmapInfo.BPM); - match &= !criteria.BeatDivisor.HasFilter || criteria.BeatDivisor.IsInRange(Beatmap.BeatDivisor); - match &= !criteria.OnlineStatus.HasFilter || criteria.OnlineStatus.IsInRange(Beatmap.Status); + match &= !criteria.BeatDivisor.HasFilter || criteria.BeatDivisor.IsInRange(BeatmapInfo.BeatDivisor); + match &= !criteria.OnlineStatus.HasFilter || criteria.OnlineStatus.IsInRange(BeatmapInfo.Status); - match &= !criteria.Creator.HasFilter || criteria.Creator.Matches(Beatmap.Metadata.AuthorString); - match &= !criteria.Artist.HasFilter || criteria.Artist.Matches(Beatmap.Metadata.Artist) || - criteria.Artist.Matches(Beatmap.Metadata.ArtistUnicode); + match &= !criteria.Creator.HasFilter || criteria.Creator.Matches(BeatmapInfo.Metadata.AuthorString); + match &= !criteria.Artist.HasFilter || criteria.Artist.Matches(BeatmapInfo.Metadata.Artist) || + criteria.Artist.Matches(BeatmapInfo.Metadata.ArtistUnicode); - match &= !criteria.UserStarDifficulty.HasFilter || criteria.UserStarDifficulty.IsInRange(Beatmap.StarDifficulty); + match &= !criteria.UserStarDifficulty.HasFilter || criteria.UserStarDifficulty.IsInRange(BeatmapInfo.StarDifficulty); if (match) { - var terms = Beatmap.SearchableTerms; + var terms = BeatmapInfo.SearchableTerms; foreach (var criteriaTerm in criteria.SearchTerms) match &= terms.Any(term => term.Contains(criteriaTerm, StringComparison.InvariantCultureIgnoreCase)); @@ -66,16 +66,16 @@ namespace osu.Game.Screens.Select.Carousel // this should be done after text matching so we can prioritise matching numbers in metadata. if (!match && criteria.SearchNumber.HasValue) { - match = (Beatmap.OnlineBeatmapID == criteria.SearchNumber.Value) || - (Beatmap.BeatmapSet?.OnlineBeatmapSetID == criteria.SearchNumber.Value); + match = (BeatmapInfo.OnlineBeatmapID == criteria.SearchNumber.Value) || + (BeatmapInfo.BeatmapSet?.OnlineBeatmapSetID == criteria.SearchNumber.Value); } } if (match) - match &= criteria.Collection?.Beatmaps.Contains(Beatmap) ?? true; + match &= criteria.Collection?.Beatmaps.Contains(BeatmapInfo) ?? true; if (match && criteria.RulesetCriteria != null) - match &= criteria.RulesetCriteria.Matches(Beatmap); + match &= criteria.RulesetCriteria.Matches(BeatmapInfo); Filtered.Value = !match; } @@ -89,13 +89,13 @@ namespace osu.Game.Screens.Select.Carousel { default: case SortMode.Difficulty: - var ruleset = Beatmap.RulesetID.CompareTo(otherBeatmap.Beatmap.RulesetID); + var ruleset = BeatmapInfo.RulesetID.CompareTo(otherBeatmap.BeatmapInfo.RulesetID); if (ruleset != 0) return ruleset; - return Beatmap.StarDifficulty.CompareTo(otherBeatmap.Beatmap.StarDifficulty); + return BeatmapInfo.StarDifficulty.CompareTo(otherBeatmap.BeatmapInfo.StarDifficulty); } } - public override string ToString() => Beatmap.ToString(); + public override string ToString() => BeatmapInfo.ToString(); } } diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs index 00c2c2cb4a..0d7882bf17 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs @@ -47,8 +47,8 @@ namespace osu.Game.Screens.Select.Carousel { if (LastSelected == null || LastSelected.Filtered.Value) { - if (GetRecommendedBeatmap?.Invoke(Children.OfType().Where(b => !b.Filtered.Value).Select(b => b.Beatmap)) is BeatmapInfo recommended) - return Children.OfType().First(b => b.Beatmap == recommended); + if (GetRecommendedBeatmap?.Invoke(Children.OfType().Where(b => !b.Filtered.Value).Select(b => b.BeatmapInfo)) is BeatmapInfo recommended) + return Children.OfType().First(b => b.BeatmapInfo == recommended); } return base.GetNextToSelect(); @@ -91,7 +91,7 @@ namespace osu.Game.Screens.Select.Carousel /// /// All beatmaps which are not filtered and valid for display. /// - protected IEnumerable ValidBeatmaps => Beatmaps.Where(b => !b.Filtered.Value || b.State.Value == CarouselItemState.Selected).Select(b => b.Beatmap); + protected IEnumerable ValidBeatmaps => Beatmaps.Where(b => !b.Filtered.Value || b.State.Value == CarouselItemState.Selected).Select(b => b.BeatmapInfo); private int compareUsingAggregateMax(CarouselBeatmapSet other, Func func) { diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index 633ef9297e..2fe7ff4562 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -68,7 +68,7 @@ namespace osu.Game.Screens.Select.Carousel public DrawableCarouselBeatmap(CarouselBeatmap panel) { - beatmap = panel.Beatmap; + beatmap = panel.BeatmapInfo; Item = panel; } diff --git a/osu.Game/Screens/Select/Carousel/FilterableDifficultyIcon.cs b/osu.Game/Screens/Select/Carousel/FilterableDifficultyIcon.cs index 51fe7796c7..ce0cec837b 100644 --- a/osu.Game/Screens/Select/Carousel/FilterableDifficultyIcon.cs +++ b/osu.Game/Screens/Select/Carousel/FilterableDifficultyIcon.cs @@ -17,7 +17,7 @@ namespace osu.Game.Screens.Select.Carousel public readonly CarouselBeatmap Item; public FilterableDifficultyIcon(CarouselBeatmap item) - : base(item.Beatmap, performBackgroundDifficultyLookup: false) + : base(item.BeatmapInfo, performBackgroundDifficultyLookup: false) { filtered.BindTo(item.Filtered); filtered.ValueChanged += isFiltered => Schedule(() => this.FadeTo(isFiltered.NewValue ? 0.1f : 1, 100)); diff --git a/osu.Game/Screens/Select/Carousel/FilterableGroupedDifficultyIcon.cs b/osu.Game/Screens/Select/Carousel/FilterableGroupedDifficultyIcon.cs index d2f9ed3a6a..acffdd9f64 100644 --- a/osu.Game/Screens/Select/Carousel/FilterableGroupedDifficultyIcon.cs +++ b/osu.Game/Screens/Select/Carousel/FilterableGroupedDifficultyIcon.cs @@ -16,7 +16,7 @@ namespace osu.Game.Screens.Select.Carousel public readonly List Items; public FilterableGroupedDifficultyIcon(List items, RulesetInfo ruleset) - : base(items.Select(i => i.Beatmap).ToList(), ruleset, Color4.White) + : base(items.Select(i => i.BeatmapInfo).ToList(), ruleset, Color4.White) { Items = items; diff --git a/osu.Game/Screens/Select/Carousel/SetPanelContent.cs b/osu.Game/Screens/Select/Carousel/SetPanelContent.cs index 23a02547b2..9fb640ba1a 100644 --- a/osu.Game/Screens/Select/Carousel/SetPanelContent.cs +++ b/osu.Game/Screens/Select/Carousel/SetPanelContent.cs @@ -86,7 +86,7 @@ namespace osu.Game.Screens.Select.Carousel var beatmaps = carouselSet.Beatmaps.ToList(); return beatmaps.Count > maximum_difficulty_icons - ? (IEnumerable)beatmaps.GroupBy(b => b.Beatmap.Ruleset).Select(group => new FilterableGroupedDifficultyIcon(group.ToList(), group.Key)) + ? (IEnumerable)beatmaps.GroupBy(b => b.BeatmapInfo.Ruleset).Select(group => new FilterableGroupedDifficultyIcon(group.ToList(), group.Key)) : beatmaps.Select(b => new FilterableDifficultyIcon(b)); } } diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 53e30fd9ca..8c978e25ae 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -38,16 +38,16 @@ namespace osu.Game.Screens.Select.Details protected readonly StatisticRow FirstValue, HpDrain, Accuracy, ApproachRate; private readonly StatisticRow starDifficulty; - private BeatmapInfo beatmap; + private BeatmapInfo beatmapInfo; - public BeatmapInfo Beatmap + public BeatmapInfo BeatmapInfo { - get => beatmap; + get => beatmapInfo; set { - if (value == beatmap) return; + if (value == beatmapInfo) return; - beatmap = value; + beatmapInfo = value; updateStatistics(); } @@ -106,7 +106,7 @@ namespace osu.Game.Screens.Select.Details private void updateStatistics() { - BeatmapDifficulty baseDifficulty = Beatmap?.BaseDifficulty; + BeatmapDifficulty baseDifficulty = BeatmapInfo?.BaseDifficulty; BeatmapDifficulty adjustedDifficulty = null; if (baseDifficulty != null && mods.Value.Any(m => m is IApplicableToDifficulty)) @@ -117,7 +117,7 @@ namespace osu.Game.Screens.Select.Details mod.ApplyToDifficulty(adjustedDifficulty); } - switch (Beatmap?.Ruleset?.ID ?? 0) + switch (BeatmapInfo?.Ruleset?.ID ?? 0) { case 3: // Account for mania differences locally for now @@ -145,13 +145,13 @@ namespace osu.Game.Screens.Select.Details { starDifficultyCancellationSource?.Cancel(); - if (Beatmap == null) + if (BeatmapInfo == null) return; starDifficultyCancellationSource = new CancellationTokenSource(); - var normalStarDifficulty = difficultyCache.GetDifficultyAsync(Beatmap, ruleset.Value, null, starDifficultyCancellationSource.Token); - var moddedStarDifficulty = difficultyCache.GetDifficultyAsync(Beatmap, ruleset.Value, mods.Value, starDifficultyCancellationSource.Token); + var normalStarDifficulty = difficultyCache.GetDifficultyAsync(BeatmapInfo, ruleset.Value, null, starDifficultyCancellationSource.Token); + var moddedStarDifficulty = difficultyCache.GetDifficultyAsync(BeatmapInfo, ruleset.Value, mods.Value, starDifficultyCancellationSource.Token); Task.WhenAll(normalStarDifficulty, moddedStarDifficulty).ContinueWith(_ => Schedule(() => { diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 9801098952..e4ab360765 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -377,7 +377,7 @@ namespace osu.Game.Screens.Select // avoid attempting to continue before a selection has been obtained. // this could happen via a user interaction while the carousel is still in a loading state. - if (Carousel.SelectedBeatmap == null) return; + if (Carousel.SelectedBeatmapInfo == null) return; if (beatmap != null) Carousel.SelectBeatmap(beatmap); From d55836c0b2bd529735efef5fd281e4da4e023baa Mon Sep 17 00:00:00 2001 From: Susko3 <16479013+Susko3@users.noreply.github.com> Date: Sat, 2 Oct 2021 15:10:30 +0200 Subject: [PATCH 2208/2442] Make `ResetButton` no longer part of search filtering The button will now appear if and only if all the bindings in its section are visible (not filtered out by the search) --- .../Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs index 0e8e10c086..806390c0ec 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs @@ -75,5 +75,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input Content.CornerRadius = 5; } + + public override IEnumerable FilterTerms => Enumerable.Empty(); } } From 6ec2223b5c231fc747b31e3d9f87ba810d2e376b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 2 Oct 2021 23:01:44 +0900 Subject: [PATCH 2209/2442] Catch potential file access exceptions also in async flow --- osu.Game.Tests/Database/RealmTest.cs | 38 +++++++++++++--------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/osu.Game.Tests/Database/RealmTest.cs b/osu.Game.Tests/Database/RealmTest.cs index b7658d6408..219690db30 100644 --- a/osu.Game.Tests/Database/RealmTest.cs +++ b/osu.Game.Tests/Database/RealmTest.cs @@ -39,25 +39,9 @@ namespace osu.Game.Tests.Database realmFactory.Dispose(); - try - { - Logger.Log($"Final database size: {testStorage.GetStream(realmFactory.Filename)?.Length ?? 0}"); - } - catch - { - // windows runs may error due to file still being open. - } - + Logger.Log($"Final database size: {getFileSize(testStorage, realmFactory)}"); realmFactory.Compact(); - - try - { - Logger.Log($"Final database size after compact: {testStorage.GetStream(realmFactory.Filename)?.Length ?? 0}"); - } - catch - { - // windows runs may error due to file still being open. - } + Logger.Log($"Final database size after compact: {getFileSize(testStorage, realmFactory)}"); } }); } @@ -71,16 +55,28 @@ namespace osu.Game.Tests.Database using (var realmFactory = new RealmContextFactory(testStorage, caller)) { Logger.Log($"Running test using realm file {testStorage.GetFullPath(realmFactory.Filename)}"); - await testAction(realmFactory, testStorage); realmFactory.Dispose(); - Logger.Log($"Final database size: {testStorage.GetStream(realmFactory.Filename)?.Length ?? 0}"); + Logger.Log($"Final database size: {getFileSize(testStorage, realmFactory)}"); realmFactory.Compact(); - Logger.Log($"Final database size after compact: {testStorage.GetStream(realmFactory.Filename)?.Length ?? 0}"); + Logger.Log($"Final database size after compact: {getFileSize(testStorage, realmFactory)}"); } }); } + + private static long getFileSize(Storage testStorage, RealmContextFactory realmFactory) + { + try + { + return testStorage.GetStream(realmFactory.Filename)?.Length ?? 0; + } + catch + { + // windows runs may error due to file still being open. + return 0; + } + } } } From ec61c3c5eeb2a72e74930902eccb78bc06abf1ab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 Oct 2021 00:55:29 +0900 Subject: [PATCH 2210/2442] Rename all remaining cases --- osu.Desktop/DiscordRichPresence.cs | 4 +- .../Beatmaps/ManiaBeatmapConverter.cs | 4 +- .../ManiaFilterCriteria.cs | 4 +- .../Beatmaps/IO/ImportBeatmapTest.cs | 6 +- .../NonVisual/Filtering/FilterMatchingTest.cs | 2 +- .../Filtering/FilterQueryParserTest.cs | 2 +- .../Online/TestSceneBeatmapSetOverlay.cs | 2 +- .../TestSceneBeatmapSetOverlaySuccessRate.cs | 8 +-- .../SongSelect/TestSceneBeatmapDetails.cs | 14 ++-- .../SongSelect/TestSceneBeatmapLeaderboard.cs | 26 ++++---- .../TestSceneDeleteLocalScore.cs | 13 ++-- .../TestSceneTournamentBeatmapPanel.cs | 2 +- .../TestSceneTournamentModDisplay.cs | 6 +- .../Components/TournamentBeatmapPanel.cs | 20 +++--- osu.Game.Tournament/IPC/FileBasedIPC.cs | 2 +- .../Screens/Editors/RoundEditorScreen.cs | 2 +- .../Screens/Editors/SeedingEditorScreen.cs | 2 +- .../Screens/MapPool/MapPoolScreen.cs | 6 +- osu.Game.Tournament/TournamentGameBase.cs | 4 +- osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 24 +++---- osu.Game/Beatmaps/BeatmapManager.cs | 16 ++--- osu.Game/Beatmaps/BeatmapModelManager.cs | 18 ++--- osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs | 66 +++++++++---------- osu.Game/Beatmaps/BeatmapStore.cs | 24 +++---- osu.Game/Beatmaps/DifficultyRecommender.cs | 6 +- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 36 +++++----- .../Drawables/DifficultyIconTooltip.cs | 8 +-- .../Online/API/Requests/GetBeatmapRequest.cs | 8 +-- .../Online/API/Requests/GetScoresRequest.cs | 14 ++-- .../API/Requests/Responses/APIBeatmap.cs | 2 +- .../API/Requests/Responses/APIBeatmapSet.cs | 2 +- .../Requests/Responses/APILegacyScoreInfo.cs | 8 +-- .../Responses/APIUserMostPlayedBeatmap.cs | 8 +-- osu.Game/Online/Chat/NowPlayingCommand.cs | 10 +-- osu.Game/Online/Rooms/APIPlaylistBeatmap.cs | 4 +- osu.Game/Online/Rooms/PlaylistItem.cs | 2 +- osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs | 20 +++--- osu.Game/Overlays/BeatmapSet/Info.cs | 6 +- osu.Game/Overlays/BeatmapSet/SuccessRate.cs | 16 ++--- osu.Game/Overlays/BeatmapSetOverlay.cs | 2 +- .../Sections/BeatmapMetadataContainer.cs | 18 ++--- .../Historical/DrawableMostPlayedBeatmap.cs | 24 +++---- .../Sections/Ranks/DrawableProfileScore.cs | 12 ++-- .../Rulesets/Filter/IRulesetFilterCriteria.cs | 6 +- .../Components/Menus/DifficultyMenuItem.cs | 4 +- osu.Game/Screens/Select/BeatmapCarousel.cs | 8 +-- .../Select/BeatmapClearScoresDialog.cs | 6 +- osu.Game/Screens/Select/BeatmapDetailArea.cs | 2 +- osu.Game/Screens/Select/BeatmapDetails.cs | 38 +++++------ .../Carousel/DrawableCarouselBeatmap.cs | 32 ++++----- .../Screens/Select/Carousel/TopLocalRank.cs | 12 ++-- .../Select/Leaderboards/BeatmapLeaderboard.cs | 20 +++--- .../Screens/Select/LocalScoreDeleteDialog.cs | 4 +- .../Screens/Select/PlayBeatmapDetailArea.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 32 ++++----- osu.Game/Skinning/LegacyBeatmapSkin.cs | 8 +-- .../Beatmaps/LegacyBeatmapSkinColourTest.cs | 4 +- osu.Game/Users/UserActivity.cs | 22 +++---- 58 files changed, 342 insertions(+), 341 deletions(-) diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index dcb88efeb6..e2b40e9dc6 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -140,10 +140,10 @@ namespace osu.Desktop switch (activity) { case UserActivity.InGame game: - return game.Beatmap.ToString(); + return game.BeatmapInfo.ToString(); case UserActivity.Editing edit: - return edit.Beatmap.ToString(); + return edit.BeatmapInfo.ToString(); case UserActivity.InLobby lobby: return privacyMode.Value == DiscordRichPresenceMode.Limited ? string.Empty : lobby.Room.Name.Value; diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 26393c8edb..0321a5325b 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -71,9 +71,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps originalTargetColumns = TargetColumns; } - public static int GetColumnCountForNonConvert(BeatmapInfo beatmap) + public static int GetColumnCountForNonConvert(BeatmapInfo beatmapInfo) { - var roundedCircleSize = Math.Round(beatmap.BaseDifficulty.CircleSize); + var roundedCircleSize = Math.Round(beatmapInfo.BaseDifficulty.CircleSize); return (int)Math.Max(1, roundedCircleSize); } diff --git a/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs b/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs index d9a278ef29..0290230490 100644 --- a/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs +++ b/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs @@ -13,9 +13,9 @@ namespace osu.Game.Rulesets.Mania { private FilterCriteria.OptionalRange keys; - public bool Matches(BeatmapInfo beatmap) + public bool Matches(BeatmapInfo beatmapInfo) { - return !keys.HasFilter || (beatmap.RulesetID == new ManiaRuleset().LegacyID && keys.IsInRange(ManiaBeatmapConverter.GetColumnCountForNonConvert(beatmap))); + return !keys.HasFilter || (beatmapInfo.RulesetID == new ManiaRuleset().LegacyID && keys.IsInRange(ManiaBeatmapConverter.GetColumnCountForNonConvert(beatmapInfo))); } public bool TryParseCustomKeywordCriteria(string key, Operator op, string value) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index cba7f34ede..b536fc61b7 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -945,13 +945,13 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.IsTrue(manager.QueryBeatmapSets(_ => true).First().DeletePending); } - private static Task createScoreForBeatmap(OsuGameBase osu, BeatmapInfo beatmap) + private static Task createScoreForBeatmap(OsuGameBase osu, BeatmapInfo beatmapInfo) { return ImportScoreTest.LoadScoreIntoOsu(osu, new ScoreInfo { OnlineScoreID = 2, - Beatmap = beatmap, - BeatmapInfoID = beatmap.ID + Beatmap = beatmapInfo, + BeatmapInfoID = beatmapInfo.ID }, new ImportScoreTest.TestArchiveReader()); } diff --git a/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs b/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs index 8ff2743b6a..ed86daf8b6 100644 --- a/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs +++ b/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs @@ -239,7 +239,7 @@ namespace osu.Game.Tests.NonVisual.Filtering match = shouldMatch; } - public bool Matches(BeatmapInfo beatmap) => match; + public bool Matches(BeatmapInfo beatmapInfo) => match; public bool TryParseCustomKeywordCriteria(string key, Operator op, string value) => false; } } diff --git a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs index a55bdd2df8..df42c70c87 100644 --- a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs +++ b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs @@ -256,7 +256,7 @@ namespace osu.Game.Tests.NonVisual.Filtering { public string CustomValue { get; set; } - public bool Matches(BeatmapInfo beatmap) => true; + public bool Matches(BeatmapInfo beatmapInfo) => true; public bool TryParseCustomKeywordCriteria(string key, Operator op, string value) { diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index f420ad976b..453e26ef96 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -233,7 +233,7 @@ namespace osu.Game.Tests.Visual.Online }); }); - AddAssert("shown beatmaps of current ruleset", () => overlay.Header.HeaderContent.Picker.Difficulties.All(b => b.Beatmap.Ruleset.Equals(overlay.Header.RulesetSelector.Current.Value))); + AddAssert("shown beatmaps of current ruleset", () => overlay.Header.HeaderContent.Picker.Difficulties.All(b => b.BeatmapInfo.Ruleset.Equals(overlay.Header.RulesetSelector.Current.Value))); AddAssert("left-most beatmap selected", () => overlay.Header.HeaderContent.Picker.Difficulties.First().State == BeatmapPicker.DifficultySelectorState.Selected); } diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs index fd5c188b94..fe8e33f783 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs @@ -58,10 +58,10 @@ namespace osu.Game.Tests.Visual.Online var firstBeatmap = createBeatmap(); var secondBeatmap = createBeatmap(); - AddStep("set first set", () => successRate.Beatmap = firstBeatmap); + AddStep("set first set", () => successRate.BeatmapInfo = firstBeatmap); AddAssert("ratings set", () => successRate.Graph.Metrics == firstBeatmap.Metrics); - AddStep("set second set", () => successRate.Beatmap = secondBeatmap); + AddStep("set second set", () => successRate.BeatmapInfo = secondBeatmap); AddAssert("ratings set", () => successRate.Graph.Metrics == secondBeatmap.Metrics); static BeatmapInfo createBeatmap() => new BeatmapInfo @@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestOnlyFailMetrics() { - AddStep("set beatmap", () => successRate.Beatmap = new BeatmapInfo + AddStep("set beatmap", () => successRate.BeatmapInfo = new BeatmapInfo { Metrics = new BeatmapMetrics { @@ -91,7 +91,7 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestEmptyMetrics() { - AddStep("set beatmap", () => successRate.Beatmap = new BeatmapInfo + AddStep("set beatmap", () => successRate.BeatmapInfo = new BeatmapInfo { Metrics = new BeatmapMetrics() }); diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs index b4544fbc85..d5b4fb9a80 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs @@ -30,7 +30,7 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestAllMetrics() { - AddStep("all metrics", () => details.Beatmap = new BeatmapInfo + AddStep("all metrics", () => details.BeatmapInfo = new BeatmapInfo { BeatmapSet = new BeatmapSetInfo { @@ -61,7 +61,7 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestAllMetricsExceptSource() { - AddStep("all except source", () => details.Beatmap = new BeatmapInfo + AddStep("all except source", () => details.BeatmapInfo = new BeatmapInfo { BeatmapSet = new BeatmapSetInfo { @@ -91,7 +91,7 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestOnlyRatings() { - AddStep("ratings", () => details.Beatmap = new BeatmapInfo + AddStep("ratings", () => details.BeatmapInfo = new BeatmapInfo { BeatmapSet = new BeatmapSetInfo { @@ -117,7 +117,7 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestOnlyFailsAndRetries() { - AddStep("fails retries", () => details.Beatmap = new BeatmapInfo + AddStep("fails retries", () => details.BeatmapInfo = new BeatmapInfo { Version = "Only Retries and Fails", Metadata = new BeatmapMetadata @@ -144,7 +144,7 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestNoMetrics() { - AddStep("no metrics", () => details.Beatmap = new BeatmapInfo + AddStep("no metrics", () => details.BeatmapInfo = new BeatmapInfo { Version = "No Metrics", Metadata = new BeatmapMetadata @@ -166,13 +166,13 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestNullBeatmap() { - AddStep("null beatmap", () => details.Beatmap = null); + AddStep("null beatmap", () => details.BeatmapInfo = null); } [Test] public void TestOnlineMetrics() { - AddStep("online ratings/retries/fails", () => details.Beatmap = new BeatmapInfo + AddStep("online ratings/retries/fails", () => details.BeatmapInfo = new BeatmapInfo { OnlineBeatmapID = 162, }); diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index 29815ce9ff..95cf6a9903 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -76,7 +76,7 @@ namespace osu.Game.Tests.Visual.SongSelect beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); beatmapInfo = beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps.First(); - leaderboard.Beatmap = beatmapInfo; + leaderboard.BeatmapInfo = beatmapInfo; }); clearScores(); @@ -186,7 +186,7 @@ namespace osu.Game.Tests.Visual.SongSelect private void checkCount(int expected) => AddUntilStep("Correct count displayed", () => leaderboard.ChildrenOfType().Count() == expected); - private static ScoreInfo[] generateSampleScores(BeatmapInfo beatmap) + private static ScoreInfo[] generateSampleScores(BeatmapInfo beatmapInfo) { return new[] { @@ -197,7 +197,7 @@ namespace osu.Game.Tests.Visual.SongSelect MaxCombo = 244, TotalScore = 1707827, //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - Beatmap = beatmap, + Beatmap = beatmapInfo, User = new User { Id = 6602580, @@ -216,7 +216,7 @@ namespace osu.Game.Tests.Visual.SongSelect MaxCombo = 244, TotalScore = 1707827, //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - Beatmap = beatmap, + Beatmap = beatmapInfo, User = new User { Id = 4608074, @@ -235,7 +235,7 @@ namespace osu.Game.Tests.Visual.SongSelect MaxCombo = 244, TotalScore = 1707827, //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - Beatmap = beatmap, + Beatmap = beatmapInfo, User = new User { Id = 1014222, @@ -254,7 +254,7 @@ namespace osu.Game.Tests.Visual.SongSelect MaxCombo = 244, TotalScore = 1707827, //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - Beatmap = beatmap, + Beatmap = beatmapInfo, User = new User { Id = 1541390, @@ -273,7 +273,7 @@ namespace osu.Game.Tests.Visual.SongSelect MaxCombo = 244, TotalScore = 1707827, //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - Beatmap = beatmap, + Beatmap = beatmapInfo, User = new User { Id = 2243452, @@ -292,7 +292,7 @@ namespace osu.Game.Tests.Visual.SongSelect MaxCombo = 244, TotalScore = 1707827, //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - Beatmap = beatmap, + Beatmap = beatmapInfo, User = new User { Id = 2705430, @@ -311,7 +311,7 @@ namespace osu.Game.Tests.Visual.SongSelect MaxCombo = 244, TotalScore = 1707827, //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - Beatmap = beatmap, + Beatmap = beatmapInfo, User = new User { Id = 7151382, @@ -330,7 +330,7 @@ namespace osu.Game.Tests.Visual.SongSelect MaxCombo = 244, TotalScore = 1707827, //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - Beatmap = beatmap, + Beatmap = beatmapInfo, User = new User { Id = 2051389, @@ -349,7 +349,7 @@ namespace osu.Game.Tests.Visual.SongSelect MaxCombo = 244, TotalScore = 1707827, //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - Beatmap = beatmap, + Beatmap = beatmapInfo, User = new User { Id = 6169483, @@ -368,7 +368,7 @@ namespace osu.Game.Tests.Visual.SongSelect MaxCombo = 244, TotalScore = 1707827, //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - Beatmap = beatmap, + Beatmap = beatmapInfo, User = new User { Id = 6702666, @@ -385,7 +385,7 @@ namespace osu.Game.Tests.Visual.SongSelect private void showBeatmapWithStatus(BeatmapSetOnlineStatus status) { - leaderboard.Beatmap = new BeatmapInfo + leaderboard.BeatmapInfo = new BeatmapInfo { OnlineBeatmapID = 1113057, Status = status, diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index 2e30ed9827..f58dbef145 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -37,7 +37,8 @@ namespace osu.Game.Tests.Visual.UserInterface private ScoreManager scoreManager; private readonly List importedScores = new List(); - private BeatmapInfo beatmap; + + private BeatmapInfo beatmapInfo; [Cached] private readonly DialogOverlay dialogOverlay; @@ -55,7 +56,7 @@ namespace osu.Game.Tests.Visual.UserInterface Anchor = Anchor.Centre, Size = new Vector2(550f, 450f), Scope = BeatmapLeaderboardScope.Local, - Beatmap = new BeatmapInfo + BeatmapInfo = new BeatmapInfo { ID = 1, Metadata = new BeatmapMetadata @@ -84,15 +85,15 @@ namespace osu.Game.Tests.Visual.UserInterface dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get(), Resources, dependencies.Get(), Beatmap.Default)); dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, null, ContextFactory, Scheduler)); - beatmap = beatmapManager.Import(new ImportTask(TestResources.GetQuickTestBeatmapForImport())).Result.Beatmaps[0]; + beatmapInfo = beatmapManager.Import(new ImportTask(TestResources.GetQuickTestBeatmapForImport())).Result.Beatmaps[0]; for (int i = 0; i < 50; i++) { var score = new ScoreInfo { OnlineScoreID = i, - Beatmap = beatmap, - BeatmapInfoID = beatmap.ID, + Beatmap = beatmapInfo, + BeatmapInfoID = beatmapInfo.ID, Accuracy = RNG.NextDouble(), TotalScore = RNG.Next(1, 1000000), MaxCombo = RNG.Next(1, 1000), @@ -115,7 +116,7 @@ namespace osu.Game.Tests.Visual.UserInterface leaderboard.Scores = null; leaderboard.FinishTransforms(true); // After setting scores, we may be waiting for transforms to expire drawables - leaderboard.Beatmap = beatmap; + leaderboard.BeatmapInfo = beatmapInfo; leaderboard.RefreshScores(); // Required in the case that the beatmap hasn't changed }); diff --git a/osu.Game.Tournament.Tests/Components/TestSceneTournamentBeatmapPanel.cs b/osu.Game.Tournament.Tests/Components/TestSceneTournamentBeatmapPanel.cs index bc32a12ab7..f9c553cb3f 100644 --- a/osu.Game.Tournament.Tests/Components/TestSceneTournamentBeatmapPanel.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneTournamentBeatmapPanel.cs @@ -30,7 +30,7 @@ namespace osu.Game.Tournament.Tests.Components private void success(APIBeatmap apiBeatmap) { - var beatmap = apiBeatmap.ToBeatmap(rulesets); + var beatmap = apiBeatmap.ToBeatmapInfo(rulesets); Add(new TournamentBeatmapPanel(beatmap) { Anchor = Anchor.Centre, diff --git a/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs b/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs index 47e7ed9b61..27eb55a9fb 100644 --- a/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs @@ -23,7 +23,7 @@ namespace osu.Game.Tournament.Tests.Components private FillFlowContainer fillFlow; - private BeatmapInfo beatmap; + private BeatmapInfo beatmapInfo; [BackgroundDependencyLoader] private void load() @@ -44,12 +44,12 @@ namespace osu.Game.Tournament.Tests.Components private void success(APIBeatmap apiBeatmap) { - beatmap = apiBeatmap.ToBeatmap(rulesets); + beatmapInfo = apiBeatmap.ToBeatmapInfo(rulesets); var mods = rulesets.GetRuleset(Ladder.Ruleset.Value.ID ?? 0).CreateInstance().AllMods; foreach (var mod in mods) { - fillFlow.Add(new TournamentBeatmapPanel(beatmap, mod.Acronym) + fillFlow.Add(new TournamentBeatmapPanel(beatmapInfo, mod.Acronym) { Anchor = Anchor.Centre, Origin = Anchor.Centre diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index e6d73c6e83..0e5a66e7fe 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -21,7 +21,7 @@ namespace osu.Game.Tournament.Components { public class TournamentBeatmapPanel : CompositeDrawable { - public readonly BeatmapInfo Beatmap; + public readonly BeatmapInfo BeatmapInfo; private readonly string mod; private const float horizontal_padding = 10; @@ -32,11 +32,11 @@ namespace osu.Game.Tournament.Components private readonly Bindable currentMatch = new Bindable(); private Box flash; - public TournamentBeatmapPanel(BeatmapInfo beatmap, string mod = null) + public TournamentBeatmapPanel(BeatmapInfo beatmapInfo, string mod = null) { - if (beatmap == null) throw new ArgumentNullException(nameof(beatmap)); + if (beatmapInfo == null) throw new ArgumentNullException(nameof(beatmapInfo)); - Beatmap = beatmap; + BeatmapInfo = beatmapInfo; this.mod = mod; Width = 400; Height = HEIGHT; @@ -61,7 +61,7 @@ namespace osu.Game.Tournament.Components { RelativeSizeAxes = Axes.Both, Colour = OsuColour.Gray(0.5f), - BeatmapSet = Beatmap.BeatmapSet, + BeatmapSet = BeatmapInfo.BeatmapSet, }, new FillFlowContainer { @@ -75,8 +75,8 @@ namespace osu.Game.Tournament.Components new TournamentSpriteText { Text = new RomanisableString( - $"{Beatmap.Metadata.ArtistUnicode ?? Beatmap.Metadata.Artist} - {Beatmap.Metadata.TitleUnicode ?? Beatmap.Metadata.Title}", - $"{Beatmap.Metadata.Artist} - {Beatmap.Metadata.Title}"), + $"{BeatmapInfo.Metadata.ArtistUnicode ?? BeatmapInfo.Metadata.Artist} - {BeatmapInfo.Metadata.TitleUnicode ?? BeatmapInfo.Metadata.Title}", + $"{BeatmapInfo.Metadata.Artist} - {BeatmapInfo.Metadata.Title}"), Font = OsuFont.Torus.With(weight: FontWeight.Bold), }, new FillFlowContainer @@ -93,7 +93,7 @@ namespace osu.Game.Tournament.Components }, new TournamentSpriteText { - Text = Beatmap.Metadata.AuthorString, + Text = BeatmapInfo.Metadata.AuthorString, Padding = new MarginPadding { Right = 20 }, Font = OsuFont.Torus.With(weight: FontWeight.Bold, size: 14) }, @@ -105,7 +105,7 @@ namespace osu.Game.Tournament.Components }, new TournamentSpriteText { - Text = Beatmap.Version, + Text = BeatmapInfo.Version, Font = OsuFont.Torus.With(weight: FontWeight.Bold, size: 14) }, } @@ -149,7 +149,7 @@ namespace osu.Game.Tournament.Components private void updateState() { - var found = currentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == Beatmap.OnlineBeatmapID); + var found = currentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == BeatmapInfo.OnlineBeatmapID); bool doFlash = found != choice; choice = found; diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index f538d4a7d9..7010a30eb7 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -94,7 +94,7 @@ namespace osu.Game.Tournament.IPC else { beatmapLookupRequest = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = beatmapId }); - beatmapLookupRequest.Success += b => Beatmap.Value = b.ToBeatmap(Rulesets); + beatmapLookupRequest.Success += b => Beatmap.Value = b.ToBeatmapInfo(Rulesets); API.Queue(beatmapLookupRequest); } } diff --git a/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs index 27ad6650d1..6e4fc8fe1a 100644 --- a/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs @@ -238,7 +238,7 @@ namespace osu.Game.Tournament.Screens.Editors req.Success += res => { - Model.BeatmapInfo = res.ToBeatmap(rulesets); + Model.BeatmapInfo = res.ToBeatmapInfo(rulesets); updatePanel(); }; diff --git a/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs index 6418bf97da..b64a3993e6 100644 --- a/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs @@ -246,7 +246,7 @@ namespace osu.Game.Tournament.Screens.Editors req.Success += res => { - Model.BeatmapInfo = res.ToBeatmap(rulesets); + Model.BeatmapInfo = res.ToBeatmapInfo(rulesets); updatePanel(); }; diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index d4292c5492..1e3c550323 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -147,11 +147,11 @@ namespace osu.Game.Tournament.Screens.MapPool if (map != null) { - if (e.Button == MouseButton.Left && map.Beatmap.OnlineBeatmapID != null) - addForBeatmap(map.Beatmap.OnlineBeatmapID.Value); + if (e.Button == MouseButton.Left && map.BeatmapInfo.OnlineBeatmapID != null) + addForBeatmap(map.BeatmapInfo.OnlineBeatmapID.Value); else { - var existing = CurrentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == map.Beatmap.OnlineBeatmapID); + var existing = CurrentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == map.BeatmapInfo.OnlineBeatmapID); if (existing != null) { diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 531da00faf..2e4ed9d5b1 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -182,7 +182,7 @@ namespace osu.Game.Tournament { var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID }); API.Perform(req); - b.BeatmapInfo = req.Result?.ToBeatmap(RulesetStore); + b.BeatmapInfo = req.Result?.ToBeatmapInfo(RulesetStore); addedInfo = true; } @@ -203,7 +203,7 @@ namespace osu.Game.Tournament { var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID }); req.Perform(API); - b.BeatmapInfo = req.Result?.ToBeatmap(RulesetStore); + b.BeatmapInfo = req.Result?.ToBeatmapInfo(RulesetStore); addedInfo = true; } diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index 0aa6a6dd0b..c46ab93ece 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -242,7 +242,7 @@ namespace osu.Game.Beatmaps { // GetDifficultyAsync will fall back to existing data from BeatmapInfo if not locally available // (contrary to GetAsync) - GetDifficultyAsync(bindable.Beatmap, rulesetInfo, mods, cancellationToken) + GetDifficultyAsync(bindable.BeatmapInfo, rulesetInfo, mods, cancellationToken) .ContinueWith(t => { // We're on a threadpool thread, but we should exit back to the update thread so consumers can safely handle value-changed events. @@ -262,7 +262,7 @@ namespace osu.Game.Beatmaps private StarDifficulty computeDifficulty(in DifficultyCacheLookup key) { // In the case that the user hasn't given us a ruleset, use the beatmap's default ruleset. - var beatmapInfo = key.Beatmap; + var beatmapInfo = key.BeatmapInfo; var rulesetInfo = key.Ruleset; try @@ -270,7 +270,7 @@ namespace osu.Game.Beatmaps var ruleset = rulesetInfo.CreateInstance(); Debug.Assert(ruleset != null); - var calculator = ruleset.CreateDifficultyCalculator(beatmapManager.GetWorkingBeatmap(key.Beatmap)); + var calculator = ruleset.CreateDifficultyCalculator(beatmapManager.GetWorkingBeatmap(key.BeatmapInfo)); var attributes = calculator.Calculate(key.OrderedMods); return new StarDifficulty(attributes); @@ -300,21 +300,21 @@ namespace osu.Game.Beatmaps public readonly struct DifficultyCacheLookup : IEquatable { - public readonly BeatmapInfo Beatmap; + public readonly BeatmapInfo BeatmapInfo; public readonly RulesetInfo Ruleset; public readonly Mod[] OrderedMods; - public DifficultyCacheLookup([NotNull] BeatmapInfo beatmap, [CanBeNull] RulesetInfo ruleset, IEnumerable mods) + public DifficultyCacheLookup([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo ruleset, IEnumerable mods) { - Beatmap = beatmap; + BeatmapInfo = beatmapInfo; // In the case that the user hasn't given us a ruleset, use the beatmap's default ruleset. - Ruleset = ruleset ?? Beatmap.Ruleset; + Ruleset = ruleset ?? BeatmapInfo.Ruleset; OrderedMods = mods?.OrderBy(m => m.Acronym).Select(mod => mod.DeepClone()).ToArray() ?? Array.Empty(); } public bool Equals(DifficultyCacheLookup other) - => Beatmap.ID == other.Beatmap.ID + => BeatmapInfo.ID == other.BeatmapInfo.ID && Ruleset.ID == other.Ruleset.ID && OrderedMods.SequenceEqual(other.OrderedMods); @@ -322,7 +322,7 @@ namespace osu.Game.Beatmaps { var hashCode = new HashCode(); - hashCode.Add(Beatmap.ID); + hashCode.Add(BeatmapInfo.ID); hashCode.Add(Ruleset.ID); foreach (var mod in OrderedMods) @@ -334,12 +334,12 @@ namespace osu.Game.Beatmaps private class BindableStarDifficulty : Bindable { - public readonly BeatmapInfo Beatmap; + public readonly BeatmapInfo BeatmapInfo; public readonly CancellationToken CancellationToken; - public BindableStarDifficulty(BeatmapInfo beatmap, CancellationToken cancellationToken) + public BindableStarDifficulty(BeatmapInfo beatmapInfo, CancellationToken cancellationToken) { - Beatmap = beatmap; + BeatmapInfo = beatmapInfo; CancellationToken = cancellationToken; } } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 2f80633279..a3081cc462 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -35,7 +35,7 @@ namespace osu.Game.Beatmaps private readonly BeatmapModelDownloader beatmapModelDownloader; private readonly WorkingBeatmapCache workingBeatmapCache; - private readonly BeatmapOnlineLookupQueue onlineBetamapLookupQueue; + private readonly BeatmapOnlineLookupQueue onlineBeatmapLookupQueue; public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore resources, GameHost host = null, WorkingBeatmap defaultBeatmap = null, bool performOnlineLookups = false) @@ -48,8 +48,8 @@ namespace osu.Game.Beatmaps if (performOnlineLookups) { - onlineBetamapLookupQueue = new BeatmapOnlineLookupQueue(api, storage); - beatmapModelManager.OnlineLookupQueue = onlineBetamapLookupQueue; + onlineBeatmapLookupQueue = new BeatmapOnlineLookupQueue(api, storage); + beatmapModelManager.OnlineLookupQueue = onlineBeatmapLookupQueue; } } @@ -182,14 +182,14 @@ namespace osu.Game.Beatmaps /// /// Delete a beatmap difficulty. /// - /// The beatmap difficulty to hide. - public void Hide(BeatmapInfo beatmap) => beatmapModelManager.Hide(beatmap); + /// The beatmap difficulty to hide. + public void Hide(BeatmapInfo beatmapInfo) => beatmapModelManager.Hide(beatmapInfo); /// /// Restore a beatmap difficulty. /// - /// The beatmap difficulty to restore. - public void Restore(BeatmapInfo beatmap) => beatmapModelManager.Restore(beatmap); + /// The beatmap difficulty to restore. + public void Restore(BeatmapInfo beatmapInfo) => beatmapModelManager.Restore(beatmapInfo); #endregion @@ -329,7 +329,7 @@ namespace osu.Game.Beatmaps public void Dispose() { - onlineBetamapLookupQueue?.Dispose(); + onlineBeatmapLookupQueue?.Dispose(); } #endregion diff --git a/osu.Game/Beatmaps/BeatmapModelManager.cs b/osu.Game/Beatmaps/BeatmapModelManager.cs index 0beddc1e9b..aa14f95863 100644 --- a/osu.Game/Beatmaps/BeatmapModelManager.cs +++ b/osu.Game/Beatmaps/BeatmapModelManager.cs @@ -173,24 +173,24 @@ namespace osu.Game.Beatmaps /// /// Delete a beatmap difficulty. /// - /// The beatmap difficulty to hide. - public void Hide(BeatmapInfo beatmap) => beatmaps.Hide(beatmap); + /// The beatmap difficulty to hide. + public void Hide(BeatmapInfo beatmapInfo) => beatmaps.Hide(beatmapInfo); /// /// Restore a beatmap difficulty. /// - /// The beatmap difficulty to restore. - public void Restore(BeatmapInfo beatmap) => beatmaps.Restore(beatmap); + /// The beatmap difficulty to restore. + public void Restore(BeatmapInfo beatmapInfo) => beatmaps.Restore(beatmapInfo); /// /// Saves an file against a given . /// - /// The to save the content against. The file referenced by will be replaced. + /// The to save the content against. The file referenced by will be replaced. /// The content to write. /// The beatmap content to write, null if to be omitted. - public virtual void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null) + public virtual void Save(BeatmapInfo baetmapInfo, IBeatmap beatmapContent, ISkin beatmapSkin = null) { - var setInfo = info.BeatmapSet; + var setInfo = baetmapInfo.BeatmapSet; using (var stream = new MemoryStream()) { @@ -201,7 +201,7 @@ namespace osu.Game.Beatmaps using (ContextFactory.GetForWrite()) { - var beatmapInfo = setInfo.Beatmaps.Single(b => b.ID == info.ID); + var beatmapInfo = setInfo.Beatmaps.Single(b => b.ID == baetmapInfo.ID); var metadata = beatmapInfo.Metadata ?? setInfo.Metadata; // grab the original file (or create a new one if not found). @@ -219,7 +219,7 @@ namespace osu.Game.Beatmaps } } - WorkingBeatmapCache?.Invalidate(info); + WorkingBeatmapCache?.Invalidate(baetmapInfo); } /// diff --git a/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs b/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs index 55164e2442..e1faf6005b 100644 --- a/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs +++ b/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs @@ -58,18 +58,18 @@ namespace osu.Game.Beatmaps } // todo: expose this when we need to do individual difficulty lookups. - protected Task UpdateAsync(BeatmapSetInfo beatmapSet, BeatmapInfo beatmap, CancellationToken cancellationToken) - => Task.Factory.StartNew(() => lookup(beatmapSet, beatmap), cancellationToken, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler); + protected Task UpdateAsync(BeatmapSetInfo beatmapSet, BeatmapInfo beatmapInfo, CancellationToken cancellationToken) + => Task.Factory.StartNew(() => lookup(beatmapSet, beatmapInfo), cancellationToken, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler); - private void lookup(BeatmapSetInfo set, BeatmapInfo beatmap) + private void lookup(BeatmapSetInfo set, BeatmapInfo beatmapInfo) { - if (checkLocalCache(set, beatmap)) + if (checkLocalCache(set, beatmapInfo)) return; if (api?.State.Value != APIState.Online) return; - var req = new GetBeatmapRequest(beatmap); + var req = new GetBeatmapRequest(beatmapInfo); req.Failure += fail; @@ -82,18 +82,18 @@ namespace osu.Game.Beatmaps if (res != null) { - beatmap.Status = res.Status; - beatmap.BeatmapSet.Status = res.BeatmapSet.Status; - beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID; - beatmap.OnlineBeatmapID = res.OnlineBeatmapID; + beatmapInfo.Status = res.Status; + beatmapInfo.BeatmapSet.Status = res.BeatmapSet.Status; + beatmapInfo.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID; + beatmapInfo.OnlineBeatmapID = res.OnlineBeatmapID; - if (beatmap.Metadata != null) - beatmap.Metadata.AuthorID = res.AuthorID; + if (beatmapInfo.Metadata != null) + beatmapInfo.Metadata.AuthorID = res.AuthorID; - if (beatmap.BeatmapSet.Metadata != null) - beatmap.BeatmapSet.Metadata.AuthorID = res.AuthorID; + if (beatmapInfo.BeatmapSet.Metadata != null) + beatmapInfo.BeatmapSet.Metadata.AuthorID = res.AuthorID; - logForModel(set, $"Online retrieval mapped {beatmap} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}."); + logForModel(set, $"Online retrieval mapped {beatmapInfo} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}."); } } catch (Exception e) @@ -103,8 +103,8 @@ namespace osu.Game.Beatmaps void fail(Exception e) { - beatmap.OnlineBeatmapID = null; - logForModel(set, $"Online retrieval failed for {beatmap} ({e.Message})"); + beatmapInfo.OnlineBeatmapID = null; + logForModel(set, $"Online retrieval failed for {beatmapInfo} ({e.Message})"); } } @@ -149,7 +149,7 @@ namespace osu.Game.Beatmaps cacheDownloadRequest.PerformAsync(); } - private bool checkLocalCache(BeatmapSetInfo set, BeatmapInfo beatmap) + private bool checkLocalCache(BeatmapSetInfo set, BeatmapInfo beatmapInfo) { // download is in progress (or was, and failed). if (cacheDownloadRequest != null) @@ -159,9 +159,9 @@ namespace osu.Game.Beatmaps if (!storage.Exists(cache_database_name)) return false; - if (string.IsNullOrEmpty(beatmap.MD5Hash) - && string.IsNullOrEmpty(beatmap.Path) - && beatmap.OnlineBeatmapID == null) + if (string.IsNullOrEmpty(beatmapInfo.MD5Hash) + && string.IsNullOrEmpty(beatmapInfo.Path) + && beatmapInfo.OnlineBeatmapID == null) return false; try @@ -174,9 +174,9 @@ namespace osu.Game.Beatmaps { cmd.CommandText = "SELECT beatmapset_id, beatmap_id, approved, user_id FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path"; - cmd.Parameters.Add(new SqliteParameter("@MD5Hash", beatmap.MD5Hash)); - cmd.Parameters.Add(new SqliteParameter("@OnlineBeatmapID", beatmap.OnlineBeatmapID ?? (object)DBNull.Value)); - cmd.Parameters.Add(new SqliteParameter("@Path", beatmap.Path)); + cmd.Parameters.Add(new SqliteParameter("@MD5Hash", beatmapInfo.MD5Hash)); + cmd.Parameters.Add(new SqliteParameter("@OnlineBeatmapID", beatmapInfo.OnlineBeatmapID ?? (object)DBNull.Value)); + cmd.Parameters.Add(new SqliteParameter("@Path", beatmapInfo.Path)); using (var reader = cmd.ExecuteReader()) { @@ -184,18 +184,18 @@ namespace osu.Game.Beatmaps { var status = (BeatmapSetOnlineStatus)reader.GetByte(2); - beatmap.Status = status; - beatmap.BeatmapSet.Status = status; - beatmap.BeatmapSet.OnlineBeatmapSetID = reader.GetInt32(0); - beatmap.OnlineBeatmapID = reader.GetInt32(1); + beatmapInfo.Status = status; + beatmapInfo.BeatmapSet.Status = status; + beatmapInfo.BeatmapSet.OnlineBeatmapSetID = reader.GetInt32(0); + beatmapInfo.OnlineBeatmapID = reader.GetInt32(1); - if (beatmap.Metadata != null) - beatmap.Metadata.AuthorID = reader.GetInt32(3); + if (beatmapInfo.Metadata != null) + beatmapInfo.Metadata.AuthorID = reader.GetInt32(3); - if (beatmap.BeatmapSet.Metadata != null) - beatmap.BeatmapSet.Metadata.AuthorID = reader.GetInt32(3); + if (beatmapInfo.BeatmapSet.Metadata != null) + beatmapInfo.BeatmapSet.Metadata.AuthorID = reader.GetInt32(3); - logForModel(set, $"Cached local retrieval for {beatmap}."); + logForModel(set, $"Cached local retrieval for {beatmapInfo}."); return true; } } @@ -204,7 +204,7 @@ namespace osu.Game.Beatmaps } catch (Exception ex) { - logForModel(set, $"Cached local retrieval for {beatmap} failed with {ex}."); + logForModel(set, $"Cached local retrieval for {beatmapInfo} failed with {ex}."); } return false; diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index e3214b7c03..197581db88 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -25,40 +25,40 @@ namespace osu.Game.Beatmaps /// /// Hide a in the database. /// - /// The beatmap to hide. + /// The beatmap to hide. /// Whether the beatmap's was changed. - public bool Hide(BeatmapInfo beatmap) + public bool Hide(BeatmapInfo beatmapInfo) { using (ContextFactory.GetForWrite()) { - Refresh(ref beatmap, Beatmaps); + Refresh(ref beatmapInfo, Beatmaps); - if (beatmap.Hidden) return false; + if (beatmapInfo.Hidden) return false; - beatmap.Hidden = true; + beatmapInfo.Hidden = true; } - BeatmapHidden?.Invoke(beatmap); + BeatmapHidden?.Invoke(beatmapInfo); return true; } /// /// Restore a previously hidden . /// - /// The beatmap to restore. + /// The beatmap to restore. /// Whether the beatmap's was changed. - public bool Restore(BeatmapInfo beatmap) + public bool Restore(BeatmapInfo beatmapInfo) { using (ContextFactory.GetForWrite()) { - Refresh(ref beatmap, Beatmaps); + Refresh(ref beatmapInfo, Beatmaps); - if (!beatmap.Hidden) return false; + if (!beatmapInfo.Hidden) return false; - beatmap.Hidden = false; + beatmapInfo.Hidden = false; } - BeatmapRestored?.Invoke(beatmap); + BeatmapRestored?.Invoke(beatmapInfo); return true; } diff --git a/osu.Game/Beatmaps/DifficultyRecommender.cs b/osu.Game/Beatmaps/DifficultyRecommender.cs index ca910e70b8..b1b1e58ab7 100644 --- a/osu.Game/Beatmaps/DifficultyRecommender.cs +++ b/osu.Game/Beatmaps/DifficultyRecommender.cs @@ -62,14 +62,14 @@ namespace osu.Game.Beatmaps if (!recommendedDifficultyMapping.TryGetValue(r, out var recommendation)) continue; - BeatmapInfo beatmap = beatmaps.Where(b => b.Ruleset.Equals(r)).OrderBy(b => + BeatmapInfo beatmapInfo = beatmaps.Where(b => b.Ruleset.Equals(r)).OrderBy(b => { var difference = b.StarDifficulty - recommendation; return difference >= 0 ? difference * 2 : difference * -1; // prefer easier over harder }).FirstOrDefault(); - if (beatmap != null) - return beatmap; + if (beatmapInfo != null) + return beatmapInfo; } return null; diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 0751a777d8..880d70aec2 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -37,7 +37,7 @@ namespace osu.Game.Beatmaps.Drawables } [NotNull] - private readonly BeatmapInfo beatmap; + private readonly BeatmapInfo beatmapInfo; [CanBeNull] private readonly RulesetInfo ruleset; @@ -56,26 +56,26 @@ namespace osu.Game.Beatmaps.Drawables /// /// Creates a new with a given and combination. /// - /// The beatmap to show the difficulty of. + /// The beatmap to show the difficulty of. /// The ruleset to show the difficulty with. /// The mods to show the difficulty with. /// Whether to display a tooltip when hovered. - public DifficultyIcon([NotNull] BeatmapInfo beatmap, [CanBeNull] RulesetInfo ruleset, [CanBeNull] IReadOnlyList mods, bool shouldShowTooltip = true) - : this(beatmap, shouldShowTooltip) + public DifficultyIcon([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo ruleset, [CanBeNull] IReadOnlyList mods, bool shouldShowTooltip = true) + : this(beatmapInfo, shouldShowTooltip) { - this.ruleset = ruleset ?? beatmap.Ruleset; + this.ruleset = ruleset ?? beatmapInfo.Ruleset; this.mods = mods ?? Array.Empty(); } /// /// Creates a new that follows the currently-selected ruleset and mods. /// - /// The beatmap to show the difficulty of. + /// The beatmap to show the difficulty of. /// Whether to display a tooltip when hovered. /// Whether to perform difficulty lookup (including calculation if necessary). - public DifficultyIcon([NotNull] BeatmapInfo beatmap, bool shouldShowTooltip = true, bool performBackgroundDifficultyLookup = true) + public DifficultyIcon([NotNull] BeatmapInfo beatmapInfo, bool shouldShowTooltip = true, bool performBackgroundDifficultyLookup = true) { - this.beatmap = beatmap ?? throw new ArgumentNullException(nameof(beatmap)); + this.beatmapInfo = beatmapInfo ?? throw new ArgumentNullException(nameof(beatmapInfo)); this.shouldShowTooltip = shouldShowTooltip; this.performBackgroundDifficultyLookup = performBackgroundDifficultyLookup; @@ -105,7 +105,7 @@ namespace osu.Game.Beatmaps.Drawables Child = background = new Box { RelativeSizeAxes = Axes.Both, - Colour = colours.ForStarDifficulty(beatmap.StarDifficulty) // Default value that will be re-populated once difficulty calculation completes + Colour = colours.ForStarDifficulty(beatmapInfo.StarDifficulty) // Default value that will be re-populated once difficulty calculation completes }, }, new ConstrainedIconContainer @@ -114,27 +114,27 @@ namespace osu.Game.Beatmaps.Drawables Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, // the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment) - Icon = (ruleset ?? beatmap.Ruleset)?.CreateInstance()?.CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle } + Icon = (ruleset ?? beatmapInfo.Ruleset)?.CreateInstance()?.CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle } }, }; if (performBackgroundDifficultyLookup) - iconContainer.Add(new DelayedLoadUnloadWrapper(() => new DifficultyRetriever(beatmap, ruleset, mods) { StarDifficulty = { BindTarget = difficultyBindable } }, 0)); + iconContainer.Add(new DelayedLoadUnloadWrapper(() => new DifficultyRetriever(beatmapInfo, ruleset, mods) { StarDifficulty = { BindTarget = difficultyBindable } }, 0)); else - difficultyBindable.Value = new StarDifficulty(beatmap.StarDifficulty, 0); + difficultyBindable.Value = new StarDifficulty(beatmapInfo.StarDifficulty, 0); difficultyBindable.BindValueChanged(difficulty => background.Colour = colours.ForStarDifficulty(difficulty.NewValue.Stars)); } ITooltip IHasCustomTooltip.GetCustomTooltip() => new DifficultyIconTooltip(); - DifficultyIconTooltipContent IHasCustomTooltip.TooltipContent => shouldShowTooltip ? new DifficultyIconTooltipContent(beatmap, difficultyBindable) : null; + DifficultyIconTooltipContent IHasCustomTooltip.TooltipContent => shouldShowTooltip ? new DifficultyIconTooltipContent(beatmapInfo, difficultyBindable) : null; private class DifficultyRetriever : Component { public readonly Bindable StarDifficulty = new Bindable(); - private readonly BeatmapInfo beatmap; + private readonly BeatmapInfo beatmapInfo; private readonly RulesetInfo ruleset; private readonly IReadOnlyList mods; @@ -143,9 +143,9 @@ namespace osu.Game.Beatmaps.Drawables [Resolved] private BeatmapDifficultyCache difficultyCache { get; set; } - public DifficultyRetriever(BeatmapInfo beatmap, RulesetInfo ruleset, IReadOnlyList mods) + public DifficultyRetriever(BeatmapInfo beatmapInfo, RulesetInfo ruleset, IReadOnlyList mods) { - this.beatmap = beatmap; + this.beatmapInfo = beatmapInfo; this.ruleset = ruleset; this.mods = mods; } @@ -157,8 +157,8 @@ namespace osu.Game.Beatmaps.Drawables { difficultyCancellation = new CancellationTokenSource(); localStarDifficulty = ruleset != null - ? difficultyCache.GetBindableDifficulty(beatmap, ruleset, mods, difficultyCancellation.Token) - : difficultyCache.GetBindableDifficulty(beatmap, difficultyCancellation.Token); + ? difficultyCache.GetBindableDifficulty(beatmapInfo, ruleset, mods, difficultyCancellation.Token) + : difficultyCache.GetBindableDifficulty(beatmapInfo, difficultyCancellation.Token); localStarDifficulty.BindValueChanged(d => { if (d.NewValue is StarDifficulty diff) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs b/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs index 0329e935bc..d4c9f83a0a 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs @@ -89,7 +89,7 @@ namespace osu.Game.Beatmaps.Drawables public void SetContent(DifficultyIconTooltipContent content) { - difficultyName.Text = content.Beatmap.Version; + difficultyName.Text = content.BeatmapInfo.Version; starDifficulty.UnbindAll(); starDifficulty.BindTo(content.Difficulty); @@ -109,12 +109,12 @@ namespace osu.Game.Beatmaps.Drawables internal class DifficultyIconTooltipContent { - public readonly BeatmapInfo Beatmap; + public readonly BeatmapInfo BeatmapInfo; public readonly IBindable Difficulty; - public DifficultyIconTooltipContent(BeatmapInfo beatmap, IBindable difficulty) + public DifficultyIconTooltipContent(BeatmapInfo beatmapInfo, IBindable difficulty) { - Beatmap = beatmap; + BeatmapInfo = beatmapInfo; Difficulty = difficulty; } } diff --git a/osu.Game/Online/API/Requests/GetBeatmapRequest.cs b/osu.Game/Online/API/Requests/GetBeatmapRequest.cs index 87925b94c6..901f7365b8 100644 --- a/osu.Game/Online/API/Requests/GetBeatmapRequest.cs +++ b/osu.Game/Online/API/Requests/GetBeatmapRequest.cs @@ -8,13 +8,13 @@ namespace osu.Game.Online.API.Requests { public class GetBeatmapRequest : APIRequest { - private readonly BeatmapInfo beatmap; + private readonly BeatmapInfo beatmapInfo; - public GetBeatmapRequest(BeatmapInfo beatmap) + public GetBeatmapRequest(BeatmapInfo beatmapInfo) { - this.beatmap = beatmap; + this.beatmapInfo = beatmapInfo; } - protected override string Target => $@"beatmaps/lookup?id={beatmap.OnlineBeatmapID}&checksum={beatmap.MD5Hash}&filename={System.Uri.EscapeUriString(beatmap.Path ?? string.Empty)}"; + protected override string Target => $@"beatmaps/lookup?id={beatmapInfo.OnlineBeatmapID}&checksum={beatmapInfo.MD5Hash}&filename={System.Uri.EscapeUriString(beatmapInfo.Path ?? string.Empty)}"; } } diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index b4e0e44b2c..f3bf690ed5 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -15,20 +15,20 @@ namespace osu.Game.Online.API.Requests { public class GetScoresRequest : APIRequest { - private readonly BeatmapInfo beatmap; + private readonly BeatmapInfo beatmapInfo; private readonly BeatmapLeaderboardScope scope; private readonly RulesetInfo ruleset; private readonly IEnumerable mods; - public GetScoresRequest(BeatmapInfo beatmap, RulesetInfo ruleset, BeatmapLeaderboardScope scope = BeatmapLeaderboardScope.Global, IEnumerable mods = null) + public GetScoresRequest(BeatmapInfo beatmapInfo, RulesetInfo ruleset, BeatmapLeaderboardScope scope = BeatmapLeaderboardScope.Global, IEnumerable mods = null) { - if (!beatmap.OnlineBeatmapID.HasValue) + if (!beatmapInfo.OnlineBeatmapID.HasValue) throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}."); if (scope == BeatmapLeaderboardScope.Local) throw new InvalidOperationException("Should not attempt to request online scores for a local scoped leaderboard"); - this.beatmap = beatmap; + this.beatmapInfo = beatmapInfo; this.scope = scope; this.ruleset = ruleset ?? throw new ArgumentNullException(nameof(ruleset)); this.mods = mods ?? Array.Empty(); @@ -42,7 +42,7 @@ namespace osu.Game.Online.API.Requests foreach (APILegacyScoreInfo score in r.Scores) { - score.Beatmap = beatmap; + score.BeatmapInfo = beatmapInfo; score.OnlineRulesetID = ruleset.ID.Value; } @@ -50,12 +50,12 @@ namespace osu.Game.Online.API.Requests if (userScore != null) { - userScore.Score.Beatmap = beatmap; + userScore.Score.BeatmapInfo = beatmapInfo; userScore.Score.OnlineRulesetID = ruleset.ID.Value; } } - protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores{createQueryParameters()}"; + protected override string Target => $@"beatmaps/{beatmapInfo.OnlineBeatmapID}/scores{createQueryParameters()}"; private string createQueryParameters() { diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index 7343870dbc..c2a68c8ca1 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -64,7 +64,7 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"max_combo")] private int? maxCombo { get; set; } - public virtual BeatmapInfo ToBeatmap(RulesetStore rulesets) + public virtual BeatmapInfo ToBeatmapInfo(RulesetStore rulesets) { var set = BeatmapSet?.ToBeatmapSet(rulesets); diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index f653a654ca..35963792d0 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -116,7 +116,7 @@ namespace osu.Game.Online.API.Requests.Responses beatmapSet.Beatmaps = beatmaps?.Select(b => { - var beatmap = b.ToBeatmap(rulesets); + var beatmap = b.ToBeatmapInfo(rulesets); beatmap.BeatmapSet = beatmapSet; beatmap.Metadata = beatmapSet.Metadata; return beatmap; diff --git a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs index 567df524b1..18a0db3928 100644 --- a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs @@ -37,7 +37,7 @@ namespace osu.Game.Online.API.Requests.Responses OnlineScoreID = OnlineScoreID, Date = Date, PP = PP, - Beatmap = Beatmap, + Beatmap = BeatmapInfo, RulesetID = OnlineRulesetID, Hash = Replay ? "online" : string.Empty, // todo: temporary? Rank = Rank, @@ -100,7 +100,7 @@ namespace osu.Game.Online.API.Requests.Responses public DateTimeOffset Date { get; set; } [JsonProperty(@"beatmap")] - public BeatmapInfo Beatmap { get; set; } + public BeatmapInfo BeatmapInfo { get; set; } [JsonProperty("accuracy")] public double Accuracy { get; set; } @@ -114,10 +114,10 @@ namespace osu.Game.Online.API.Requests.Responses set { // extract the set ID to its correct place. - Beatmap.BeatmapSet = new BeatmapSetInfo { OnlineBeatmapSetID = value.ID }; + BeatmapInfo.BeatmapSet = new BeatmapSetInfo { OnlineBeatmapSetID = value.ID }; value.ID = 0; - Beatmap.Metadata = value; + BeatmapInfo.Metadata = value; } } diff --git a/osu.Game/Online/API/Requests/Responses/APIUserMostPlayedBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIUserMostPlayedBeatmap.cs index 4614fe29b7..15f67eda47 100644 --- a/osu.Game/Online/API/Requests/Responses/APIUserMostPlayedBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIUserMostPlayedBeatmap.cs @@ -16,7 +16,7 @@ namespace osu.Game.Online.API.Requests.Responses public int PlayCount { get; set; } [JsonProperty] - private BeatmapInfo beatmap { get; set; } + private BeatmapInfo beatmapInfo { get; set; } [JsonProperty] private APIBeatmapSet beatmapSet { get; set; } @@ -24,9 +24,9 @@ namespace osu.Game.Online.API.Requests.Responses public BeatmapInfo GetBeatmapInfo(RulesetStore rulesets) { BeatmapSetInfo setInfo = beatmapSet.ToBeatmapSet(rulesets); - beatmap.BeatmapSet = setInfo; - beatmap.Metadata = setInfo.Metadata; - return beatmap; + beatmapInfo.BeatmapSet = setInfo; + beatmapInfo.Metadata = setInfo.Metadata; + return beatmapInfo; } } } diff --git a/osu.Game/Online/Chat/NowPlayingCommand.cs b/osu.Game/Online/Chat/NowPlayingCommand.cs index 97a2fbdd5c..89eb00a45a 100644 --- a/osu.Game/Online/Chat/NowPlayingCommand.cs +++ b/osu.Game/Online/Chat/NowPlayingCommand.cs @@ -37,27 +37,27 @@ namespace osu.Game.Online.Chat base.LoadComplete(); string verb; - BeatmapInfo beatmap; + BeatmapInfo beatmapInfo; switch (api.Activity.Value) { case UserActivity.InGame game: verb = "playing"; - beatmap = game.Beatmap; + beatmapInfo = game.BeatmapInfo; break; case UserActivity.Editing edit: verb = "editing"; - beatmap = edit.Beatmap; + beatmapInfo = edit.BeatmapInfo; break; default: verb = "listening to"; - beatmap = currentBeatmap.Value.BeatmapInfo; + beatmapInfo = currentBeatmap.Value.BeatmapInfo; break; } - var beatmapString = beatmap.OnlineBeatmapID.HasValue ? $"[{api.WebsiteRootUrl}/b/{beatmap.OnlineBeatmapID} {beatmap}]" : beatmap.ToString(); + var beatmapString = beatmapInfo.OnlineBeatmapID.HasValue ? $"[{api.WebsiteRootUrl}/b/{beatmapInfo.OnlineBeatmapID} {beatmapInfo}]" : beatmapInfo.ToString(); channelManager.PostMessage($"is {verb} {beatmapString}", true, target); Expire(); diff --git a/osu.Game/Online/Rooms/APIPlaylistBeatmap.cs b/osu.Game/Online/Rooms/APIPlaylistBeatmap.cs index 973dccd528..00623282d3 100644 --- a/osu.Game/Online/Rooms/APIPlaylistBeatmap.cs +++ b/osu.Game/Online/Rooms/APIPlaylistBeatmap.cs @@ -13,9 +13,9 @@ namespace osu.Game.Online.Rooms [JsonProperty("checksum")] public string Checksum { get; set; } - public override BeatmapInfo ToBeatmap(RulesetStore rulesets) + public override BeatmapInfo ToBeatmapInfo(RulesetStore rulesets) { - var b = base.ToBeatmap(rulesets); + var b = base.ToBeatmapInfo(rulesets); b.MD5Hash = Checksum; return b; } diff --git a/osu.Game/Online/Rooms/PlaylistItem.cs b/osu.Game/Online/Rooms/PlaylistItem.cs index 1d409d4b56..48f1347fa1 100644 --- a/osu.Game/Online/Rooms/PlaylistItem.cs +++ b/osu.Game/Online/Rooms/PlaylistItem.cs @@ -70,7 +70,7 @@ namespace osu.Game.Online.Rooms public void MapObjects(BeatmapManager beatmaps, RulesetStore rulesets) { - Beatmap.Value ??= apiBeatmap.ToBeatmap(rulesets); + Beatmap.Value ??= apiBeatmap.ToBeatmapInfo(rulesets); Ruleset.Value ??= rulesets.GetRuleset(RulesetID); Ruleset rulesetInstance = Ruleset.Value.CreateInstance(); diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs index 60e341d2ac..3df275c6d3 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs @@ -178,21 +178,21 @@ namespace osu.Game.Overlays.BeatmapSet } starRatingContainer.FadeOut(100); - Beatmap.Value = Difficulties.FirstOrDefault()?.Beatmap; + Beatmap.Value = Difficulties.FirstOrDefault()?.BeatmapInfo; plays.Value = BeatmapSet?.OnlineInfo.PlayCount ?? 0; favourites.Value = BeatmapSet?.OnlineInfo.FavouriteCount ?? 0; updateDifficultyButtons(); } - private void showBeatmap(BeatmapInfo beatmap) + private void showBeatmap(BeatmapInfo beatmapInfo) { - version.Text = beatmap?.Version; + version.Text = beatmapInfo?.Version; } private void updateDifficultyButtons() { - Difficulties.Children.ToList().ForEach(diff => diff.State = diff.Beatmap == Beatmap.Value ? DifficultySelectorState.Selected : DifficultySelectorState.NotSelected); + Difficulties.Children.ToList().ForEach(diff => diff.State = diff.BeatmapInfo == Beatmap.Value ? DifficultySelectorState.Selected : DifficultySelectorState.NotSelected); } public class DifficultiesContainer : FillFlowContainer @@ -216,7 +216,7 @@ namespace osu.Game.Overlays.BeatmapSet private readonly Box backgroundBox; private readonly DifficultyIcon icon; - public readonly BeatmapInfo Beatmap; + public readonly BeatmapInfo BeatmapInfo; public Action OnHovered; public Action OnClicked; @@ -241,9 +241,9 @@ namespace osu.Game.Overlays.BeatmapSet } } - public DifficultySelectorButton(BeatmapInfo beatmap) + public DifficultySelectorButton(BeatmapInfo beatmapInfo) { - Beatmap = beatmap; + BeatmapInfo = beatmapInfo; Size = new Vector2(size); Margin = new MarginPadding { Horizontal = tile_spacing / 2 }; @@ -260,7 +260,7 @@ namespace osu.Game.Overlays.BeatmapSet Alpha = 0.5f } }, - icon = new DifficultyIcon(beatmap, shouldShowTooltip: false) + icon = new DifficultyIcon(beatmapInfo, shouldShowTooltip: false) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -273,7 +273,7 @@ namespace osu.Game.Overlays.BeatmapSet protected override bool OnHover(HoverEvent e) { fadeIn(); - OnHovered?.Invoke(Beatmap); + OnHovered?.Invoke(BeatmapInfo); return base.OnHover(e); } @@ -286,7 +286,7 @@ namespace osu.Game.Overlays.BeatmapSet protected override bool OnClick(ClickEvent e) { - OnClicked?.Invoke(Beatmap); + OnClicked?.Invoke(BeatmapInfo); return base.OnClick(e); } diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs index f9b8de9dba..61c660cbaa 100644 --- a/osu.Game/Overlays/BeatmapSet/Info.cs +++ b/osu.Game/Overlays/BeatmapSet/Info.cs @@ -24,10 +24,10 @@ namespace osu.Game.Overlays.BeatmapSet public readonly Bindable BeatmapSet = new Bindable(); - public BeatmapInfo Beatmap + public BeatmapInfo BeatmapInfo { - get => successRate.Beatmap; - set => successRate.Beatmap = value; + get => successRate.BeatmapInfo; + set => successRate.BeatmapInfo = value; } public Info() diff --git a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs index cde4589c98..4a9b8244a5 100644 --- a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs +++ b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs @@ -23,16 +23,16 @@ namespace osu.Game.Overlays.BeatmapSet private readonly Bar successRate; private readonly Container percentContainer; - private BeatmapInfo beatmap; + private BeatmapInfo beatmapInfo; - public BeatmapInfo Beatmap + public BeatmapInfo BeatmapInfo { - get => beatmap; + get => beatmapInfo; set { - if (value == beatmap) return; + if (value == beatmapInfo) return; - beatmap = value; + beatmapInfo = value; updateDisplay(); } @@ -40,15 +40,15 @@ namespace osu.Game.Overlays.BeatmapSet private void updateDisplay() { - int passCount = beatmap?.OnlineInfo?.PassCount ?? 0; - int playCount = beatmap?.OnlineInfo?.PlayCount ?? 0; + int passCount = beatmapInfo?.OnlineInfo?.PassCount ?? 0; + int playCount = beatmapInfo?.OnlineInfo?.PlayCount ?? 0; var rate = playCount != 0 ? (float)passCount / playCount : 0; successPercent.Text = rate.ToLocalisableString(@"0.#%"); successRate.Length = rate; percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic); - Graph.Metrics = beatmap?.Metrics; + Graph.Metrics = beatmapInfo?.Metrics; } public SuccessRate() diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index bdb3715e73..f987b57d6e 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -61,7 +61,7 @@ namespace osu.Game.Overlays Header.HeaderContent.Picker.Beatmap.ValueChanged += b => { - info.Beatmap = b.NewValue; + info.BeatmapInfo = b.NewValue; ScrollFlow.ScrollToStart(); }; } diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs index a8a4cfc365..7812a81f30 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs @@ -15,12 +15,12 @@ namespace osu.Game.Overlays.Profile.Sections /// public abstract class BeatmapMetadataContainer : OsuHoverContainer { - private readonly BeatmapInfo beatmap; + private readonly BeatmapInfo beatmapInfo; - protected BeatmapMetadataContainer(BeatmapInfo beatmap) + protected BeatmapMetadataContainer(BeatmapInfo beatmapInfo) : base(HoverSampleSet.Submit) { - this.beatmap = beatmap; + this.beatmapInfo = beatmapInfo; AutoSizeAxes = Axes.Both; } @@ -30,19 +30,19 @@ namespace osu.Game.Overlays.Profile.Sections { Action = () => { - if (beatmap.OnlineBeatmapID != null) - beatmapSetOverlay?.FetchAndShowBeatmap(beatmap.OnlineBeatmapID.Value); - else if (beatmap.BeatmapSet?.OnlineBeatmapSetID != null) - beatmapSetOverlay?.FetchAndShowBeatmapSet(beatmap.BeatmapSet.OnlineBeatmapSetID.Value); + if (beatmapInfo.OnlineBeatmapID != null) + beatmapSetOverlay?.FetchAndShowBeatmap(beatmapInfo.OnlineBeatmapID.Value); + else if (beatmapInfo.BeatmapSet?.OnlineBeatmapSetID != null) + beatmapSetOverlay?.FetchAndShowBeatmapSet(beatmapInfo.BeatmapSet.OnlineBeatmapSetID.Value); }; Child = new FillFlowContainer { AutoSizeAxes = Axes.Both, - Children = CreateText(beatmap), + Children = CreateText(beatmapInfo), }; } - protected abstract Drawable[] CreateText(BeatmapInfo beatmap); + protected abstract Drawable[] CreateText(BeatmapInfo beatmapInfo); } } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs index a419bef233..2c6fa76ca4 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs @@ -22,12 +22,12 @@ namespace osu.Game.Overlays.Profile.Sections.Historical private const int cover_width = 100; private const int corner_radius = 6; - private readonly BeatmapInfo beatmap; + private readonly BeatmapInfo beatmapInfo; private readonly int playCount; - public DrawableMostPlayedBeatmap(BeatmapInfo beatmap, int playCount) + public DrawableMostPlayedBeatmap(BeatmapInfo beatmapInfo, int playCount) { - this.beatmap = beatmap; + this.beatmapInfo = beatmapInfo; this.playCount = playCount; RelativeSizeAxes = Axes.X; @@ -46,7 +46,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { RelativeSizeAxes = Axes.Y, Width = cover_width, - BeatmapSet = beatmap.BeatmapSet, + BeatmapSet = beatmapInfo.BeatmapSet, }, new Container { @@ -77,7 +77,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical Direction = FillDirection.Vertical, Children = new Drawable[] { - new MostPlayedBeatmapMetadataContainer(beatmap), + new MostPlayedBeatmapMetadataContainer(beatmapInfo), new LinkFlowContainer(t => { t.Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular); @@ -89,7 +89,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical }.With(d => { d.AddText("mapped by "); - d.AddUserLink(beatmap.Metadata.Author); + d.AddUserLink(beatmapInfo.Metadata.Author); }), } }, @@ -120,23 +120,23 @@ namespace osu.Game.Overlays.Profile.Sections.Historical private class MostPlayedBeatmapMetadataContainer : BeatmapMetadataContainer { - public MostPlayedBeatmapMetadataContainer(BeatmapInfo beatmap) - : base(beatmap) + public MostPlayedBeatmapMetadataContainer(BeatmapInfo beatmapInfo) + : base(beatmapInfo) { } - protected override Drawable[] CreateText(BeatmapInfo beatmap) => new Drawable[] + protected override Drawable[] CreateText(BeatmapInfo beatmapInfo) => new Drawable[] { new OsuSpriteText { Text = new RomanisableString( - $"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} [{beatmap.Version}] ", - $"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} [{beatmap.Version}] "), + $"{beatmapInfo.Metadata.TitleUnicode ?? beatmapInfo.Metadata.Title} [{beatmapInfo.Version}] ", + $"{beatmapInfo.Metadata.Title ?? beatmapInfo.Metadata.TitleUnicode} [{beatmapInfo.Version}] "), Font = OsuFont.GetFont(weight: FontWeight.Bold) }, new OsuSpriteText { - Text = "by " + new RomanisableString(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist), + Text = "by " + new RomanisableString(beatmapInfo.Metadata.ArtistUnicode, beatmapInfo.Metadata.Artist), Font = OsuFont.GetFont(weight: FontWeight.Regular) }, }; diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs index 713303285a..c221f070df 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs @@ -245,27 +245,27 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks private class ScoreBeatmapMetadataContainer : BeatmapMetadataContainer { - public ScoreBeatmapMetadataContainer(BeatmapInfo beatmap) - : base(beatmap) + public ScoreBeatmapMetadataContainer(BeatmapInfo beatmapInfo) + : base(beatmapInfo) { } - protected override Drawable[] CreateText(BeatmapInfo beatmap) => new Drawable[] + protected override Drawable[] CreateText(BeatmapInfo beatmapInfo) => new Drawable[] { new OsuSpriteText { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, Text = new RomanisableString( - $"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} ", - $"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} "), + $"{beatmapInfo.Metadata.TitleUnicode ?? beatmapInfo.Metadata.Title} ", + $"{beatmapInfo.Metadata.Title ?? beatmapInfo.Metadata.TitleUnicode} "), Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold, italics: true) }, new OsuSpriteText { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Text = "by " + new RomanisableString(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist), + Text = "by " + new RomanisableString(beatmapInfo.Metadata.ArtistUnicode, beatmapInfo.Metadata.Artist), Font = OsuFont.GetFont(size: 12, italics: true) }, }; diff --git a/osu.Game/Rulesets/Filter/IRulesetFilterCriteria.cs b/osu.Game/Rulesets/Filter/IRulesetFilterCriteria.cs index 13cc41f8e0..dd2ad2cbfa 100644 --- a/osu.Game/Rulesets/Filter/IRulesetFilterCriteria.cs +++ b/osu.Game/Rulesets/Filter/IRulesetFilterCriteria.cs @@ -14,15 +14,15 @@ namespace osu.Game.Rulesets.Filter public interface IRulesetFilterCriteria { /// - /// Checks whether the supplied satisfies ruleset-specific custom criteria, + /// Checks whether the supplied satisfies ruleset-specific custom criteria, /// in addition to the ones mandated by song select. /// - /// The beatmap to test the criteria against. + /// The beatmap to test the criteria against. /// /// true if the beatmap matches the ruleset-specific custom filtering criteria, /// false otherwise. /// - bool Matches(BeatmapInfo beatmap); + bool Matches(BeatmapInfo beatmapInfo); /// /// Attempts to parse a single custom keyword criterion, given by the user via the song select search box. diff --git a/osu.Game/Screens/Edit/Components/Menus/DifficultyMenuItem.cs b/osu.Game/Screens/Edit/Components/Menus/DifficultyMenuItem.cs index 5f9b72447b..c458b65607 100644 --- a/osu.Game/Screens/Edit/Components/Menus/DifficultyMenuItem.cs +++ b/osu.Game/Screens/Edit/Components/Menus/DifficultyMenuItem.cs @@ -10,12 +10,12 @@ namespace osu.Game.Screens.Edit.Components.Menus { public class DifficultyMenuItem : StatefulMenuItem { - public BeatmapInfo Beatmap { get; } + public BeatmapInfo BeatmapInfo { get; } public DifficultyMenuItem(BeatmapInfo beatmapInfo, bool selected, Action difficultyChangeFunc) : base(beatmapInfo.Version ?? "(unnamed)", null) { - Beatmap = beatmapInfo; + BeatmapInfo = beatmapInfo; State.Value = selected; if (!selected) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index f424587e22..e5e28d2fde 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -242,15 +242,15 @@ namespace osu.Game.Screens.Select /// /// Selects a given beatmap on the carousel. /// - /// The beatmap to select. + /// The beatmap to select. /// Whether to select the beatmap even if it is filtered (i.e., not visible on carousel). /// True if a selection was made, False if it wasn't. - public bool SelectBeatmap(BeatmapInfo beatmap, bool bypassFilters = true) + public bool SelectBeatmap(BeatmapInfo beatmapInfo, bool bypassFilters = true) { // ensure that any pending events from BeatmapManager have been run before attempting a selection. Scheduler.Update(); - if (beatmap?.Hidden != false) + if (beatmapInfo?.Hidden != false) return false; foreach (CarouselBeatmapSet set in beatmapSets) @@ -258,7 +258,7 @@ namespace osu.Game.Screens.Select if (!bypassFilters && set.Filtered.Value) continue; - var item = set.Beatmaps.FirstOrDefault(p => p.BeatmapInfo.Equals(beatmap)); + var item = set.Beatmaps.FirstOrDefault(p => p.BeatmapInfo.Equals(beatmapInfo)); if (item == null) // The beatmap that needs to be selected doesn't exist in this set diff --git a/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs index b32416b361..8c33b1ea0b 100644 --- a/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs +++ b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs @@ -17,9 +17,9 @@ namespace osu.Game.Screens.Select [Resolved] private ScoreManager scoreManager { get; set; } - public BeatmapClearScoresDialog(BeatmapInfo beatmap, Action onCompletion) + public BeatmapClearScoresDialog(BeatmapInfo beatmapInfo, Action onCompletion) { - BodyText = $@"{beatmap.Metadata?.Artist} - {beatmap.Metadata?.Title}"; + BodyText = $@"{beatmapInfo.Metadata?.Artist} - {beatmapInfo.Metadata?.Title}"; Icon = FontAwesome.Solid.Eraser; HeaderText = @"Clearing all local scores. Are you sure?"; Buttons = new PopupDialogButton[] @@ -29,7 +29,7 @@ namespace osu.Game.Screens.Select Text = @"Yes. Please.", Action = () => { - Task.Run(() => scoreManager.Delete(scoreManager.QueryScores(s => !s.DeletePending && s.Beatmap.ID == beatmap.ID).ToList())) + Task.Run(() => scoreManager.Delete(scoreManager.QueryScores(s => !s.DeletePending && s.Beatmap.ID == beatmapInfo.ID).ToList())) .ContinueWith(_ => onCompletion); } }, diff --git a/osu.Game/Screens/Select/BeatmapDetailArea.cs b/osu.Game/Screens/Select/BeatmapDetailArea.cs index 89ae92ec91..72c2ba708b 100644 --- a/osu.Game/Screens/Select/BeatmapDetailArea.cs +++ b/osu.Game/Screens/Select/BeatmapDetailArea.cs @@ -22,7 +22,7 @@ namespace osu.Game.Screens.Select { beatmap = value; - Details.Beatmap = value?.BeatmapInfo; + Details.BeatmapInfo = value?.BeatmapInfo; } } diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index d59d76300a..6ace92370c 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -41,16 +41,16 @@ namespace osu.Game.Screens.Select [Resolved] private RulesetStore rulesets { get; set; } - private BeatmapInfo beatmap; + private BeatmapInfo beatmapInfo; - public BeatmapInfo Beatmap + public BeatmapInfo BeatmapInfo { - get => beatmap; + get => beatmapInfo; set { - if (value == beatmap) return; + if (value == beatmapInfo) return; - beatmap = value; + beatmapInfo = value; Scheduler.AddOnce(updateStatistics); } @@ -170,26 +170,26 @@ namespace osu.Game.Screens.Select private void updateStatistics() { - advanced.BeatmapInfo = Beatmap; - description.Text = Beatmap?.Version; - source.Text = Beatmap?.Metadata?.Source; - tags.Text = Beatmap?.Metadata?.Tags; + advanced.BeatmapInfo = BeatmapInfo; + description.Text = BeatmapInfo?.Version; + source.Text = BeatmapInfo?.Metadata?.Source; + tags.Text = BeatmapInfo?.Metadata?.Tags; // metrics may have been previously fetched - if (Beatmap?.BeatmapSet?.Metrics != null && Beatmap?.Metrics != null) + if (BeatmapInfo?.BeatmapSet?.Metrics != null && BeatmapInfo?.Metrics != null) { updateMetrics(); return; } // for now, let's early abort if an OnlineBeatmapID is not present (should have been populated at import time). - if (Beatmap?.OnlineBeatmapID == null || api.State.Value == APIState.Offline) + if (BeatmapInfo?.OnlineBeatmapID == null || api.State.Value == APIState.Offline) { updateMetrics(); return; } - var requestedBeatmap = Beatmap; + var requestedBeatmap = BeatmapInfo; var lookup = new GetBeatmapRequest(requestedBeatmap); @@ -197,11 +197,11 @@ namespace osu.Game.Screens.Select { Schedule(() => { - if (beatmap != requestedBeatmap) + if (beatmapInfo != requestedBeatmap) // the beatmap has been changed since we started the lookup. return; - var b = res.ToBeatmap(rulesets); + var b = res.ToBeatmapInfo(rulesets); if (requestedBeatmap.BeatmapSet == null) requestedBeatmap.BeatmapSet = b.BeatmapSet; @@ -218,7 +218,7 @@ namespace osu.Game.Screens.Select { Schedule(() => { - if (beatmap != requestedBeatmap) + if (beatmapInfo != requestedBeatmap) // the beatmap has been changed since we started the lookup. return; @@ -232,12 +232,12 @@ namespace osu.Game.Screens.Select private void updateMetrics() { - var hasRatings = beatmap?.BeatmapSet?.Metrics?.Ratings?.Any() ?? false; - var hasRetriesFails = (beatmap?.Metrics?.Retries?.Any() ?? false) || (beatmap?.Metrics?.Fails?.Any() ?? false); + var hasRatings = beatmapInfo?.BeatmapSet?.Metrics?.Ratings?.Any() ?? false; + var hasRetriesFails = (beatmapInfo?.Metrics?.Retries?.Any() ?? false) || (beatmapInfo?.Metrics?.Fails?.Any() ?? false); if (hasRatings) { - ratings.Metrics = beatmap.BeatmapSet.Metrics; + ratings.Metrics = beatmapInfo.BeatmapSet.Metrics; ratings.FadeIn(transition_duration); } else @@ -249,7 +249,7 @@ namespace osu.Game.Screens.Select if (hasRetriesFails) { - failRetryGraph.Metrics = beatmap.Metrics; + failRetryGraph.Metrics = beatmapInfo.Metrics; failRetryContainer.FadeIn(transition_duration); } else diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index 2fe7ff4562..5940911d4a 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -40,7 +40,7 @@ namespace osu.Game.Screens.Select.Carousel private const float height = MAX_HEIGHT * 0.6f; - private readonly BeatmapInfo beatmap; + private readonly BeatmapInfo beatmapInfo; private Sprite background; @@ -68,7 +68,7 @@ namespace osu.Game.Screens.Select.Carousel public DrawableCarouselBeatmap(CarouselBeatmap panel) { - beatmap = panel.BeatmapInfo; + beatmapInfo = panel.BeatmapInfo; Item = panel; } @@ -109,7 +109,7 @@ namespace osu.Game.Screens.Select.Carousel Origin = Anchor.CentreLeft, Children = new Drawable[] { - new DifficultyIcon(beatmap, shouldShowTooltip: false) + new DifficultyIcon(beatmapInfo, shouldShowTooltip: false) { Scale = new Vector2(1.8f), }, @@ -129,7 +129,7 @@ namespace osu.Game.Screens.Select.Carousel { new OsuSpriteText { - Text = beatmap.Version, + Text = beatmapInfo.Version, Font = OsuFont.GetFont(size: 20), Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft @@ -142,7 +142,7 @@ namespace osu.Game.Screens.Select.Carousel }, new OsuSpriteText { - Text = $"{(beatmap.Metadata ?? beatmap.BeatmapSet.Metadata).Author.Username}", + Text = $"{(beatmapInfo.Metadata ?? beatmapInfo.BeatmapSet.Metadata).Author.Username}", Font = OsuFont.GetFont(italics: true), Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft @@ -156,7 +156,7 @@ namespace osu.Game.Screens.Select.Carousel AutoSizeAxes = Axes.Both, Children = new Drawable[] { - new TopLocalRank(beatmap) + new TopLocalRank(beatmapInfo) { Scale = new Vector2(0.8f), Size = new Vector2(40, 20) @@ -200,7 +200,7 @@ namespace osu.Game.Screens.Select.Carousel protected override bool OnClick(ClickEvent e) { if (Item.State.Value == CarouselItemState.Selected) - startRequested?.Invoke(beatmap); + startRequested?.Invoke(beatmapInfo); return base.OnClick(e); } @@ -216,7 +216,7 @@ namespace osu.Game.Screens.Select.Carousel if (Item.State.Value != CarouselItemState.Collapsed) { // We've potentially cancelled the computation above so a new bindable is required. - starDifficultyBindable = difficultyCache.GetBindableDifficulty(beatmap, (starDifficultyCancellationSource = new CancellationTokenSource()).Token); + starDifficultyBindable = difficultyCache.GetBindableDifficulty(beatmapInfo, (starDifficultyCancellationSource = new CancellationTokenSource()).Token); starDifficultyBindable.BindValueChanged(d => { starCounter.Current = (float)(d.NewValue?.Stars ?? 0); @@ -233,13 +233,13 @@ namespace osu.Game.Screens.Select.Carousel List items = new List(); if (startRequested != null) - items.Add(new OsuMenuItem("Play", MenuItemType.Highlighted, () => startRequested(beatmap))); + items.Add(new OsuMenuItem("Play", MenuItemType.Highlighted, () => startRequested(beatmapInfo))); if (editRequested != null) - items.Add(new OsuMenuItem("Edit", MenuItemType.Standard, () => editRequested(beatmap))); + items.Add(new OsuMenuItem("Edit", MenuItemType.Standard, () => editRequested(beatmapInfo))); - if (beatmap.OnlineBeatmapID.HasValue && beatmapOverlay != null) - items.Add(new OsuMenuItem("Details...", MenuItemType.Standard, () => beatmapOverlay.FetchAndShowBeatmap(beatmap.OnlineBeatmapID.Value))); + if (beatmapInfo.OnlineBeatmapID.HasValue && beatmapOverlay != null) + items.Add(new OsuMenuItem("Details...", MenuItemType.Standard, () => beatmapOverlay.FetchAndShowBeatmap(beatmapInfo.OnlineBeatmapID.Value))); if (collectionManager != null) { @@ -251,7 +251,7 @@ namespace osu.Game.Screens.Select.Carousel } if (hideRequested != null) - items.Add(new OsuMenuItem("Hide", MenuItemType.Destructive, () => hideRequested(beatmap))); + items.Add(new OsuMenuItem("Hide", MenuItemType.Destructive, () => hideRequested(beatmapInfo))); return items.ToArray(); } @@ -262,12 +262,12 @@ namespace osu.Game.Screens.Select.Carousel return new ToggleMenuItem(collection.Name.Value, MenuItemType.Standard, s => { if (s) - collection.Beatmaps.Add(beatmap); + collection.Beatmaps.Add(beatmapInfo); else - collection.Beatmaps.Remove(beatmap); + collection.Beatmaps.Remove(beatmapInfo); }) { - State = { Value = collection.Beatmaps.Contains(beatmap) } + State = { Value = collection.Beatmaps.Contains(beatmapInfo) } }; } diff --git a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs index 3ad57c1cb0..f2485587d8 100644 --- a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs +++ b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs @@ -17,7 +17,7 @@ namespace osu.Game.Screens.Select.Carousel { public class TopLocalRank : UpdateableRank { - private readonly BeatmapInfo beatmap; + private readonly BeatmapInfo beatmapInfo; [Resolved] private ScoreManager scores { get; set; } @@ -31,10 +31,10 @@ namespace osu.Game.Screens.Select.Carousel private IBindable> itemUpdated; private IBindable> itemRemoved; - public TopLocalRank(BeatmapInfo beatmap) + public TopLocalRank(BeatmapInfo beatmapInfo) : base(null) { - this.beatmap = beatmap; + this.beatmapInfo = beatmapInfo; } [BackgroundDependencyLoader] @@ -55,7 +55,7 @@ namespace osu.Game.Screens.Select.Carousel { if (weakScore.NewValue.TryGetTarget(out var score)) { - if (score.BeatmapInfoID == beatmap.ID) + if (score.BeatmapInfoID == beatmapInfo.ID) fetchAndLoadTopScore(); } } @@ -79,10 +79,10 @@ namespace osu.Game.Screens.Select.Carousel private ScoreInfo fetchTopScore() { - if (scores == null || beatmap == null || ruleset?.Value == null || api?.LocalUser.Value == null) + if (scores == null || beatmapInfo == null || ruleset?.Value == null || api?.LocalUser.Value == null) return null; - return scores.QueryScores(s => s.UserID == api.LocalUser.Value.Id && s.BeatmapInfoID == beatmap.ID && s.RulesetID == ruleset.Value.ID && !s.DeletePending) + return scores.QueryScores(s => s.UserID == api.LocalUser.Value.Id && s.BeatmapInfoID == beatmapInfo.ID && s.RulesetID == ruleset.Value.ID && !s.DeletePending) .OrderByDescending(s => s.TotalScore) .FirstOrDefault(); } diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 7820264505..2fdb41a1a1 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -25,17 +25,17 @@ namespace osu.Game.Screens.Select.Leaderboards [Resolved] private RulesetStore rulesets { get; set; } - private BeatmapInfo beatmap; + private BeatmapInfo beatmapInfo; - public BeatmapInfo Beatmap + public BeatmapInfo BeatmapInfo { - get => beatmap; + get => beatmapInfo; set { - if (beatmap == value) + if (beatmapInfo == value) return; - beatmap = value; + beatmapInfo = value; Scores = null; UpdateScores(); @@ -116,7 +116,7 @@ namespace osu.Game.Screens.Select.Leaderboards if (score.NewValue.TryGetTarget(out var scoreInfo)) { - if (Beatmap?.ID != scoreInfo.BeatmapInfoID) + if (BeatmapInfo?.ID != scoreInfo.BeatmapInfoID) return; } @@ -132,7 +132,7 @@ namespace osu.Game.Screens.Select.Leaderboards loadCancellationSource?.Cancel(); loadCancellationSource = new CancellationTokenSource(); - if (Beatmap == null) + if (BeatmapInfo == null) { PlaceholderState = PlaceholderState.NoneSelected; return null; @@ -141,7 +141,7 @@ namespace osu.Game.Screens.Select.Leaderboards if (Scope == BeatmapLeaderboardScope.Local) { var scores = scoreManager - .QueryScores(s => !s.DeletePending && s.Beatmap.ID == Beatmap.ID && s.Ruleset.ID == ruleset.Value.ID); + .QueryScores(s => !s.DeletePending && s.Beatmap.ID == BeatmapInfo.ID && s.Ruleset.ID == ruleset.Value.ID); if (filterMods && !mods.Value.Any()) { @@ -168,7 +168,7 @@ namespace osu.Game.Screens.Select.Leaderboards return null; } - if (Beatmap.OnlineBeatmapID == null || Beatmap?.Status <= BeatmapSetOnlineStatus.Pending) + if (BeatmapInfo.OnlineBeatmapID == null || BeatmapInfo?.Status <= BeatmapSetOnlineStatus.Pending) { PlaceholderState = PlaceholderState.Unavailable; return null; @@ -188,7 +188,7 @@ namespace osu.Game.Screens.Select.Leaderboards else if (filterMods) requestMods = mods.Value; - var req = new GetScoresRequest(Beatmap, ruleset.Value ?? Beatmap.Ruleset, Scope, requestMods); + var req = new GetScoresRequest(BeatmapInfo, ruleset.Value ?? BeatmapInfo.Ruleset, Scope, requestMods); req.Success += r => { diff --git a/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs b/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs index 085ea372c0..1ae244281b 100644 --- a/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs +++ b/osu.Game/Screens/Select/LocalScoreDeleteDialog.cs @@ -29,8 +29,8 @@ namespace osu.Game.Screens.Select [BackgroundDependencyLoader] private void load() { - BeatmapInfo beatmap = beatmapManager.QueryBeatmap(b => b.ID == score.BeatmapInfoID); - Debug.Assert(beatmap != null); + BeatmapInfo beatmapInfo = beatmapManager.QueryBeatmap(b => b.ID == score.BeatmapInfoID); + Debug.Assert(beatmapInfo != null); BodyText = $"{score.User} ({score.DisplayAccuracy}, {score.Rank})"; diff --git a/osu.Game/Screens/Select/PlayBeatmapDetailArea.cs b/osu.Game/Screens/Select/PlayBeatmapDetailArea.cs index c87a4bbc54..b8b8e3e4bc 100644 --- a/osu.Game/Screens/Select/PlayBeatmapDetailArea.cs +++ b/osu.Game/Screens/Select/PlayBeatmapDetailArea.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Select { base.Beatmap = value; - Leaderboard.Beatmap = value is DummyWorkingBeatmap ? null : value?.BeatmapInfo; + Leaderboard.BeatmapInfo = value is DummyWorkingBeatmap ? null : value?.BeatmapInfo; } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index e4ab360765..6cafcb9d16 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -345,22 +345,22 @@ namespace osu.Game.Screens.Select /// protected abstract BeatmapDetailArea CreateBeatmapDetailArea(); - public void Edit(BeatmapInfo beatmap = null) + public void Edit(BeatmapInfo beatmapInfo = null) { if (!AllowEditing) throw new InvalidOperationException($"Attempted to edit when {nameof(AllowEditing)} is disabled"); - Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap ?? beatmapNoDebounce); + Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo ?? beatmapInfoNoDebounce); this.Push(new EditorLoader()); } /// /// Call to make a selection and perform the default action for this SongSelect. /// - /// An optional beatmap to override the current carousel selection. + /// An optional beatmap to override the current carousel selection. /// An optional ruleset to override the current carousel selection. /// An optional custom action to perform instead of . - public void FinaliseSelection(BeatmapInfo beatmap = null, RulesetInfo ruleset = null, Action customStartAction = null) + public void FinaliseSelection(BeatmapInfo beatmapInfo = null, RulesetInfo ruleset = null, Action customStartAction = null) { // This is very important as we have not yet bound to screen-level bindables before the carousel load is completed. if (!Carousel.BeatmapSetsLoaded) @@ -379,8 +379,8 @@ namespace osu.Game.Screens.Select // this could happen via a user interaction while the carousel is still in a loading state. if (Carousel.SelectedBeatmapInfo == null) return; - if (beatmap != null) - Carousel.SelectBeatmap(beatmap); + if (beatmapInfo != null) + Carousel.SelectBeatmap(beatmapInfo); if (selectionChangedDebounce?.Completed == false) { @@ -435,18 +435,18 @@ namespace osu.Game.Screens.Select } // We need to keep track of the last selected beatmap ignoring debounce to play the correct selection sounds. - private BeatmapInfo beatmapNoDebounce; + private BeatmapInfo beatmapInfoNoDebounce; private RulesetInfo rulesetNoDebounce; - private void updateSelectedBeatmap(BeatmapInfo beatmap) + private void updateSelectedBeatmap(BeatmapInfo beatmapInfo) { - if (beatmap == null && beatmapNoDebounce == null) + if (beatmapInfo == null && beatmapInfoNoDebounce == null) return; - if (beatmap?.Equals(beatmapNoDebounce) == true) + if (beatmapInfo?.Equals(beatmapInfoNoDebounce) == true) return; - beatmapNoDebounce = beatmap; + beatmapInfoNoDebounce = beatmapInfo; performUpdateSelected(); } @@ -467,12 +467,12 @@ namespace osu.Game.Screens.Select /// private void performUpdateSelected() { - var beatmap = beatmapNoDebounce; + var beatmap = beatmapInfoNoDebounce; var ruleset = rulesetNoDebounce; selectionChangedDebounce?.Cancel(); - if (beatmapNoDebounce == null) + if (beatmapInfoNoDebounce == null) run(); else selectionChangedDebounce = Scheduler.AddDelayed(run, 200); @@ -803,11 +803,11 @@ namespace osu.Game.Screens.Select dialogOverlay?.Push(new BeatmapDeleteDialog(beatmap)); } - private void clearScores(BeatmapInfo beatmap) + private void clearScores(BeatmapInfo beatmapInfo) { - if (beatmap == null || beatmap.ID <= 0) return; + if (beatmapInfo == null || beatmapInfo.ID <= 0) return; - dialogOverlay?.Push(new BeatmapClearScoresDialog(beatmap, () => + dialogOverlay?.Push(new BeatmapClearScoresDialog(beatmapInfo, () => // schedule done here rather than inside the dialog as the dialog may fade out and never callback. Schedule(() => BeatmapDetails.Refresh()))); } diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index e6ddeba316..2093182dcc 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -20,8 +20,8 @@ namespace osu.Game.Skinning protected override bool AllowManiaSkin => false; protected override bool UseCustomSampleBanks => true; - public LegacyBeatmapSkin(BeatmapInfo beatmap, IResourceStore storage, IStorageResourceProvider resources) - : base(createSkinInfo(beatmap), new LegacySkinResourceStore(beatmap.BeatmapSet, storage), resources, beatmap.Path) + public LegacyBeatmapSkin(BeatmapInfo beatmapInfo, IResourceStore storage, IStorageResourceProvider resources) + : base(createSkinInfo(beatmapInfo), new LegacySkinResourceStore(beatmapInfo.BeatmapSet, storage), resources, beatmapInfo.Path) { // Disallow default colours fallback on beatmap skins to allow using parent skin combo colours. (via SkinProvidingContainer) Configuration.AllowDefaultComboColoursFallback = false; @@ -76,7 +76,7 @@ namespace osu.Game.Skinning return base.GetSample(sampleInfo); } - private static SkinInfo createSkinInfo(BeatmapInfo beatmap) => - new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata?.AuthorString }; + private static SkinInfo createSkinInfo(BeatmapInfo beatmapInfo) => + new SkinInfo { Name = beatmapInfo.ToString(), Creator = beatmapInfo.Metadata?.AuthorString }; } } diff --git a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs index 27162b1d66..5c522058d9 100644 --- a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs +++ b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs @@ -111,8 +111,8 @@ namespace osu.Game.Tests.Beatmaps public static readonly Color4 HYPER_DASH_FRUIT_COLOUR = Color4.DarkGoldenrod; - public TestBeatmapSkin(BeatmapInfo beatmap, bool hasColours) - : base(beatmap, new ResourceStore(), null) + public TestBeatmapSkin(BeatmapInfo beatmapInfo, bool hasColours) + : base(beatmapInfo, new ResourceStore(), null) { if (hasColours) { diff --git a/osu.Game/Users/UserActivity.cs b/osu.Game/Users/UserActivity.cs index 75aa4866ff..91bcb37fcc 100644 --- a/osu.Game/Users/UserActivity.cs +++ b/osu.Game/Users/UserActivity.cs @@ -27,13 +27,13 @@ namespace osu.Game.Users public abstract class InGame : UserActivity { - public BeatmapInfo Beatmap { get; } + public BeatmapInfo BeatmapInfo { get; } public RulesetInfo Ruleset { get; } - protected InGame(BeatmapInfo info, RulesetInfo ruleset) + protected InGame(BeatmapInfo beatmapInfo, RulesetInfo ruleset) { - Beatmap = info; + BeatmapInfo = beatmapInfo; Ruleset = ruleset; } @@ -42,8 +42,8 @@ namespace osu.Game.Users public class InMultiplayerGame : InGame { - public InMultiplayerGame(BeatmapInfo beatmap, RulesetInfo ruleset) - : base(beatmap, ruleset) + public InMultiplayerGame(BeatmapInfo beatmapInfo, RulesetInfo ruleset) + : base(beatmapInfo, ruleset) { } @@ -52,27 +52,27 @@ namespace osu.Game.Users public class InPlaylistGame : InGame { - public InPlaylistGame(BeatmapInfo beatmap, RulesetInfo ruleset) - : base(beatmap, ruleset) + public InPlaylistGame(BeatmapInfo beatmapInfo, RulesetInfo ruleset) + : base(beatmapInfo, ruleset) { } } public class InSoloGame : InGame { - public InSoloGame(BeatmapInfo info, RulesetInfo ruleset) - : base(info, ruleset) + public InSoloGame(BeatmapInfo beatmapInfo, RulesetInfo ruleset) + : base(beatmapInfo, ruleset) { } } public class Editing : UserActivity { - public BeatmapInfo Beatmap { get; } + public BeatmapInfo BeatmapInfo { get; } public Editing(BeatmapInfo info) { - Beatmap = info; + BeatmapInfo = info; } public override string Status => @"Editing a beatmap"; From 281a3a0cea270b1531cc13ee1a9ae6cb559788c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 2 Oct 2021 18:40:41 +0200 Subject: [PATCH 2211/2442] Add test case for legacy loop count behaviour --- .../Formats/LegacyStoryboardDecoderTest.cs | 27 +++++++++++++++++++ osu.Game.Tests/Resources/loop-count.osb | 15 +++++++++++ 2 files changed, 42 insertions(+) create mode 100644 osu.Game.Tests/Resources/loop-count.osb diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs index bcde899789..560e2ef894 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs @@ -149,5 +149,32 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(AnimationLoopType.LoopForever, ((StoryboardAnimation)foreground.Elements[5]).LoopType); } } + + [Test] + public void TestDecodeLoopCount() + { + // all loop sequences in loop-count.osb have a total duration of 2000ms (fade in 0->1000ms, fade out 1000->2000ms). + const double loop_duration = 2000; + + var decoder = new LegacyStoryboardDecoder(); + + using (var resStream = TestResources.OpenResource("loop-count.osb")) + using (var stream = new LineBufferedReader(resStream)) + { + var storyboard = decoder.Decode(stream); + + StoryboardLayer background = storyboard.Layers.Single(l => l.Depth == 3); + + // stable ensures that any loop command executes at least once, even if the loop count specified in the .osb is zero or negative. + StoryboardSprite zeroTimes = background.Elements.OfType().Single(s => s.Path == "zero-times.png"); + Assert.That(zeroTimes.EndTime, Is.EqualTo(1000 + loop_duration)); + + StoryboardSprite oneTime = background.Elements.OfType().Single(s => s.Path == "one-time.png"); + Assert.That(oneTime.EndTime, Is.EqualTo(4000 + loop_duration)); + + StoryboardSprite manyTimes = background.Elements.OfType().Single(s => s.Path == "many-times.png"); + Assert.That(manyTimes.EndTime, Is.EqualTo(9000 + 40 * loop_duration)); + } + } } } diff --git a/osu.Game.Tests/Resources/loop-count.osb b/osu.Game.Tests/Resources/loop-count.osb new file mode 100644 index 0000000000..ec75e85ef1 --- /dev/null +++ b/osu.Game.Tests/Resources/loop-count.osb @@ -0,0 +1,15 @@ +osu file format v14 + +[Events] +Sprite,Background,TopCentre,"zero-times.png",320,240 + L,1000,0 + F,0,0,1000,0,1 + F,0,1000,2000,1,0 +Sprite,Background,TopCentre,"one-time.png",320,240 + L,4000,1 + F,0,0,1000,0,1 + F,0,1000,2000,1,0 +Sprite,Background,TopCentre,"many-times.png",320,240 + L,9000,40 + F,0,0,1000,0,1 + F,0,1000,2000,1,0 From 3e403cfe031604792798898218927691d3c2fe21 Mon Sep 17 00:00:00 2001 From: Susko3 <16479013+Susko3@users.noreply.github.com> Date: Sat, 2 Oct 2021 19:16:46 +0200 Subject: [PATCH 2212/2442] Add comment explaining the purpose of the empty `FilterTerms` --- .../Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs index 806390c0ec..2cc2857e9b 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs @@ -76,6 +76,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input Content.CornerRadius = 5; } + // Empty FilterTerms so that the ResetButton is visible only when the whole subsection is visible. public override IEnumerable FilterTerms => Enumerable.Empty(); } } From bc1ff019da91aebf64f730b424ba4f442096ef71 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Sun, 3 Oct 2021 11:27:17 +0100 Subject: [PATCH 2213/2442] Implement relax checks --- .../Difficulty/OsuPerformanceCalculator.cs | 15 +++++++++++++++ osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 5 +++++ 2 files changed, 20 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index bf4d92652c..a8e43ce09b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -90,6 +90,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty aimValue *= lengthBonus; + if (mods.Any(h => h is OsuModRelax)) + { + aimValue *= 0.75; + countMiss += countOk + countMeh; + } + // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. if (countMiss > 0) aimValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), countMiss); @@ -126,6 +132,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty { double speedValue = Math.Pow(5.0 * Math.Max(1.0, Attributes.SpeedStrain / 0.0675) - 4.0, 3.0) / 100000.0; + if (mods.Any(h => h is OsuModRelax)) + { + speedValue *= 0.75; + countMiss += countOk + countMeh; + } + // Longer maps are worth more. double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) + (totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0); @@ -160,6 +172,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty private double computeAccuracyValue() { + if (mods.Any(h => h is OsuModRelax)) + return 0.0; + // This percentage only considers HitCircles of any value - in this part of the calculation we focus on hitting the timing hit window. double betterAccuracyPercentage; int amountHitObjectsWithAccuracy = Attributes.HitCircleCount; diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 9364b11048..e8169ba660 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -7,6 +7,8 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Objects; using osu.Framework.Utils; +using System.Linq; +using osu.Game.Rulesets.Osu.Mods; namespace osu.Game.Rulesets.Osu.Difficulty.Skills { @@ -79,6 +81,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } } + if (Mods.Any(m => m is OsuModRelax)) + speedBonus = 0.0; + return (1 + (speedBonus - 1) * 0.75) * angleBonus * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) From 03f0a3658947d0b99e0d91c7e06f0700e728f08f Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Sun, 3 Oct 2021 11:53:14 +0100 Subject: [PATCH 2214/2442] additional balancing --- .../Difficulty/OsuPerformanceCalculator.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index a8e43ce09b..62e0dea4fa 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -40,6 +40,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty countMeh = Score.Statistics.GetValueOrDefault(HitResult.Meh); countMiss = Score.Statistics.GetValueOrDefault(HitResult.Miss); + if (mods.Any(h => h is OsuModRelax)) + { + countMiss += countOk + countMeh; + } + // Custom multipliers for NoFail and SpunOut. double multiplier = 1.12; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things. @@ -92,8 +97,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(h => h is OsuModRelax)) { - aimValue *= 0.75; - countMiss += countOk + countMeh; + aimValue *= 0.6; } // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. @@ -134,8 +138,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(h => h is OsuModRelax)) { - speedValue *= 0.75; - countMiss += countOk + countMeh; + speedValue *= 0.6; } // Longer maps are worth more. @@ -219,6 +222,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(h => h is OsuModHidden)) flashlightValue *= 1.3; + if (mods.Any(h => h is OsuModRelax)) + { + flashlightValue *= 0.6; + } + // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. if (countMiss > 0) flashlightValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), Math.Pow(countMiss, .875)); From f05cb6bb5b677255517212369ed4292d1d4c48e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 3 Oct 2021 13:53:26 +0200 Subject: [PATCH 2215/2442] Add test case covering reset section button hiding --- .../Visual/Settings/TestSceneKeyBindingPanel.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index 168d9fafcf..1effe52608 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using osu.Framework.Testing; using osu.Framework.Threading; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osu.Game.Overlays.Settings.Sections.Input; using osuTK.Input; @@ -230,6 +231,22 @@ namespace osu.Game.Tests.Visual.Settings AddAssert("first binding selected", () => multiBindingRow.ChildrenOfType().First().IsBinding); } + [Test] + public void TestFilteringHidesResetSectionButtons() + { + SearchTextBox searchTextBox = null; + + AddStep("add any search term", () => + { + searchTextBox = panel.ChildrenOfType().Single(); + searchTextBox.Current.Value = "chat"; + }); + AddUntilStep("all reset section bindings buttons hidden", () => panel.ChildrenOfType().All(button => button.Alpha == 0)); + + AddStep("clear search term", () => searchTextBox.Current.Value = string.Empty); + AddUntilStep("all reset section bindings buttons shown", () => panel.ChildrenOfType().All(button => button.Alpha == 1)); + } + private void checkBinding(string name, string keyName) { AddAssert($"Check {name} is bound to {keyName}", () => From 4f00a9e165af5d7b8a321e2bf72fe81144331482 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 Oct 2021 22:32:46 +0900 Subject: [PATCH 2216/2442] Adjust max runtime for diffcalc runs --- .github/workflows/diffcalc.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/diffcalc.yml b/.github/workflows/diffcalc.yml index bc2626d3d6..9e11ab6663 100644 --- a/.github/workflows/diffcalc.yml +++ b/.github/workflows/diffcalc.yml @@ -53,6 +53,7 @@ jobs: diffcalc: name: Run runs-on: self-hosted + timeout-minutes: 1440 if: needs.metadata.outputs.continue == 'yes' needs: metadata strategy: From 07c11953cddbf66b5b84c996449f6af82821b1d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 3 Oct 2021 15:39:54 +0200 Subject: [PATCH 2217/2442] Modify special test skin to visually cover regression --- .../special-skin/hitcircleoverlay@2x.png | Bin 247101 -> 26595 bytes .../Resources/special-skin/skin.ini | 3 +++ 2 files changed, 3 insertions(+) create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/special-skin/skin.ini diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hitcircleoverlay@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hitcircleoverlay@2x.png index a9b2d95d882b99a53aeba501919b5e5f1600bf26..8e50cd033596fb3daacd173247d6779168cc8f99 100755 GIT binary patch literal 26595 zcmXtf1z1z>`}f(#U?WF|#3&`CRX}QlGzv&5tssg>O2;`WPa6viJp%h001T&O{m*WzV4VU=C2sy1exeavrCgohQHSo z=)3a^$b`A};>N@aInm*~Svw3;Ns2#|j5!!(WMnw`uWvuM@GI>0wM#5;!LpX8=G0Mta&- zArpTakHV+)Uy6r`LhUyg(tpc(R(SQwRy&eEc+4o1mk%0V{V9?iz11t;{Z3+|Tjl_c z)N4C|uyz}Y;b<_%BI0cai|KC9-qp-yg%8v!LlK`}gl$n>B<9@H<|5Nbl{G%P$)mTa z846CJhJ%qk3)c06QoCq%O_AHT;DWF*@{wPX%Mb=VTaWSE5A+)`PIjgY9D zL$mv#-#RtnkoS;?@5_4NWo>;xsD9!cJaQsMTpU;b74So%S5?XX4#$+=%vEFNS5jSwxje`$OOrgV1Ag?N8;wNjk*7nUiiS7W^JcE@ zgi;0{mC!4SfXk|gDc8UGoZK8c34TlCoe2>uIrW>lc5>69_kZtygcG9uBxbF_OnQ#e zb-3uuErvR`b?xR}o|DPRmWqwYcd3YOJ)Gj4-0rTQdw3Sz)OJ8?k*1APr-G3f{jLSZ z4dT-(fYV%G?xhj#iZG`=ZBx{e&GpmqPuvOgBgOzYW*~m^20M$0pk3%b)j0}x zeeo3(#{fJDd6QVZ_~q1rx*&@a&{y{(csqoX+SwpII3W|8kD+t1ISdetZF4aUhXho& z%G*TgfKWgpaXD*SzKvjk)V|(SWel)&m2Ue2L@pO0$rNFLv26zdbo>(5@=n4QKTFKv zae)*(M`0Y|P@L$fr~gi3z=pY*yAT_j8|`TG5b25hD*~iJa%ll;pdj&pAsQhzw1 zUTnfnqPR^Vd<^EmGZcRuPe(MO4j6*qE&*TLP9ozZ#BQkcjJ{{U^m}jJbPzfG)vB`_ z7p-OV9|6&2v1!1Hr}=P}m`R6w286+Zmw?L+bx-KuE6#h$S6*~7G*I8+*wV ziTj1PCN!Q4aQLtDMM%jxLEWv*Dc}t2x!}Z;s43J|(pR_yEJ|wbW9S)a%NcXP8G^eE zd;&1K>T-`plm7dp9Qk6(M?kZ?OifS29O8YC`&dWV0Y+2>EzJ4>DlgO>i_e}GW{sVQ9(+VImrtJiLB@E{+8)jI_>DBdw+k_Lo7R`OJf~_dQ zu>7@U(f2Lvsu3#`<1!@LWT*^Lz6lUt790o8F9#Tlq|EnbcAIOj(FYE3R^=iA1Hcz; zIIkld^XTwY!X2g0XrV2FY1nA?$fq6hfSz8l)=H)!MHi)r&gj#xT!gIr2R-hD9I;taU8!lWr71 zc6sqyW+R;pMUfeO%5lNYtS(r3U@*4NnM>&U^%Jk@CyZ{kZ_jNO%vYKfNVJw&i%{nz9V=uDf^{@JEoAV+CHW!XU_3;ej9 zY^l$ipJv%1;VyeSS)qq#AxoiyXF01E+iM+d4m^=>nNjLM2qz==xtjUT^04b5glCFr zM3a9LbP@OnsvxS_5Y_w?jdzB#UJ!1$j~W%Ed8%!0c!>6gJREE$NGv*A9W6keVP%fh zj6gl~a27xM)80~D^Z3wZnW!v&_pYPkltxv2viD=gIE5ENhcobc%p(%TCSRC>-G;)pqV+b6SUQQ3cOo6k%faRC&-$)pUTT&Q{6h4V;O7*NTB&I!H; zH`paO9<${#CA8N$t?e^>CTN{f?!&VF^uAh^nxB`a8v7l!OM#9Fs=wyLj&Vx*s(VfQ zR?x&A7Mk0!3feU6?^&baNEfpo6@{5?!%SW)XB7gk_hzb=WXQeFj~GACZCrb+^e*S8 z=od->AMV-37oMqmdnUg051)R9;gzkYNPMf=XfD!ejU*%@s)DN3w(7)nAn1j@iep&# zPyNKT@N?H6{@1dMWdf)o2p9H-LXFz~{XE04Yo*thYM$73uUBaBvvf9EV8=Ep_Na$-tY-I+f{e)ALEzL+Ol!-U z%pbB|?+6@ftG}VI^=VGnTnH%RQ_|-RTz{m&d#z*Ng zXJmZz%t^-j@y!a)kzSM9kqKkJ%Bq`1>FLIcI)$mqJF4I*9^aiXFsD@%T8SCGbjk!! z=mEL*6OA0cTm>)fVb{V_&F4TEFn8_yD2pgC{-Z2rbdbslM6n4w3p7;C(mg#93HOi< z{L}VotaZzG_)joxdUmh(myy3&j@f&%6Wp!E10yxlo2L5vsbRUns_qZzLpcAkt+36p zO(h4oXyyi7IacDjhBVBR1+-2oGF^O(*?97JZ>7FRBk?ZL56yHxct6W}b#CNVd%=q} zo4l&m&h|H@FM2-i@prFvkhGx>mCjLm*gBl3?8`aW<aixs-)kQ%$;Rjxbhr(kxd8^+_^SpIm-+$6WQdvT}+uhS&AM3EKM4_AW#{6 zh(Ne27Ohb5e(oGi_Da`KphG$R&3wCyFN=F?^-C}trgzu%>I>2Bx&IEMnUxT42@3tC*mQ#VmLw6A$Mpb8_5f&JoH6crwI%9$u-zJ z4&GRo%o~0)7KFXPQ&l%Q{blXT{cJ--n1lb7hCpI z$(3;?)x^xZ*=J6-2UMDnudKF{~3DmObBRG#D|HRAuzq?09&qaeb9U(8g$?| zEySVawj2YIs;mvvi_8=5mkmnhP34!WRdp`Rl#gopIDbtUNGWB>q+5U9`lcYj))@1#<+9^s@m|0;mXDMWqckEUZ9V&uL%t=^h0T~Y z{?1(1y{E=+-sefS|hQjyJ*3CUQe+&}W z2-V__6Svc&VTq<0(xAgyeZQvj#=4s-0GtL3AC0!J{e}UvypIhQf(X<1t6rDHEX|Lc zd^uQONLrw{pWK={vp4-n$yIjGGMsaFW>PSN%;^N3VF|+Sd0e+d`0TZpP+X;mvm#bT zEt_PIB0>tvN|A^_LTiL!KD64c@!oTmr3Es!wx9Z6^#97MG#JV5*tg94Szw(sVmZ)r zgYVle3x>wZkg7zwLrWSxsXOF%zsoG~-c{HvEPJi>ZU@$nj0=9mPh3!u76zB4`d z_O_qZR@PZZeBOG$A41;R?${1%v?+R#f3c>u-27(rr4TS&=1Q4up7fB%A7y4uo#j6K zw^N=&?jy1fFo#X#ntk$Yd)7!T#WH>3$%EkI?2SJoe@14uX4*G93C=~lQ$n}mR{u-Y(@^d%H_JS0s^C*3Uwi_lf{)1?@;Gu_=Hj4G{g{q{%OF1Js zUYa*R2Fd{$^<$Wu&qT}_%SNE_>4mWutne*bmpKdzjoSAlKsz1X}PDdo1)rf zMeKArEUxlw&D=@5HibO%#K60F^mB^b6S~dJO|`-5oa#4T>Ng$?pV_l)vKqcPJ(L*4 z&7MuuFkf-**Xw81o+or6Ck*;P1QCCC?mh&Zf}EpH)e8k#C$i zU7ozCR`qSO3Y5=dyikfSqQ78@a_3+*MV(`G(65b`wB*F=3TW~iOoLAI@b~t6>d5}i zz^K~bnaSeGVo>|M_&yY}u2;dFc^V#lqn(nke z9>xr;!xELJxjFXlK~L30R-Vt5O(q1mWH zOSMJ3Aufuyfyr(A4GPP}+l-OGixr6&QhE}T~3EFOY^ z7d7Z0ld>`4oo7>OZ8m+u=M@wBEwDP*|ElXgJCbds^RGPGS#XYyVjzrX;f3lSVH+@z zSUiDd#&3%1h@;*o=|}qP1;`K7ng|5T^D)03^{_2tna!dcZ=~-uE;J5YS^h3KF3Kl% z;L}Akqlq9iq(I3c#7@Z3gKC%oMJUSO1!atg3Y@d0)h@y*upDaZ5mgNs1KJR}SxRk> z4)~U{s>5V7-z4ArI9esA#@)K+eV-I@QoN@h`Jgbhm;1uI`_+Fr?!F^6kj8%;C>Y}C z>>w`~PhIEe7~q>}fQ}B~O0#p?sV$5k0y+_@E{>`?mAvppL%DU?Ikc4%M3BMI)RL{P-*G8pHP2HdbS1)>wHsA%*b{ z60OO4Hw$(}_ME9M2fH4Rc9BeOvj1q{xQqHj`^KM#b>SKjTZip;!vn)BZ+=Q+nwIxg z#k_w6-DT=y{D~H6auNMib*L8T5mjKx^2=GG9lZ2k;aS4>`p-U5;(0>n z?J-ml-a2zQX)rqhx@YM)2jOdpL1#=bZ5NLm?rmDQ($O;Dgg6;#7ioiE(@h1^mP>4P zqRQ4kz8S7Bd`}JrqB{6T{pw@94{}3&DZMjtoz$_D#b4#2V>$bKl8aB)Vr?QTSX^$B z^M`{YHKLGzRYoXpqcvrC`y^sqtO0+^RErJ|D+nO3eU|SVYT=(n`w9>6#QA*)5O{o; z==}B1SkT$8TB~Y}u7eax_rdsvs3QC*(t{m+D*lDgV57gg-sbF6yX?6Cf+u4xPJ!#fid%>n&7V~K?g9cuSajaphxR5-F4{v z)!^RgcW(My$jW{~WVnjp)i>Unj%6=At$)Zrwtt5E3M)lJxEoaQuF2*@K5+9y|C%rA zn0irC=N{RwRg0kkpMjBCBrssIA_ zuIezd#iVk2YV)S zhpvY@??31J=zR?NHPCk3~ zq)Yp!kcO?9uk2C(^mjaO9t>35<-!U4l+&d$kIkzyfs^Da6~ED^*4WFx7itDtisZV{ zh`)a7lyv%2E4{g1gqURCAX`Wk<|w?c00OvtXA5zg3q;DQ^#w^j7=L2cxj#mJ6Ln45 z_L8mt9od}Ey12HB^^7x=w5K5|a{_8rcUdsz5-Ou?4Ghw`(`=_ki`cZ~^n_4#SlK95 z4k(BI@i+n9{bhcZ1kkT6W3eX(F@y$6?>Oa86hZl~8knH)kG?u47eYx;4A<|y)}kod z(-b!*4iWRo1OVr->QCTHbxtp&$Jjy3c-wwoG_r5x3TDZEvT${!El}ci@`DoZlBJRe z%|%V9szQxa>7wvdmc;Ol-8J0Y?JeDUT!yFpOU(7N2pT%8gIf-fAY5nq#B+Y|mSZ~2MLd?f`%^asQwI^Sq zuu^2sZlRB>9z<%a9!i(#PzG`rn1fyqfJjz`x4t={co7EE!WF&ByvsOu+P{k7+AAAG zOV>-wVRWb_ag`z^lri+4749Y5X_B3s(Jvnfr$4jA`WpuGm*%)%Q2!E~$cyB`GU|7A zbuIp`OF1-zh0CT@hi7#tCjavKq<_3%F`#*SL^N|z`Jref)4b~w$IG)l7=L6+ZDg4f zyufnoR7R|KUE7$*tF)JZoG<;;`HOF}rN22Z2|+BQITT4gD~$ijg@v=Q0R5jKGM)dZOP0uf;`Hv|q0UB<5(9#R1g6n%UVUQ7zjG!|Wi<@+OdjvU32viJ3^kb7Fn-)}32zYbeWPCzzam_0{} zW$VtdCWjZ%ik!zYY)rk1H+FAts`fAhZhs$cIK#V8GEy><^{O{1JJzV;w$vz@bBbT7 zX(h27EsgUohTdYuw9Ls3RLpE+;<)D?A8WE{8{Hx$ZOv@`vrT22Q5vT=-jk~#gx;yK zXId}{*%D^)5(8rzb^sf7{2=Epe|uf`f&KAax7(pG`w#B)fCi zwbcrI4H2IHQ4F%e6uv!!hc+{+&*)`cv!jW#e=c%U$eJ>K~3Sgp2l`5sXYN?<(A5k?m5 z6X9|!Ly0dlXuwcv2fRNz+<(2d{DR|;%KRQn$5BQNIIKxxvkA3n!}irIP{=>%sKzBQ|np^!aSc%)<%t{;9$=1 z)hws=ENf7VY#p3#CJz3qT*LlLH^a&kXDqg=>Tcl8PM5w>!Z- ztm=&>9o8A_mgnC>w*XozM)qGM}n-gcWxAb?%)R#Yu@yAG_O zhbsAa-Jg6trk!z?r2uIPW_>c9R-D9$jQ>2Dbl9%siWgA)kiwsi!6JY3A;hxklxO*0 z2%Ns&yY^3Dwo@9NWv`Z9O%M$?mCXO4tL7M{H2;zLsE;NJGBsKzfqNZGKsywl9>>x`fII*Ik-USOKZx1 zo$rWpFe>+yu(={W*9E22=NM0Z9?b)c0m5Np>VK&kwq7r{#d|ND4=B{}jK3q+4|~rU za$es5*B2*x4?SO8~S^`O?syx^NnA!cSU*`-z-^!}OI zn!NX`EIu;r+pEVK!n&Hghe~W2q7YY>JBEO8o46&amH#>;kLwfO=tnVj!9#)jPiBL9 zd;}6qF(y)JUNqr&3u`LrxVxK zgaZ7|iEci*^1~b@+;?sxwlZwJdo5;kJ*+UwavwmJV@ z%@mv!fKtwrdZU}sY`&4zhXGH|aL_6y%U20xzNvnlH@olbG}5~lpP2M}FG9K<6LqL1LYh2hQq!)U6jJvxsN z*5qONGk;`3U8MBxgw;Cr-KcfQH5;5G z7354yNv1g!LyZWwA;RsGVA}wlD!*`BX`Su&N16JE*q5CAO%Kn2 zjo}6hS6+mETy)YW%qLmQ)Or(G-Ap+Q6V`d$rbd>?^8!G!-h1Q3!t8OS>Sf0WLnRqHf>Hud^JmkK>n2 zWos5Q+7}OI^hPqSCt-$|Dj==_5Qb`o1>gwFJwlLbpxUP1`n7pyZu#BbG08e$^S9$i z&@ada!tA3gj}49}xk|@MBo8r^iy9dY`@Pq8Mf=rFGkuTJE1Q@6OGo+CLq|g+4~s&_ zo9>!rb;pVm27brR*+NaM@iGlSOwt|Lo$}IlAoBGpLH2hq?fbhY7OUUE68o&yq!t#G z;$zSfHoAIb>^lEG@rb$d60c>iWK5hD+=aIXgyKE|OC>;iT1vJ;{a=T%G8QN22j_j# zq}z=xL|@BZVidQ4SQ=qrGe#}zYUYptysp(DxAM_JIQ3i$)%Lk9c6`Uj>0kZM?_t#N zW(sX7O;pBi&Yy!$5}Eh#%c3n*JAr7L^KtY=@MAca;9ICfzQY@_-Q&5vD?4?e57esq zRc=dKVXR&LAg87TS>%Yf(&#Z#OMX48gn~_~LjmsnNKXB5esXMVes!i&7$UJ%pE>iHVMgdDoVh=)a;!0423_K8)xp*M0X6c~(R|rBx5=QV?+S7l zyk4QANLP6db!8yT2r8E(uACkBdw5o~{fk%;CCx&!sxxn)_j&PXLRs1C(f)5Cx0C|& zu34?h<%31kjJT+A<7afS0(9pNo*Ke?G7cyXu56nr=PuoMQa1QkhZS&sc_OEH}~T;f@Ke{ zzt>uhxU{$2EZTHqCB5Y9+gxM%DCs#xKPD*~q7dTWBI-jzeQ-nYx7Erz*}!c30$kbL zQc!3JewU?n%akXPy9;ZFCNJKnX_z*hj4ovJIbV`<S-bE1{$ zH8^>sk3Zuiq)~C}h44gn%9R^{;}Na0aOf=dCV4iysa_qdgZ}F?^t@MyAzb;cWj|i` zKoDJm`HM-l(Bxs7%G=4K)c*)huQrElpEtYVvPZ*(zQEXqY$eEFOl4bi`{$wS^}Q~RF$?mRby(JozIfL>{)ywMq^SLZsn$JKT>l|&o&9)bSn$=5B1mJ zOZi@e6UQsut56W@g)DUe2J%Ji4^B{0^V(2eZce3YPGi zVfWE#>2Q#N(+%Dq`k@c;6(5`AQF3Vfkg5>>bxMK}Q|Q#_*${)M7td_4Jre6o;pBt< zgVA&8A7kc2IEs%u1rGX)uvHs9^m>nxofeSRtuBpPb%6$-Cc*|&cx2CAK2CEsyGhiT zs6qwbT^JV=RA!sX7qZh+@2m74rFMEdYwM9x#o#3Odd!lmTIs<-HMxaH4F>+l3Hm

yftJ!Y0GsdcN%;MEM+5u>2KjHnOqx_AeVsl z_CP*3E@GR?8nTYm7?*od@l=B^+bO&1n(VdXMvK|N!rqKja`Kj{vv~f?(>M=p=c)}| zOvv?Al|yS>FjK${A~9cf?eXzO_o5MXclE2rX9LkU5zkX)UYqP+`w)CzE`P7{&dJ9g zm@6d|ZEWnPV~@4lML3)+>Nhx4NZYf1R_J$6m!a zz;P`c#CMGl|9vdXa+l(``Ew=)Y?f{W9!%J@YDKla@!VTZx;n}cj^r)XdUS$yq|ae$LT>Uj2lnE z1dwpKVfy~Khpk8BMsBeE8FTeueKK_Fv$lPZI=q>on^SJIy-O|nHwMH59hPy_rys-6`O;iCCar`US4wr>E(?kD5Y>WE5cYiTgY2_-v?f!90 z0@e!o;xXmU+FRD>38qxfW-X339=Z-5mC|1O>6ek;nhY~xhVD+>JWQFSZ0TL2AXv{2 z?92H(47a=>M3p=c>Jl;1$Oro9&iswXP?w0hc3W37yEKZ&wwC=tH*KGFq`Smj%EZ!j z_?%_>o8>SJcwHmTx9dX?1AFG}bP=vmh>N3V;Zb*(etZI%a}K_T0^I_L5I zdTOw|0T_ur`mCEiob3&{Q+2sY$?s<4&s?>YwtcDv-uW!`OJL3e96^BR9Nh|2-PPhz zzN=Z?V)J`4gY=(>{f;s3uF|||Dv1MTs4V5%LIsTM>Bv_ayp!ur;ln~qclIeJA52%N zpsLa30u4F|Jo1|1ELXA27~Vy&EpTsdLR5h@V+nNgmbrd<+Br>JmAr;zy3a84xXd(# zOWKyk(@!-%tF@MDya@@X`eZ29P;-u`UXdHBxMRN_iRx_R`s1*DU-hq&`S-3Ow{KsN zqQjvXP%r5UoYlAXoxuddXISVUA=2O&dy!_VH}AqTX;7|xE}=nf8=D$8(*-4n>^l1= zh3+=Ew&!|uG{R-Rq?v2_s$D!Iry)NOCV-=qD+^BO8rV?(CQoDi8TGpUgZ4e) z>o|TTFzR=b*Q8O^I2qLqYSGuD7AUcg>aixg2sEu-dWcSx<{GQNwshARNIm8HRY$zb zd?)6(+{hWBhHAS;b2$V?%O%!ulA;0`153I4LxSIU8Hi~zFBqUiFZVTJb_g*7B~e5e zgeKqh8OsUDjAjcaCKB&Dxp3+3x)K=bf$ayn5#RK-XHQ=7ei4}Doyhz1Sj+0{@9w2H zN|~u8;WC@Tyefyu+nmbGyk`1O*2kD{pEo;4$sSvmFU$$yrS`g z&NpLE*6W7nS3AEy1pbL=38T(sh=1+yJiCWZ=kp3(Uib5%r~+wvmv8IM!cs7Etxi(p zx3Mfk$$Ze5y;7;lqP#i-VLlXh7bT7#XJLdwH0U1F1PmOtRTQp|JyB*L{`d{3;%|V> zS;$w=m9B?J;F=fs@?n;JB5TiInq05QJ2x;xl>+gQ4%IRC%qQonbb0gC zv!Pwhr?r5#Dp5zPYy(#Qj12&MdmJwLd3ACrQ|-hafm@anmPUMONVD{=X9uq*D5h&~ zU&RR(-?g}l5~q&HdA9V=;K%533LPEKE7t0sqz5100ZuI?hn3wu(e^9_s|kxYv`1^| zBohM$97kQjQ@JNsa0Dt8*(JO|@`#;~X+MltPbRm*^-e4oA$6rw%BVr zI8?}b>>UTbEn(qupPzu#0IjJKL+(jAU(kh+zl@j}Rvz6JRH{P9xl&z6<8iae%{z#@ zH@9>*YF;~9j)x6Np?_cf8Kr1m-BF*W*cliLGvtRFe|lEm2UtN3`x0dXU)8}?49`Nu zgy?&0ssyuiTz1acpJbBLEH=#3KUg(LKu;@{foRQ?_rd^(`yF;aj{_8m9@6&OSDuDt z7=6T5Y$xt@mt)`smt5YyN1%YIeC6uX9!kS_k4*^c?j^Xw#`ixTYTsvAaAG{6H6z^F zC%o;E)X!SMU`A+ivG*^U>2TzhfF$)f8(^#pC?fhmfB2cx`4a3J(V7VbCLkli44&3 zhL6bO5t~{f_xV@vedk-+veC%ziSgH^?u?YmeTArWaPP1K*MKM@kErgCNBW44Tboav zCtR*=(-bBAeb)U`*GYR6d_5^^_h6kQR+yKj)v4iPTNPu&L4?jWT!5?AHKHCrU=%IA4?hAs+# z*yX3cxwQ^B*W$*WWI}2Hm!xGeF9gqRFW#3Qknp)~A&#@@I__5bG~;20M3ygQZ%0-1 zaSF7tsPBO>JavUgmegHBFvR8jo6YmE#N&5Q`7!13Ow+?={lr)l2f{m6DKw`SvE=sKp7hW>=_RxJKxa!hM3L+L`;N* zAeGJlBj5Z#c)BOwx2MMynN=<|VxYcgDk)}EG|U8I1IF*`8Gu0cH*hr1$LoM+#geeG zi!l&^htB;6Ef*e_N44;O`Fz=iPGu1 z42*{r%*>YEeOl8o(yZ+Bv-3nf8rhSnYT3of2sSJ@{>z|GKIQ~s=hZ>0><;5>x|OO< z2e_@o+sImdYIreRIeQ1e8 zfnB_}SQA6~tq}uxH9BIRBChi;Z2SiRw;n>!zl7wkI5DC_&jFe7?p7uv4wBB_ z-BBw{T5T-Qe{t0n1OpT^4oQW$fH&^vdk$PM6?La`LCdcW{?XZb4AVsFh{Z4fk&9Y} z&tT~5%DXoUZ6p>=;!E!-Y0ve0v5fae$bXIadKYn&f-~(>OHY@tSxBPT-F+#PB)$AH8zzn}cCy(Vb zU#W88C*iNq&slaQi>U%Fi02J2l+%0m{*h_%m50BGycu)qIL>R@;3#L93T~v`!NOi@ zG$2&khc!zDcn^J^j^rR_!R>~bQqKo4&6I1uD{1Dr?Z*4E*1-`(m@mHK2h8%nMKGuK z!`#;JMfe+W^+c*|5@$edo#j`?-nQ=j&aQ}3>2k@DP`6_tHwZf3!&h&}7w%Lntc^;n zxR!aw!V zx~*w;VJ~J{E@DX+*DJ}tE1k>fC;Z)2Z6&Sf>prX@s{ti^djQ@{8Tr94O`X&z=c6(N z!zl@?vMmBM?4W{wT3q$7xcQ0a@;6pt8lyAlS6lHienC3sRf6SwHLtca8>=*qt|7E9 zf3v8xQ?T(YZr3SI{Yedm0p&q>urB5z@^g7WQ=p`G>6q;PArZ2~SO*rAKKxJi>|hVW8g*F9vRPVG9`>$kfp9 z1tm*si}{xSAu+P@V;mV@IkRMUsVF~m*E{}P7;sLT=ztXbON-3WS z9h)n02KAGv%L-Ga>ArX1oaFg04PM+UWf!nExft-^UWt`#8a>~)XKx^AnWIO<_nD~P zrBF8mDk;~X=<|AGhlr~HFH>?VNX+%`Cg#B{qM1c7six&Xt=?DR1XCi z%*s<2qaa#lnv*Qu7F``ZWuimrc@Vx+W&A?NeJL3@VlZK&y(4X(V~T7;Q1!YrrcazD zt#QdBA=llwhINoPM@BkC(KaNS<0)#>*vd2 zX>icx(eevJHy)Me@I!IC575thPm8F1>i-dr6JoJ%`rBRj&fX|3{as~qsW?BPFqc)P z|BsdkKgb4NNyGG5p{%X#`HPHVb~{zZx1tk6Os$E3)2#n(+NhXt-thBr@0vAx z)k_zq?r+YzN+kcl_6hI5R`(afZuC(;j-OyfPO(M%LSj=)Ja=GT^SU!Jqba5jZP3Cr z+yQZ?KUZxcL1x=^h0170UyNmiK2EK@Csiq4An*H=wd-eR;UOe%#kE8$R^dB>^%9YR zN)Jv0R5UnE-JC80pH~+aySMiddVdqX|2Jb>%L;-q20N3)rtN-`7y+D$dOWgf9Qd)q z(XsUWHT!6PMRUc%BIunK;wRD6k`z1gau;#*_HpC@|Jq1}A+Awx&^e949Dbm1&~4ljjEG*y$@n+_&K7Q>$gH zOryOfOB=`DmEb~|AD@R$Zi^wPvYemHB3zt@A&6;48euVj!4Tuk?8#u{PlF6bL}xI5 z;wsLcuAE*3Au_itx}opD-m{V%szFBS3v~P#)tvMFU=;Dlnw$jE7@T34a-4K51)oTJ zj)mbXBJrxqG|!)w5Xu={o-!qVhTkS#5^I2$X*|8FnCcSkZ!<7qH&dWB9%7l43GeHN z>sgVLX+FQOIX)S|n9pLL9ypht&%Ac&&G4m{%B!l<=^u{k=N_LnaoU}cOFdy&Bxu15 z`Dlv7<|JmP6vQ0u%Fjya4z&Lm^bAyUl6#i-0kn)9rk(aYg0u-6!!=f<-I{^y%meK7^6+52<#w!KI#D>?X2hr|cx5l(B`#+me0BD3UEnmMk-4tVxl*QbXBg z4`rPqi7b)GHq&A!TNuppy?cEA`TgeKdCbhY&s_Jp&UMb~dG7jsFYoY}X1Bgv%?CG} z2t=wKUR%CsppoU2QhF87aeDT0%_q;(#RpLz%;g=yJGG-|t$ID@AF)xM?dMzB`FbKK z`iDmr6KuB)3>~iPYz^vPybbIWr0)^RGk93M1b zO0f$=YF+VS(LnV&@KawFbsn!iElexdWq|KSkTot%mucSy<(+DQszJ)mCD|4>^f}6q zYphD>mfAgLlH{usX9RTm*vl4Jv>=on6+57uUuo`ysFedTP_N2?U7+hS#ET)=F3J0O z3ruSg*3(3318zj)u57~(5l#EA54?VT@+)>yD4qEG(t$ayuDkyc6HG?WmXlJrl8>-z zAc!343-Hx@wbFI$B*eAL+5;va1lqA3?Hv``YW-P2^D(TO3*K-}(lvbfFcXgJ_CE}) zxheR3{hWvISJ6-qV#$vlO>?2|VX$PhD}G>j!|Ljl`nBPC^wpeiR72_D%DzaKW|nRW*&N3T@B%JT?B*Q|ruL6Gy3-mJi7$W=0t z0F5<#;^66JKu>`81RDmzW6xstEfO}{cH2+@pI#{1b33$ncV}?i-%3l|u`i)sPY|Kn z1m)1?@HUr(F^^GEUmL5+4hr|Uqjom;<))W0h=v6B==gwM z&krdI=FSi4exr@=75*jU32-7pIPtMCm@HA)$E^amE9GS_$w4=a>C8AtGRGOFm-vDw zlTST;CO@WT1eK;LT|s@fp4F|NVmLoCvzTQ2WLctkRjLmfPw$c>Rr0L%L$QJm7%@N@ z7(T*EaH+CWLQh4vd~3V8s|%)W&nHqt2G96-I^d7IkQ!nI1l=K7VBAE-p)u3imkw5* zsD>S?JoX5}x#ODl?^wR3XFQ}4NJH7A!F8!w;tWLUa~MSWJ*g;6e*lVQ2?{&2Bo0yI z)ojz>n3@R^vC<-5s=}MxId+}p`h@dqR(kl+!O1vbDxCs~54wsvP41k_W<0f0B>%yV z-T;@-sF?um(_>~#>@&%AqSNkSSLYjc67z#~GCQIkvgPnIC?VCE4Sx*CyJrR*vHdK_ zktUKR;@IjKSNUfr;^#z`Mqg<4OGzbg6p9cm@g!yt62eKsUrRM`!0jguX6dss0H(y^ zVO&vX_1*9$MW?&38b7aT}IY_(e*`5aUhAPiEVDqe>>xwTje%4_a+(4*JDdj29;WahnJoTky; zr>py!LG#Y@5bY;@EL@tm-aT`H^f;M7g{eM-t6Wh&mS*KO2Uz#fy6PpfaS&G*^L_Hr zd%4rrz46Lwhf^f@eIRw|XnSl&F0Qj0V5mWLubF+@TL`YiO><(t#AEW<^>3XLqjCcb zRXhDF=d7bqq+ZQS;D)7)+oOIc8*3F$Ta{|RF?#Y^YA`CF4FJ>!bxb+E79QjKV(py z%|z&GKI9|kC*q}NJhv1!$_DjKpdQ@Rmna;r(_{QEDTi1n`o>LuB~ANz7B=FbYx2Kq zK8<;5^I)2k^^SZx;ylxX<@#|xaK-f;maai%1rA?WoPEsdJbdb@8JNLstWX*HI7=D zGmnB^?tEuCl{Wi+?_HUVlX@{!6f5VF2|h4MeViH5(Z8nZ$n}y zyL+$Hg#(F+Wk*N@M+@Qd5vK+}5;h%x0{E3E^+y65^Ds^@39=^!p~JzJgukvqnnw2GjRMTkd?sG-9|H*&Y+0>jsAmePiBE{D5cnq8H~vRlG^ zh<21*7&-{f`qPXFs-12&g7Rxl0bXOce4+r=vJCKLL9Ge#<8FW~VIxR#{I<4w|2p@8ja>K$bFdQU0-k3P$LpY4Kt7V$p!|xY6%oaPdIY?K&wX~g8R8w1 zaI(j3S!3`F4%b8mwPQC?D9o`q4pkuT?Jad>!QQVV`tBi$_I%wmou3B63JGXE@CU(A z6Q=b)vLo>4Q1%KV=;tK*NR3=JqwpR1nG!%`Pp*ettOB!3cPrdcMM?H9|D?+!;hlCbB1A zW0~)8+z8|BfCg#QD)Z~B_=*EB&gV7%%L>+57xGW_=m44^N6H3@JO{m?s_Mf)?K!>_ z&xgD}vux`w^IGb{Q|l6PBrn5=)yp8&fcOQ9ZfB2m{t=s7vpSs1A}@#iC|$q>fYw;) z{=+!hvaP26WOjI5Sz#(XpVPxFM-}BTBrZX^ts%iJP+knn?<=t$a7b3xIiOS$0@iof z!B6?mCcqF*JkL;snOs=oUl zpR~_m=^=f#cKwnYC--d~KwWklNIYW_k@!aaeXqAFu>qdX!p-e%yb%oQ`j#y|rPaNu zTczQLJ|C9=6;!l-t#L&ujxI9 z@ym+PHVp8i&Kz_65duwL+Ojzd{ zg0&|(vf$&L$V{75-k*7%suhf{C}lgCFkiGL#ne8=eoG0iJ~L8z1SDWKXZ`Ayx%Q)X zeogLKb&!wxG6T<{*=icYRZ&c)Om*Ph_dHV=MC`W zQ^|I)`pdlOJYL4>oKyqM-U%9`qNapa@!OHku!GzXX}ZHP-hX-U! ziGM1_5Tv>~>)K*NEypj-d{yZ>iv_8InAx)uQ@y(ykVO3fmLj=yKm9Ny%}=uSvgAE| z8_-d*unzWJb&6WWfi_$CEH&nCUQ+o}uf`_7n19<;=>qzu9vR#577V$M6Mc(y`dr;_ zCy^}xDzm%sRU+2r9!{E!n+jEeBI=;Q+xW6Ul2F_BF!pG{IE13R(UWjFW(a;H;BgLE z=5!x9{mdz`Snw5Xv!9+j4sxGe5W_n^B@>Oojs>ePlJFJ(gv%HeJ9IR9>6_%kuW7%w zBlI$U;&f9GO}7>)CvuT^#A9RAr1w?Lme^xR&WAKd{f^*cRLn}P4hd;&}~Np#n@ zhm(#9+HSYp={&-cPT~CmrdY!w3P0{ib&Ll|es~UjLlngb$S8{{< z9G^IuCwdcGHl5qN$X#(z@%u!#ZdVPMyG=b<`?;6a>A~n4UDpPag9o<@-M*c6Zk5ZVKZ^Dw)byph@e+@dAgT>5eDEtt+av$vH^f6<)QSUbVo+J+( z7z#3lB0Md}Q}(Y0yH0&m?$WT>&#+gp8@wXc=JyQL{=bVbJGtwUko?p7H0jJjg$gD= z*nCC-RC6V~7x^5@WoY-#NG9$=frs7Nn4JfKkqF$x*YweScX-MUH0H*W!~3hpTx9mZ z!)YUk8aMBPneBmjVDU}ioY4D4XatSP(H2HLH$EhBU}F~kjm$}1e{n!Ahnl6c!JT8( zEhywu1&pYRP+uHt5g*qAxW@+i27b^8xehWI3fGYQTL~&1GMYeLg1s~* zUuy7N?~oV#g}=HSLEWBmnKgr#fZwgVD7C6;?uk#U*fk(Ip6`fk4G$_?nKu#35Te-= zF@aCE1h+55fJHv=P&Ko4|D&2#lVh`EoA3iMNx<8IL{?q@aOV*cr0! z?221x%_`_%cXOc+Sx4q?rznIA&+#AmPk3(RsCrMjIlO+kKb2$!2SBXf7-ai7Um&E| z)0?G|behfYb5P}n**?+aDN#{}adUcaAMb$#lBt7Y|9s}aiR^W8T<5|6N6wQSwkl(Z ze`(l_j}Yv1oZh~B9Mi}P@Vu+rljoX@T7*@!=31s&-p)Sp3|nA?|p0&{!~vU@4M9V6m1HR9qUP?l){{2PkD@Y&d^KhTAQZIviBlq?dyXsU~fTWr{{anp_f}6R~J$!ItH}?nY zr~-n6J?rucmNb<9;n>;r`S-}yeoNky@sZIiiw$sfp2+xNJFa+$gj6~7W<^J#FxVS- zJ=d4A@AWHq?qAUY9Dh=Mdig=*Ne|_S=9L!vKD8QGB!e8kz&sMs!0^mQul~`yzq^m) zB{P}L;l%@i*S_yuWkdYAFYjOH9scqBsq^lm<(G;TnM@1Ca@oO=Fn(A8?+ie8xtMAy|k4%8h*g&-s??ONtSL&M#vXuSxYC_ zn0YG>l|um*L~%$To-8Bq1rMB*F&e~LhwN$W;`YmrEEso6AL>ThmqcI=&c4qR?eA$6NqBPe= z;Ea($Zy`VpIfIO+7E*x?Qw&GSVmed#{TmT$@5?#%_G1B+H%==(7%|u`pzV>dZ_l>t ztcA9Sm6U|%pDGLUhdeJ#F#^ESND#)d;ieF+M{mTV-`4G zLnv$j6&&hzGeSRE|H2Dvs(zS8`hDR^S8`D2i15clkA;|fTJ zSu6Tglmzvzge3H_jbfo%Q_NGfNVH=aO;(250LR^#qL9N7c}Lk*ZK0c?hxKkf)OT>I zrw*_&MADL0d7sh%-TschT9HyA@Y%5NN_8%eYf>XqHwqUTx!G}8x9$5?Y?DE)PBKg#4~p0Nar}ysT-LBU zd)Gbf$J5+FY=Yy&)=sQm``LDCV|duzqcQ|UHc&~+LWyJ~+nVd!u_$vE@j9}$y$=Z` zK~x+<8RyQvFoIdpM|W@!rvStJP0G_d;TEQ)A3o1ja~K5=pXZ zfQ!`rD99~?G6bgL2e^JiPpO@d=Hq&)e&l;{qt;JDxnkB6Qcj!G*G$A0F=&#z)8^ku9^5?5WO`a!gJ7A5d=~BO~fc*?f-^%UYR)g)n2$mW5 zI5Pqq-#3k&Gxz7A=uo}gK_R|3F{Ki3W@Ufw;=Li4H6-Jqim|#HZ#ZBq zhHp4nSZtHy^vVx>8lhV#fm@E7*D!*o0Pcw0HigBvHG&31(IR`qM zR(1Uh2Jz1u>t!8|f%X;&wvR06T#nH;TRixI$F%qiaMFug;JbCS4pkx^XflR^q3s&$ zEdqQ)4cM>(hV=>Q+VqjyKGWyxcV)(f$=oe*YBOrhK8_2ZPa8Z-r-Z}V+=m|P6fPXv z@Umz=;FNz>#RyJF&w|`y60;aJFM*OZJ%y97WJjv7faIt0f^mLJjXUmjmabK~D<-Y8 zs>hlhnuA6zV`DlUl*aon^s5TBc*@tm2!K%Cwyf|~l&5oQ#2BY2FZss0v9U9p1r)H!8HKUD}JSZ-C06;teQ~)w;)co~Ag z;kRo#0L3Url5$1aa|5|oau)V|S`+}W5=f<>1 za>4j%%O=oR{>sRP$=8#OO)=ozr57OP@HG2+v7P+)98c#b6}~AidPO<+e%cdDX$z#p zCrU<>PygQT>Gm4=35g z2(UFwMHULHkiM>4WaL*v|GXzDOngwr!=9`37Mdqsm(Q`WuK190MQMB>EU>1QJfYU; zzh>SrqMW;0@BTUfoQY^;Z+9WX>u%gyTT#V2hNK!$Ud zkw{{SR^UC&`-sMXuYpADU;3dq7gd4<;w1~|{VwHH#7_N-5fdQskV@Fd-Sx479onzf zOcD3Y_#-mx?7+9N`W+)juM64U&a)ZKX)uq!2c?VKI1`rPge5z2eG2jPQqo6GhN%%C zDj91Z0C9vJPAE{1RKyys@(0|$DVeE}jOgcVBW<6U?FI)jWftm+>?Jn;n)*>SD!Mz( zh@d=hKXL2$uUqGW37cNW{-a&|odl7A5oCJ#T-eU)nx=m{p6~kVve}m0#rN-yA-x{+ z%-^Ule`@|txr}A>TR=m>QDxUk{yvjNyGFm_C+;d^>~fV(@_8(ynwPqUIo&UxwtOV8 zFRmEl_UABfgLtixiF8V1S{ROb7y}n?GcOXDHhd%z+1eYoYR2SHM5r^ z`tv{68=>MM$oKeo8?!S8wXsD@`h1nYHMlAAr>3s|DtTOAvYb*VoKm=yQvNi>*U9Be z{f=#6Ko+}PgPpuflkd|UPhoI4abk%-sPu@?c=&$0?enjMs3+r$rM;D zly-Wp8#@(AsV=oF%dj?&d|oH+ z-m@GwLJ6L{te!#%2E6s?D-<>?g{6Aopc;*&kvQ5(JaNN5Ydynhx1=A%f5r~CwzE$l zYKrPZ*1hcwNGmNb%3J(yv{d*f`Yn}Q7rxzx90eaPAo%YY0XfzF;5nzIq+7O~C;fDfSgviFzyv{KT)oxF=egxshwoK;HlY7e`cR}7l5N|C zpeaphMK#yCZkWDiGtCput9{nwd9z<%VNQcFXvk~$BH7qR=mMV<-w#t66rUlY8E$tA z(3}Tk;c6ZUNfx2bIRcLhU2VLCCG?SLf^SSOb|R?J9`{shxBub{HablO)CmEtTl0@- z7F``55e!ocz_k{BS0EM=fL~-my<3mv;uVAH{#ZQ8Yw*GaGx5A1whu8oyq!{WAsHt7 zW8DljVr0Mbiqv_5$mGd70DY@6$@Xp6wecsK;}f$8fq}`^UgA;~QJZgoN%PUT?@@q| zxTJd4*8aAy>k$7HIt*Zl!>}$^xGzm^cMlG%P_zxZjyGp+Lwd_uk1D_}_Oksn-Vm#8 z`TL0`EqAjFyHYws{&k1QKWJ&V4cAXR}fQ+5xH6R;2X4(KTR%N@MgUcPSCNtrr6M=zD zC1Jp@*LkH5Q0^}?ITd7{d?|;d?MxOor2d{koGAYDrog=#= zNfK@J7#Dkm823EtP+n#@6Chrg_xX=Xgi(LIjrruz%j)n6S)Xp?CVaz;Rg2I834mq7 zUL&HQ!V?60Ym{BzWoRoRBKZ5R6yXph$6%RVhx)IaK^y?}T=QOcmDLuB+2c$_gk-76 z1f1{pTeXbXhxVQEZwbkFRTs}rXM~a`1#`T)Z64Pbk9sL=q zm4^8_w1mu&T?yLWx7_@AtU!+tLvR#?phN(+}!x7&0JNwlTm7m;|D z+h<{N0-l6kN7Fvr-1Hf`SAj1NDqn-xI37&|yJ{-Y3PPi&2kwBa#IXdNWCX z?eElo9$sj_eqJ<)jV1;O$U+=VNT`L@&ZVN+EFMB+hfWo-#M}1y$MNeAvlw{TGLPi| z8XQ!}NYs`l8$|Zvq1Z+^ycWf$ydt8{ZRlhII2&|csw$cjtCXeH0y>&7$Zx5*S8(9W zpDu2yE_A}!Der?giM?R3LN$ke#mV}IBRxMilvVN?DQ9lIQoLVrJDf!3BlXg ibi*qn-(#@@{Y;qu!}KMWkxm={zAj$)&$!wUL-;>oHH~=y literal 247101 zcmb^4Ys|j+b{F(1^-wC*dWfKvbR3L8;MsGX4`VlQ9ZMT_*^~k;V&FcuWA~mJW@fj$ z4W><05G4>WM0`Pz7>yO>of6WRLIfmYh@ymGOfbIjg$a!!ZzLW*>wcd9Zee~G+|Tn| zo3i&D{?~P1ztdXZ^mw|@S2{F?c@-+Jro{N>lZ^$q`! z|M{&S|HoH<)4o4VUmVUK9xrcy_qUI4|I)ngyI=O!Tfg)V{Gs3fQ{VYtees|C-9Pm$ z%b)vWzv&zP*Z<|CU-Ij}_@%${XMXQ@{7e7V+u!@gr$6|geaG~Nj{o=vf8xjf?{EHn zAN-=<`al1xKj)Wx`=1&9kDvQHzy8nt#J@KE>p$-w{I~!5|Mf@ydjHSq#KL4%1xBt^W@8jS0 zb-(r({mVc2eLwv@fAK%LeBWRG8~?)a&;R~^_}}||f2sUa-}<|M&v*V4 zf91db^}paR{PfrV_WWDF)c&GB^_%|l|Mb87TR-;ye*Pc(tAF$I!5{thZ}`=};`6`Y z`-ZRnvCsY3Z~TS-;=l91AOGu*{^@V}>BEnH&-o|YkN)J>{Ud*J`7eIUzwv+mQ{VPY zKlaw2{`0@?|M{E0;aB|yGx>8r`ZeG8$y;xI)1Qt1|8;-pmw(f@y!G`z`a9nLp6~ti zd%y5&-#dNy<w8W=`h%zb{LZiXm2ZFBSKsr3FJC@Ay#3WL zf9Zoy-uvq3-?_c_J%1hl_}zEjzWtF;fAI6~JTB<>e&LI6Pal4KdAt5x^KiL>yd7^m96$WZr=Nevmp%^R%O8F1wI6))G)*S#-LD=#dN==E7DKxI!Sm^Bf9|8N zeEg-`kDpKPUcPks^5uh1Kk;Ytul?DNe&EATKm6neKHPZq*Z%e2@dKvz3t#x!ZzR9v z%U}N5Pki#}@lbXk7LXa3?hqxt01@4o!ZeBSZ6 zKmN6cKYjnY@ofB?zhh9BufD(WN8{go?T>!+-t_V1@acyif6s>>e(BBL`Qn>de0!SS z{-wY9{SVF`{_rQiEJpD3$1n5Y)63`IiEZ?dwGT!1J$d$CR=-y@zdS47%d*Em`h^eA z?_Yo9Yk#zlKU(^u{d-yeUXlHB|NXT;Y7d_epC10cfB1ZQ@A~1#Up`oY_rHAj{>!@` zeenJ7y#2LxeQ)~kOCNshhkaf7v;X;buyh}Pczyp%{>9&I@or!Hg)hAK9Upx1>EVOZ zNClp)by-roLRSP5C%l&(9y8z7kt9 zo_{=@=MPWb@iSl7wN=@k^8BI8y5qy8sM?3)p{X9KLwRbi*{RKo^P8{vbsxXaKpejG zv)>wDb&jt(cX?Mfmxoi|W)Ih_>K?kbygW2{-(IhIpI@@mJ8#FHc+YEm(jiXa=ikYn z{^9q=-u%*69{*wAWd8GR9{>IH@4xTw^Y4G=U;oAr-nSIRJ8#FXdYt{=z`&X1rmoBS z>7Re`{jXlW^u6=@X6KXJhZJ@5v;Woi{aycx4f>f$douxprTN58{p86)jfd}l`l~O! z|0}bs$cwx$%dTyQVra{Cn3t@n#zdD1wk-YFP4zIf zc|OjmCzhe9iaH;1;~B;xYqPOf%YGc@uI!4$6GdKT<&-%b5>%!~WM zA)ia%xh8&N=*qgvhM3W&>5IDVy`WyEX&Kvm>Jv|lPNSDwjB2fxX~~wd&7F{bs;hcf zQcq0zu=L*CEagqMBHS!a?fud93oR8S~Wa`l6~UvbmTG=Y3lH*^bJqzRcQkDN?(A zUB)n0^*rU(z}iiHQPwQw)D%tGB}UE%EOW_b)&8_EtIMpZ=PGa4`0*uiKJv_N&zi=$ z7}h467`t|==ej7nd2y~jyGwFbdY;yzs@lG8hmu7s%VntAa%l6cO?_29jmuK-a&y%$ zE&EuH*}6KA{z;!F>{VVg?NZnN^i)rC*M?KDSH{*4GQX*D6x}klQ&x@B%7}Jeo7Z#J zteuh7bz(j8g6%2FWv$1$XAg^cwxzn!?{u>RlNfnf5AD#-w(exmc6zp~%`jJtqT?~9 zo*2WŜIHp{xcoVtR=YO7*x6KkF~ZPqMVHLqPPW;c}C(9{dd()LBuC7+m`g{B{d z+U}eBZXVjcE2nYrQR|wpKDn89y328%ymk}^*neZ)7VEgo!&)WA(JonU1hr$Hb=lH3 zZ8x-IGfq`jjB~;=05Ya&Eb@xeTg#=H=dzl+FqHnunp*Q=sH(;u3M=Z<7?Z-OD*I*Z zr=d!%xnWrD!7B9D(9y2uJe+$w4CU0MMm{wQFPSmz&14ODwyt9!Mw_Yx1rj6YWO|On zUXR7-YI~y|pbp+@b$k;~%#25wEp^*0&abmqEig*Oz}0qS!ouYXCz~x<-VBXZtg;H` z$(Xl3Zydw~F3FcZYv!(AvvsTrC#xF9m4~Q+o7x6Td==MPkA}W3EFEm+<$P{e)J3~m z_|HBO!2y?4)g6bM6~mC1RW^o4%kqkyNIlUGeN#>ISkL)1tjwdS@S*m9S7wQE6xn+7 z1y$@l{=(mM&J(~e0x~K8Q8+w9KUDR~3@}4eHI^kSId)@THFaXmnal=Yt8ApE9Eye^ z;{8Ua5!T4Z)K`^jy_VTpini-^1+n}{1_~H{Ue>9(syOE+FA8S2TNYr8lPh|-r!P3| z#BMJbggB&g)77I9d88?(ys-7FYE2=4qFKwC37In65Y(vaoFnPme5g7gC^hodp|A>$ z1-l9J)VWzKd!w0?1|+G#9`qFHZYg1KAR>J;m-B?HaGx@}7uq$uba5AyE#;MM#GWS;&B;h}bsj@Amu`!x; zEu43lV+7*@W;I3XH>SD;k1MY%I#XKKEN>aEWSm-4kvLIPJNSr#yPwR0)9n{)_}h2R zQ_2q(bDgsdeYuWPUspC&-`g@UT*JwyuxK$iTtPjQVAb66eVJ{T4SBz|D>Itf?aQ!E zYnAsSZ?Vo6yB2q_b}VHxr{a%d1?U;JVqp%ZnL!zP+oM}el&~m)$e=tn#QyHvVshxY z!dVbv(&j$aGci}IEj#8WOH{CeB;cPzSOF!I|4W?@BSO z_O3W!nJN~l%Q7Apo&zu99P$rC$|jasme-=1B_Fx+9wN`@zH9-jry>4{t#qhQ!`?SZqZ&c1)Zo0SAJh zvhbio$9LqbC)!d}i9<+^qi!=6huP@*W`SXPf6my7vMRaA1TGP06uCes!Yx6^P?n76 zGz+?OuWA#vqzt1oc_I*BZ);TZn2!MB!dmj&iCxl(Az+O)Hsp6Pt9&WM4-mm04{@F z-qa292K;Y8)GoCXxz*BQG|W!LLuH~Bn|7E*MXew-F><(;Ijq+???qE=L@jQ~goX@i zk&1-NvA_U0B0}bM7IqfBaH17S7obL-+Ph9qF8)~svK>3ObXAUMT47B*MV`P6O!(T2 zg#bkOm6e%I!=aU+5U@0+VgvMr{Wn%EqXq^zF4Z_Nc{LIcPnjC| zugH?62CIkU6CCR>iYOpO&|#i^jZ-clk}67BI1nSR$1wuhhSgcmY$U_NGb}0V z<9%~(TjLZ^6SFg%+w~y1xXPOndw1feo<<;XHq4&ayQyQ(p(&dD%RP}$NqOil?Q&)t~g8~EE4b64?wunr_L48bFdZN*d8 z-^mYu_}LXi1~PxX1tl2l0_;=HVAe4DMgvJaQPrLz;IQ|n+JHqvNSlBxh;hkW`RrW#Z!rK zwAi$WOkt^N%yT%X8*Rgu7s`3X#1poiP{@XgPzd(6J=or*1G*ru1imse1xBICZPgYF zFz`U^qP>{qaDU>QqoHb`ldUPCYsVJ4uZO&aEaAT?Vd1JV*06?*E8LyKoeKgR`?*|k zqiyP|CcZY{4=)q>4>_GkRR|DT3z?_(m7unR+MH8!1;no`L%!q{+hw9ty99JzbHi*F zTN3wGHWO?N5cQU|OwF#n!3>&QzmOH^!D%vGPE={_yVROHde$o(A2MyiRYRlWRQUTw z44K-AnEYB4Z1B0o1ILk3KP17`1W0kaVL{g`gQ2bSZ&~ z@#S^fge?ILTI3DmRAXgqrWUK6g$ofz;%+~?+iPf5sM6G9Ava?~wrtxV+2G6$$&rhh zE!9*F98!+DzX^F2H&4}@Ie-lPqfy3`Z)G+2ZI7B$Ob z01vEZM0zYq%Aeb|5F{&t29X_G)}oZ8KoIgzMW3>Xb+tyW0QW3?+f?8H4sR76R})e$ zOL(;!Ik<2$nD%liaSMzUCxCE{XthuHC=bjF`p{Vw6I#t_nGy3yQAALgC$LZa1Qd=g zS&JBub;M(Ue$WPloSLgzu0%csxorWPb?7F>cPP2X6`h<~bNG2#*O5zxf>=}u1x+0E@I_lM{s`~^QoB~*9k%1I9YSz3<`lxu@ShT<_(4sb4uoH`%2oA`gg4)SfjM7&k9cm?u< zQBBR2fpZfk$({#s`xTVKXbHR~S)tS};lS{NSOqa{la+$nhCdL;;Bu-l5f5V78J)pW z%XKpIc4W>&qs|c`D5Jz&p&U!}G)O71^}i#ky=tUfN|~BE;hmcd0&${*Ljzzv6%F>P zo29;P$TBewbY>@sRRZdz+@lR5{>a+}!3vtC&WEL|rPFzf%AuD zvW7#XOM%lYHM>Y@*@7AK0lnBEwi74C2^NuJNwLnJ;Ql1!ku1_>Sq_s12QIUQ38@l$ zw<(>CNEM7_+pfKR%*jd5hkih8pL)Xno^yZ_JQQt-TSh5+21PO*1(=%Mig8)wmcc*5Nu>~w? za^!iDg{g4>M#jBFXp6bAqp~ITqGX}>R0-P-B05q?OQwdMn0S<1!WB&6PK#We`bxy$EaVKx#g2qZu(YcHWPI_op&7V;p6IPd2aPDCc) zS|w_2MSn4&nuH!TIAk~S)`{P+F~mX>JM9Uu32hQ|J-S=gJ(ar)mBUK&113x&zzoEd z{u^XJI6FHf^&2L#9Yw+9C`>}Q*%dm0`uT}e9;boWB%U#p0~r-A2caxH!rbOEO;u_q z;(HLrY>hk#L5BP;rU-E^Xozk~*b zQxAR#qGKOtd7yfM01|t*Ef5Q}GaV~{!;+1Z{*jKcQ@FTDAIxj_MxFa?mqTf)^2NMM81Z?tRy zD*})TUL;+uXE41~gxG}CT$x)uhv5oLA!76*!%`rPxM!Ou^&2P^B)XM z%YCRLpNLpm)ScG{(vVE`R%E`IcDbGoj zVMEEA3JRG}`)}p_Soj{ujVPcgqFv}vN;RVh@(HGd!9vN@Z(w8k&C6g6E1V=y$l?Oh|p%{i6(>~sWq2qroW(-5IQ+W6lYXXLN8?X+{_7&BT})0 z#-LO9j)Kg>(c=Iyqg~`96XO6yBBmt;5P3DLs9y5CL(Vbz4sq&|I#)})1Wv_VCo5hz$C#1kp-rvUnNgKvN#}-m`Ob`2>w8&+_%vq ziUx&I&g_c3T~hfdrjtI`|?GvL4IlO0XW zRZuCFBnE3p&Ph~7-A&#NML;zv&lw(5curJeks)G)StO_#3$PUNp7@QM{}RsPxFB_G zWG{`(UWnC^E~%Z^4V2N5(!|G9SvE!n47?zV5Z$F1;VxKBg5MVLjcdV#LW)6%Fk4Z0 zk@_mbL?bh%1R}DvGwnR3Fn}e%Piee_a8lsmpcvdR*COuP*nk-DB*oTJ(*QnF2M8H| zl?!zU&m#P$`T~X%UnPf(dc#*{3IXJTK~Q*unn=Kav`#(&gRwSfuW(Ep4m}4E0DwbH zp0c7Ov4tdrln04WsIF}QSRCye$SJi;kR-G#1YodeKzFM)$vwzM=OXmfsWl1!K#2^^ z5hW@mL@)sTgf=QMqls}ag2v%I!47t>vLvh@iGo-P%1l{Nf(ltTu2^k~SXI)G@q`k6 zAH`PcM6vq8UpTzjbBwS+fx{F^bWCGPS)+;t*NUsMT}P-DfGK73$d=3;O-N!KaGG?9 z{15c5_COK<;xEir&WNR$RDp8XH=AjyJ36%<$`lRSC~;p$xDEd^T}#SG$u1YgyU5C_}5@4MJ=%vc@v5^d2Te&XJ=QKAABMfG-S(m z7nyqG+~fa<4ibA;J~w2KabC><*sA^kkT=&h93mw38y(S{k}kPw!RpYL;ZGuQsp2B3 z@j_pyHgj^jjB3E2lK$r%D5kO0MM|Yd8WlF3z?T9*EFyh?Fo3Uf+H;za#8(CAjxFUZ zhP)r68ZJZt*MV6nZ-D;?_ey0^5Wjf9PbgB6CJtp8UMBGyR3(w|AeF?G%g1=YNhE|J zBXlfNPawq#p$6>&X+lW$R!|l|k=2^21bdD+w0^i$8tI^ASVr5)?1lUTj!pcA)RwOi zm<2CQu(?fq#KGj+{0a z&9F-{u)(SpRA3~Z@Ht2VG#soW62TASgvARqMle}ofoHRnH(;B5jj*eZ z9Av~d%EIW-``}PgIWNVTNDI{;jsyc0QHPp=qGIKdw|QdC2L=(F7IlY>Iu_->b~*Mn z(8HAc4ZsZ`$slj|D_9i*JW+(4FLkxL$j^>kZRjlqmN~X;7+N_BC6RYsj6cg!OSaa90cnr^~U?PmY6w8A9<(?}~?1x)!PtCDznA zW;rnq!8{L3F&FiEAOKQ{*aV0YsM7b#!Rm*^dKPdp*RiUpO0 z5vr2nNJ*0U(a)-pO0N}ErWXnE+PY?}b0*L4^s+0gJoG8V> zJo^*kUzSJxSZJ2$uvW;6OH6&0^DHgFK4`^aqJbuW4pRf`E2K+2!7)kwvvDxq;Gig@ zly0Vn8=R4p!qnRl?I;Ow5~UXMMp8TJzR)_U=1KjAOfDLf%>rBmE{alupHRw$6iz8? zBvx1upio@y$U(6}NxceK2BAXAJ3DQ{Sk+)54uyC`o}=v|ixO(WA$87?@^%C~CBo+1 zO#v?+T&euIx`V_x;;>>Z;!jv_PF(ebYDzr#sDdfAuNd@^_a=7;Ih%t%P)guo6+q$a zQ|p1LoTUBnDiS|rTSJCFCvd}BE6Yh)QDt42->6CGghKdsF>tWG)`$)%fyg2P(1wC( zWSm895|UBvOeCWYM4*=#Ira>t+eX!EA=islBC8cX%VcrD$tS>(&@#q}0ANGsM>~S) zCAMLNQok`Uhd6eKgg2&m;@`PjA{aquD%&TTApoDS!NCNJqC#VcKY_Agr4(9n)G&#` z_ORp~nYzMoNi0~4*;2}q_^OFE!uwQi*-TMdE*RDzV3A`Iho1u?y6$I$V}3f&;mTd2VyD!ORh&f zTH>ql>lTDSQiRF3J6)s=yt-pRbCNny`ZplM^fZWi`Y1g|humy5g^I=e>~FC7U^w_g zsYY*z_fs{9-B3lUGBm~9S>r)%Qxq3MtjrF67t*KwbpGXWQ@;UrN%zV5@do?|Ndreh z4nQ-VWLP3RRgn`bVj+0Ls8ob1Wy7&+seBaU5tTk6S29VIy9hOAi)_$Dsg6i|6|jmK zq{tMlL~445Q^28)-EQT)6HnlG7(*On$Yp4ERpiQa(#r6rl5vSANX8qr%~8$=wn;)W zia}Q2gj*>kAiGCTM29O`He{X5^DV_j0fUJ4!KvSXh1qB9zkGyX!otxuknT*JT1t^( zSInyhc_Y%KV!j^5 z*{Q@_SzepGl~@c0QFX12EUDdO^#9zz3ZiKVzaXI#nIHBKB1VkR zvdF^tC9Ps2iNnRgHmQ*-Qi`e%%Cp7Wr?q%iB*6+Lw+|L4$nzb zL;(#f#91evU~6x?oyZi>11HanVucrAG(*2b1IOGdOpqXR#6YaXR}pZkV+h&jC{0ZRAyyL*vyrl7a{`4&>l8|O z280e1)C)l|QHw+35YUvTek0UxAS?SL;*hS7l6ygp_=Cwm8*zju%0j( z0VP0?(jnu=SVJX`im|jS2^qb(JZLE-2uXV4t7^VW6|M#{vV53yVv~>w$KM84OFSX+ zP@}-Y>6szVAgr;4RS1By+9)M_)FhWH_sf$Lz^jl}u}nJ%W8wQW3rX1BFbVLhFeS>B zi4=p%ql7Wfl$@#f9HhSGMo6cmYH(rY966`ZL~|5)^$DL`y_wz=jYNc@)hMZ2W{}h$c58NHnCdS>*w1+Wng>)qT2bK#2T!^zJ)`MH;tAj@+6R~$Tp&e? zlYv8l#@HW-CG`Zgu81ixOsGZ}9x)mS5wdC$$HWs9GW>S5&D2yUsw@QRkt-SbIBA5` z6RO6dt0RaIZEAo%QhH(tb{#B9SO&%?Lx|b1A_(Fl4N?fgQH&9Zp(GQ(5ryL_O9&MB ze`uU=iM)#RS=9}!m3jiJ?wCjM70vF79*U5a5LovmE-UebAuw?Pu|z7c4;7pFlCWulR4#HQWN~>+c#YmuM4~39V*IvmJF*NDpfu zg3>)eJXA46s&a;vG9@*;tZ3xgrPrfN6Wr|x<0{w&68cmRA^ST50THJRuZYNY4G{2HJ6HC&9Vu|8qL}S9jkvocNEF)rD{|Z|yUcsWu$#K=mC!*rPu=#T`I_x>H z$EZt>Dy2weE%l%Zfdx!&!eA?G9NaDj!S|}IPV5q#prpRYC>r&FG%{on*oxGpWRnOW zdV?T@Wd<=C<)RG9{K`t9y@YG2dd}#1L_!~JTYxK6r|f%lU%;8s>m_HG&Zm_~ahAs)Fniwas6C)IlMo!E*v4~ld!4qu~Srj%_s>_Wh zkQY>nAsC?aq87mxaZge8Yz-6Rh{h%Y6kl#Jbvg?nsy!0bpH6)OEqNssBvJ^mz>CvE z;pu68xDP}?N*l$a3Vj95(OiqDk6l!z(c?)$E!p10S5ae-xQ9pA0YJQrLr7Sgp0cU|djLx5--y|88GQ2fM3Zg$T4$O)!Vj5@KeG)R@rC?jR zhSCm!MglV+3w(j#LrfFZQRE^-uO#SdO`OZrnopuG$t&VK2143Tr!h%C;|l6Jg|DPI z{D%|~BSwy64>D_@2_Pt4owPmy(veX5Wk1qCe0Vg5qznwzMQY8NacdiCbN!HPeGV5b zhzzGnk(No!)vQ2t9OSFLuZZHygiQiz`L+-+3 z$ku6pBEXkiqxxlymNv11!T}|EQWG}2&LyxzG#`+-lkbvRO|XN87u}7cNY64@POVA# zH2_=MR#chzDj}@8AfDB_2dm@-dM+S#I4d<@sVBfx7L7pKR*fd+K`PPPt-5XS=!qwS zD26|o4-^xhLHZz)4sk!ewo1)aG!qMgBsQuHx9D6%5RsrtLTEen8xFd3bLik`oUx1g zKXAS%!dt`e#1pjCECJz)7MVhO$per9a;H4=yQwvI#-m!vIg!o@Y%FLoPY4rdJ;gH! zRVW~&qP$#v4GS+@ji@I7K&z#qBBoGH)UBDf{p*PMpivJt3Uo4s6n_=|8+9D{4#y4! zC07V!a-q5jC1W*C0;&xm7>XTdP!UDz%n@a7nx>?BY}pu0r=$szIXX`2FQHlyeaD6w zd1?ir1=J%ePOydIL(l^lQ6v{Vgi=aCwVr4}7%Q#~pd=^F6ljC5SnKEzm{`H+|4RuC zNQbPFYZG#U{j3QMXsT-+;ziUPVjYcMlr3Ygkn{UphqaU0pQVEA?#!s;F z2)%H3ss299CZd3@LWgb%zGzVtz+HbBy-BEf662r^C4!3fRH0S2F46nMNsK@W@Jl@r zD<*slAq`tiU_yqYnUa)IWU3NRgcl?A#`#eMi_avDP=)p+4nT!p>Iq(qhBxZlJN&b`Q6`@Urqr%9wb4JkMpGuS^IE0HRo$r9I|t& zBxqy4kPvx$ZXOO*c6u6PRvjKr*Y0${qB)%M%f&jLuJK)$2UzE1(z;7_ zX}s`s|9lmvraWKshpav}@m)g@SoHVLS93b?ht~&6;KSn>583tf zZ~|Mp3m;x~_kUM&K3yef9}azfdzaA`hr`3g?z&v^b9Fi0zwbN!G+QU9J{hh|`^O&E!tGpbV1NMY-y!&0JOLs0$#lxY#WRL53 zI6Fg^?s7V0)s?wO0LqPhf6lM6CXQm$o#S0Ci%}mR`itf4vSV>5?w|WhbIf9|UeDQW zuhtKTEI&RVKu)J?(OZH9eY5dhHhI&sG|uB~?(MV}pT~;{(?aawgu{NEum0SWKJ#!e zSY+wxa_`)$E6Pr1X7F^5JMY4-9*;U87^LwPtGSz-If zI`SPyuGa@0nsayU&*%HcSR4`QVFvpfX7aA?3UZ2Uk;9$YOi{EO^HrS6=5WIk%?;vxo0E*H3uWZpa^wN3f(k7ia(WUb`B| zB=q4AQS$frU5Df0V)N(K>3F?#hg5@-jky<7*UkA5^L2|mUg6UU@aB33c3bW{_B|+Z zh40UGb?&+|k9R)YJx1-oPjGq6{S)?maST&5$M$e(?RND*PwV(n8!dy|Kuf#eucA9% zPgkIF2cK4e_vbU{rJU&1XQ)42hyn|DwjRUYht0{)wp)Lyi-VqXmsCl`#{N1erjm7g zs4mCAtFa2QOk#!8wQ9~6lCryTbeIW#t$Jv#^$iz_VcG-iD-`MjPBDsi?=O+4AWxKC zyooy?{`R5Ij=t-fUv%xc|6Rw-yms(!fP>t_;!qG23LymS;A5OdMugm!)$HO7MKJF;4RdtB-*q*yv+>yi4P_VEa0T`_W zs{3uuwQr7H3!A(ilM_gF^--{%)hx4hd<9U1&r+lHhG<0^{!KMtj&^=$OrW= zwf)sJE;zmlBd_h@P~43tz^?N#H`xch+GC8bi20PQwWk)(w8J{elb)|-iI8}G{78V- zwU>(NdVQ=w4ts5mC6xMln^Rh^YhQw@)H#p)>&3Bx#zB|rdSOUU*EjKFRrpLdOP|HN z-Su>L%oh!AFeIGC9`8Cer-qH*tUyLJgBcNQRGL2=9A6A@gQ~1U2(3@^5%E*Cgq{du2TDZ z_@VM3t~&|x_LzGy$3g0NmvUpruc>*?5*r(MRVpWs>krrd*%CQP_g$9xz6C}VQ2B?$MJ zFA9C;O9E|&xhJ@cTU1XER|KKNVdZ4j_|+b(a}X)XvfUkTV_q&tSbCrDQp86%y9XvG z_&HF4Nzh7rJlYAXDtwn=L1?m_^t$2uZ14}ErlF>Q<| zTS%vHhJ89rd3J5@&WlzOQ~5D+(yu?;@veh5Lwn>EszTbI=ppvRjbD95UKGhvo)nVq zGR9K+;9!3p%Kj;LB4ZT!4x8@MAXS>X^%J7(LPX{O?lAXW#76I9a}WpJ885d3pQF`_ zsJ7*|Rn9){)fejls`emR9Qf^#u>GgN;_lccxvs z`Nuarz)Lx+TFyN;Ibl~{{Vqv_LoQvo%e$Jh^1kpLZ*+Vw_8r7Qr)wCj9c(n&@6e)Vvcqqu31UX%NE9SIfZ@y2z$dL3(WDuUcy<_qUu9E3tU_}bdL6h7Q| zrdN*iorRr*MCKYldx|epVlGUkH=d)^y)E2J0B{M zUG^7t0w~|G{?KiaE+ z`7L%oR+JR5kAF%Nrc&}=exil~PZA{ieAiiqz1s%^9E1G|{pt?*i=fKIFZ=BK6FqxR zpR>nzo#NI%@#8Ma8x~dgX^$-HmWRvf-2B|))FS&@V4pFT0x?Di5M#Zb5_JN7;u2;={e^QUL&tApe z6UWm}=z9?Jb&k=N4^*wHl#dZA-S5-Rr=T7U@AX_7NuYPpukNw$)q}v&v_8^(G52(7 z*D(L;nyN!5C4ai#7G6TtAvLticS*co`qKmL>5bzP&8W1(uVSC{@}rAQlpb#E)yq-# zmqfJ87@iw8f%hHH;vjW#JszvW-8nGD_D8KNng`Orm@og;MM!hVlzo)f>gxOM^&Q@& zGJ#Z154+oWq!YasAXIT|$fXJj*1LJkCOe+05-x=0-sIB+ln>o0!-5o0+4*ihy2;M1 z9KQ4TdS1>Qe{NMd-thfv>*h2fP#{mZx5s?BpVPvtPkX&yPTLg)u?mKcr}jvul^@qa@g1+%uey`m&Xxn)LH`vUq|S;8C2?*xIHArf=b$N_D-|J)KmKamo683cy z%+q9@9@g&eJt3E-AY>POkCxNvDZXomF`|Z!p#U)oPtElnXB+icU-rqJD4KKQpZc^tzANgHL%IrMK4GR8_dvi-&;!V# z?ebk3{RSns2VT9jCqh1TC$(gsJ@E=HMteE*QA@bb7^AfeY0eHYa(mG!A)fy_?e8)3>U7$x@-+QzcA5KIww7kgUVZ&{9pkbLMd`1v-D+uv z^{#M+Ua$XByIdkwcN!xapz3zXi>e{N>Nt1vX|J7oiOibnbQeBazd@X{daE7wM3+T7 z8s$Da*upM5_wlw}zAG;;MSa{w1MczwNN%6Jy9~fZ3#45zIm)-B_`T@?UcJBM_^?*{ z>Y18o_0`7ZhiT}b!&uKoan-RA4{W9((- z2sw7>H>57KI~M0%YRWXOJkk2x?ANQuc4 z?Nj6FrYd)aL{ZcZXQ)5veJg(4V}F6sw+<=0d{=ZcWxrsP8|VHC-bBTbYTeKl?0_*9 zE8wFeMn1mj$M@3O=I)^To;ti;*71O|y6SJT%NVt6ZD`5%SjWSm)x1@_{rVay1+?w; z7`)sL>qso8R5kiED?q(*wJ+||+@WB!q`hIxSKEoBYuq$;-evBOCywZ`2g0kri}Eh! ze-|uw%8Sdv8QLT6I2DJR&V7%)dLqED^z7Or?l?hAeH4H1V53j^i1y}d2d-9~h<4=s z&<4{HKJ#^^YL;kcC*IT$zqB`er!157ai8zPepTAG z?lMM7pHr`Yp)>Sa4W+(giklyMS-e@tm&R7euXEhKz6UORde<&JQ(bWBx;!>|2fNy9 zspQ@()xIvkc*FoN&Of?#qRF3XDPKSLmrK_~AKlmc4&_5`+fWm`%ex2>NYi$CSCQ#J zb&!lJZZ#QEQ$}^K8Ku(o9GT@x>3cOb`k9_+j`o=^>i<(tiW%)DUdh#xGGE+!z6+M? z3##qV7w)l+Wk&gb5nH{UmlGnF?(5xWjB!OuPfva--{?eju@~lQ!Mgl6O} zxe2|=>$R}GUc6Un!##MpKAN0Tz3yUH+hZ1$Cl80>YklVBB;D0;?e*nVF?;`AAo>Bj z?k02!4RY?LQ+RQXqE`^Bp6~WuZ+eov_`CEO()8x=&|GMAwXbKb74gxg>kLK|TlS~B zb+!62w|VGu;EC7v#pn;mi=GO_Lw(UWm1gUr;t*4DRm^xOD=Llr?p{y*F+aGnh+Tbs zjNH?jP+CQ<>D8Qj?^=yhuC1~@;BV?qUWyAiRVAx|}l_BXu>=#NjE~MBc7l02{Q6>kr#~AJT z#>HQ|jFEWd+%htIeAh8WlM=YcHyj!IrfWh+~>P`X+Bqrs&#wni=h_vWwmbZFo-K>ckx5VLLHbE(z}dN z#OD<4v%gB6Z?!?*<-0_WE-cz-uew{bJ}3k@VwJ)_%6w2kM6+hMxtyA~)a71$e=Mb8 zZV~9~x!Pl;3sp_`xW^gNt1fFFYjt01S6zRji!L5ukMD9n{8d+_UEbBW9#C=SKJRjw zc{Fy|V~h=%!d1D?4tIh^(<-5mtA{QO_3dsk>K_!Tnp3s@jRqWd*OVQ#^{LCam~4l= zsub2I<$Le|gs^c}=|1~QyCIE_V(t~QKJq3n?Tdk3RN1VV%nn?~u|Mk8f=SrrU8mDo zVMg>3IXqxvRL~u?{8xh&6$2_Zv%BwrJ|5lbqFwMFm`N+KxU)MnBK1?P+lx75;iJ|n zBvvryjsLQ1Uy3o^t$8|UY>t%Et$$&tw?gLO9yFIVW%5vW-x24wJGU3RBwJL=10y$LV(cNg*zB zhsO1Oe)WXob05Vnd1O8B4wYsP@YmPo{-yW}0%-E++bPd?<3wMfO+RVxp%W#X^ZFdV z8rGbmsdi^0^5m!|S=`ZEKh&*WLv43*Rq5S*6;EaEMo_VPN5B1K3neYwQzqMUgmg4# zyBFu2O6>uZ#O%RGpIjWQUc$YUmEtD&3jzUM_nVhf0tBy*K6*bKL=1I zFo3^A8@Jf%#r)S<4!?`z<071={G%L?p5jNBa=S9Sea1af%olM1K(8B>OKMUV1hJPq`YK+jMTDnEx6ioU8DC-n6V&Xtt`jVdho$EMBx| z%#Y+Dddu8$bFZk8(#Nc9qVCs4moZ-vi@r3ry12jA1-3XC+J~5XTi)$|51OSiV|*z7(>v9nJAfyAY-u(6}Vm|sA86DiC+tWo@Qa?NKxlnQUmOI?A|?J zl*66!z&)@phjY(L?`7wgHd*%hxBcQ(G>Z)>R}!%EhP{|X&n zDAZzE>1hRnI>qGo_xHUPrOu@40^G6mWWQd_S@~t{I`D<1?0bBde%mksL(Qx7XSYin z?vCBBTCBNSWS1HPMLJcfOZG$Ey{SD_yL)-i!cIGNO|$rdd-p2Lb~Rme?o|ZcH@kW$ zXfd*j%f{+bYyP`Am}^ZuQRt|C4;gzA-xVyR7OIz~KkL91_k`R(#`@Z8 z3nxx16`=>c`sLq(b8-DmsS6e2Y`_Z|GU6uz#*8g!WF+Zb)Fo}^N#840PI z+Uojl-WPQb7_k!wrzdXcnDgL5Kp`y=P=C39uXgRB4+y;j*C9sJfR$!`2mE#Iw9nDy zHvHogo_H~Tf7KQhw=IUL-ptpF@dQD2jGLEB`%X<7SRb1;;C`zLeXP4&v*BOxuR&<9M=za60OdVNK&_~ z2es3#9X(!@e%^nEE@j;AaAm(-jwEWht-|raQ6L~KUGP=i&B0tR2m3r~br~$*1lz9HxV=Dc3}s)}FhhOAku zcGl*4^@{nk=kW2deC;ZCl9t(A+#VZUBwY++-4Elm6q{o!ilOaCRU7rZxN~wG`(~(y zH6O--5GSFUdi=GbSf@7Y>$x&QA6d5RFfXI1cPf1P)<+hd&sygxo9bAAp&#;j^qt+L z6>F976;Ho57eloyZL>5>;n%XR9;$v7VpsK&YWx29SlKjV*Uoi5XYrAaMlpF$+ppa? z&7131PWhk%{#;JkZMoa79LK5atEri)X4x8BIhFl9&+JkbW9ydOdXGzGyA|2?a+m99 zal6G3>d8t^4dv#+q3!9aweFFDF(repz@v64sKKRNht1`db6eMZo~Ak8 zpZDu3d#5&JD2j5JwtsC2^U)4jo89(g+2oUWepsx^FpOKfq^(!~X>yieo-Dwy+Eisb z&QsSc`Ly|1v-I^cteqc-k1Wf{Tb4G72HjwQLz53f>VBig&DG8AQZ&wLo8LaN zWPI8sj(e%e$fm(V+s*aN5ExpRAO+2%MH@ytg~iCV-hPcSp1XFeS6i!D zH<#OfFNZ8!hGvQ>aW~|!bPUWim1DbXZsno$V+&{2JdSN?9!;Hp3XyC-)@0eZ6w_GW zjCpMQmDdu$)Y|TCj%}DTek$V?eIn!ED$%w-x_nwHHw|q~$@sE^GiAltFZEiieH$LXkY`x;(bh9H{GO^1{c(}Q_Y`=D?igC=x*vfseH1^h7k8V5}m(3Hf z)Wb4B>zr}y$)WChfA5OZZYZX9b8O4YmV22P(tIjeqT4Yg?iuHCs+U@|+O`Se*fh3r zRJnX}(oNO4b_)&Sgw zHmdxsJNA_8=o&Z%ylI9-iTKuX=de~gm<3Dqj0MQEwao`2y z7Zhnk>koQo*7BTUl7rjP+_b6>v;3Rl{X}~C=05VmOb@V+3dT5dV=+143uwtO&(jtX z%8RL5VOTx3%Q)dfr+T@u#Xzs}?=ikITeDjKEQl}`VVJvWXx(_U@F>Hwb$Ig9zneAb ztLmHk7~!FDVALynu1`tJ9>?a>Z0K$-9hmsFa5;5N8=J37vpt*Dn#}<$tkrYI20@Y@ zKr%(QRAzFHvsZS7d8ESK++e^1qTaApj+zOB6y@2W}cQl^^4)Kw( z+RIpG)9n;+-}5|~{ISl=%!VIuodhJAXEFeCl%X^ z0gI;X#)5&^Jl)#p^_rf{16Cj`^D1iRh03`Aq1R1kCGst^rJPY7@z}UV@KQnfnz7jl zTOTgF2nqgwLmWws>3fVm0Uq_}M^VaFmCE=o+pSZ=Nx zLSf;j@^%*K1r)6^rUlDV+a|*nILzmloq$;4Y--!JzL6gzWF#!j{iM^diMw)vNjKNa zGoz|pX{~MowF@qFC8>IC@DFD-z(sM%H9yrHwAtF1MOLm3`MTQlH^b0kveWno82ODcjyPll{*RZ_{LisI19YwQJViPQb9#;trvIlY!csr@QTEaUv4P$qUyw z!A?m)XkmG80 z5It)q0Nl39L1T1dS43tdj%hi>@pF3ct5v_bZmo;rTe9I>cN!Hav_7pXdqq{M@Om5X zw7D-kmPhzduHsauXF%p6H=z^2VQMxPp|kl%2=sYDOW1?fFVy9;QVK=i2k_}JI~IIK z>$Ogfh`gI76SPGXu!>7oJD@zsZONsbrolFWWFzR^I&(u0g)p%dqZa@%Lsbe>fqUP- zNrXrK;Uzl-#NUVna9vPjOsR6af7qNRoCc zHcjl9OQs+*h1?ckvZa>Z8Ew0z9wC6OnH;N;g{^JlM>x77lZs|-GS^&Y;}&E?RH7~P z$Aj%}AmhI5STSkLD2~>%2`+TPk&Plj)QJ0l*3CR)A-z%;)u62BmYx(yFN4gG6Q@Dn zW_Ap6Kvo=cF&P5-?J7%`Ku z!gE5T0_li&Uob0=35*$YJFZOZ(oTO5XCK8&rC&12~RX0g^ZFTMskHH!yzh{QM9x5k&(_~O{IBnp)L5sp9|$=5jB|E92+FF3O02tdnEbM zqRYTNS0m!A-&_D#P;Ph%0}|(rN98CxlnZFXRBwG`QHg{+*IK&BdrW>sUHaVR7}9zB zF$b~{HV={!W^x>{1nY=+#~at(7F-kExZ{2?kqVBjTFqFsn__3e0*W7seAIP_E#VwvJM&-C3TE^;jF z`MJn%E??4jrZbV`glOu`ne0U0oE2kyf$#<0qE@+LxF*$K?%3KN* zk`bk?T-DYt>4*%#tX@=m+gy5x%wiGUSDPtsw{VHHYd>P?nb?~~NaPp(4pys%vR$?x zYx4%PBVe%V;X1MfPuhe)CgU5nK60=E7&36=Ic^b;iZieL(6ca?b3;|cd*t#g!9Ypa zI!gS)GFAB|z;Hyk3aMb(m7B+rFc%gGq(KNsU~6nM98~G@$SK~C`F*mfQgkSQdM>Yk z*VL53a6&y`aMJrsrIpT`p|3XiWdUVzJ9AT+KiNs|L5Q=;T;CRTTQ0hqM+DbD@dv1>O)ePKs<|YbI@ut}JYm;ie)GNUYd?atxk|dU5M9 zFk}X~Dmt25)KTE|4AZOF>uC!sBLX@AFxQFcmO~X}T1a3P_baa50)_%G(LHAc3*8og zyP477;lDako{f*JD8|sc*m80cd$O~~OAaZ8!U*`C0rk>e80w0 zsBM1a3^qzcxTNM86j|i^x<#zW1>VMn<%B3jtgJ51XY<$a`n_7fwY}+RMm|v%yyYeb zilZ&xt&#(EK?7Soi%6}I8z`&xncmc6!ox}u?^)=TWXDuuPb2cPxq1>d_I${tE{b(C z?uJ5%J25flKi2I|nIe)rU~32rZ`sO?r$WXFXQN z!%mLabcA2!O>7)G-&8-9o2bHLawN0S<&TOac1OrVMnktXZb||0H`A1@jlK0mMTm*p znj(hVpRz6AU5R_wnzHbDNttSj_*xQ`!tEA7_hM8tg8Tw%H}Z zw}N#z?>V4X$P0CvZz_*)9rCAgPPmD}rX6WrPcje)2qErD6N9?n`ZZf5@A{#pySSMk`u~XE<@6~{yRFSd@zUGIB#~4wF*eS7 zB645B*--s%{F+Uhp^yC1{1}L>QwXPJe`o_N;pSs&=A=mb6M9Cl61sgzzQIIg^|*y6 zYq~YkRiWV%F5!95I@kn!v#;O!wH!6jk`6krH-qO0Nh5_OJ}TKarzC%o!6Wn#Cy=ws zuii`$URs2Sy$B|6Q-+TU#r3UA@Uu5Od7tDN>ldht>i$^)v^*ah}hJm zYz8Y-DZ$RlF)lZ^C>#^Hfaw7psF=1k7wUkrE#lheaIfYfLR#IV@aoSv0krILvvp9( zv6V%!D6Do2d6x9+=1102HhlLpDO+-C{EhAWJe`U8deJVC(Rj%&;mwMu0}0M5ESK-? zp(3`D9HH7Y3hnK{5Z%|0qCsbI1J`xQim8dwxq6a~&)<^(BjzJktdALz_=M=ruix$BE@+R%*_xKmu1)i*_G3{` z;$$N7V!08MPWC7(8rZboGzx<_jTg*0nRqOit20{9) zeCV(1GLm?mB?pT(i)@G>Xc&Q>Cn`cZMb5lek`=;?Zi|cy?L?2Cd8RR^-op6QDluia z;^5%^_(-17ireBFwuBIjjGp?kdrZoYbyP~)&XLtbwQW8|=qJW!_#S1mF&{G)n<*Kj z3^yNBOpp;Yp!otk-6^VTG|DzGQ4N+X+#u}HHO zQSs7-MkJ!hDQ40lsu{3$dZj4U*nA9_nVWL7#9bIX`UEqHvu%}0sn^+Bx5j>?qclQW z>+Y&Tfm%EkLXGD=ws4Fn9Sav@yR(|4=@eWo41s|5X3P}|66Y$AHUHE}TVv}mXdyvu zh$;M>3f4>&o#I47W=hJ<$689ZF#ZTG|AP;(^+{kTykNu4ne3#(leakI4?0 z0=8PO7?-kgn|Y#fR)xuWg#|xCD$zj|x6k_O#wq}yPmIQ{j{aIpv`j_xB8cs9r^8QfIVunNw_tYvsAv=oKn#p0@i8)R%b3e^I$4oyBLj+Eq5y@%FG6-D ze%qSKC>29JV{OGY@mNNeLR^T9E2U^QAEWUGmQ@(g{>FKW;$P%D@6{u!xA;l%v;6;K zNlus`)QkigaYacuvMo)d`W5$qTamd4j}y*nkOD*j7Tc0i8W^Rjm{Y{?|IgXIBsr2K zNrOHSe!}}PJ3Q~s zjQ}h}!?;;_Z=%+e+Vr4-W|ZGL!8KoUVuGRC=z4FuF~Cu5g;$~isT>N1=+$D09-8y!`r(K&&K!Ui>KU;Larw3{IZdYI-E{2|4@1V~ z0vuc_aCA|)8Mn9=Alu`=O^948A3TV50`zDJi1ml{Tu9`{9JqYz0*It>jk(Fsd$4MEp{QSo9Vynh0u&qCpM(!!9Mc(tZ|t|_288PhCAx%Si6$-SbyB07pf-95V`=Uq*_{Uvw<#AMO$OnL8qn`ZBV zs|RXDbYkezc;>ht*j5hiO|NW}b_tF4(i{}0t_3{sL-a@Dv_J1S8cR?MIq+ab%~J3- zS_zvPOsT<|jjDl3h`d=L;%**$-FH$PKWt{Pa(G}2tMhrR{J~yvm;S566mtYTRhgM8 zC&zT^l`kY>Gms>3eX(q62fFVIL?_u4uF+K*RfnVNL)t<;?9B83mVfY z533zydz7Z25^S`=M-fBBnsCMTDycbT-%(Q|H>eta&U>SaUuz(swG%|=N$M5478MUM zNV<;YTuw7QRK+D#XpH@E2KQnvb+~$L?BMV}d2F`)Hnw-Bak+6&K$d(p*zLnq-1d!q#H1^%!~`6Md1uK0PPk=toiRuI=~1Oh(NB(xihXJOy=*?MWRA z|;5XK-yqrh5(7KlYd({HaCPluw;wMG?@s~n|xy7MTzru zmLQY=%_{rE4joJv?+!b2cfK|~{kKaM)Oh0Z2>LjH?8U^bUOf~Fw}m27)7D)aj2 zIdA7s0_92baAnxc#~3uJ8;d<@5JyGF;5yb1psZfX)b?)S21ipW;WoN-E_Yzh zDC;lA2zMW--8w5yBBQ&@*!irrGwsQxVDOMlWj-@w&#@3hM|oIF%w#j|5}xLd8i+*$ zPGUZ*q}3B7{oA|pXLT}0+5zRs}^re2a%;*RUxX6#uCT_ z`Y3vo{;t>URz+tQlQZ6x1buhUROodvzEt_WV6<*@)c7(j1O@xdI0^ZSGYb^-KXfm}NQ?+VT7hWF6fl=p6eLfulo$Gm$iFnoc zU5<-7NMZ;MnAjJxMgYo;^vP<)kQcbf=P;<0K*r9%#}C*hgy|yxdgI4@gGq2b^^y zqJwrd${$9p3J`F80)G<~V@~(P4)h6FC3$SuTOJ8an`=W7sWmf!--ft1fd*R8|yCk|MeE@=oK_{#mKwXJHiF-h3IkpTh zF?R)lX%l-*G);!8kWvW(9)DiIL{yE&;p8i6@>!kthE{Q;JQO6tKO4Pq9t?@85XJOn z?FFqxQ(4n7P~`w1$9|j48fVhpb>p-UMCwZ6%>sf7esq^;25jMefL_pPVoB_Db%+U8 z*ib*lo}@fPM~wG5HGP&%%13oHc554#%oRL}P*kly-)CZP_#* zY2BiD;c|1FMVG~zV-lr5hZ3AZ4A&ERN@}9HyXT2C@&e9oqi{l`LUS;@IJ2-?!c&@6 zQ6bCrgje6NXDgr8HvlVKpkZ``5QLD#Qlaq_Mu{R)0_Le;Ud|(JPlACcdDI|sM4n`0 zPg2*~U5hI)E4&7|=y{UVGZ;@0fmsZz+_d0vetWaD>nO*F>C~L_Y8)k~s35~S%%T9; zw%{9jBtdURv4T;}d`WsvH`m@}V6;kjjPToE?DfToBPl8R{nuY;D zKOsofX?Pf^JUa~6*VrzR?TD`7LC&Gxt9v!BiHl=2(4fCbJcZy8#9ktv64jTK>f*10 z`*kDk`q^V&(zjCusMvULCsWud15Jg8kQrz&3hJDt2Exr%Z?Uxz^n;o8VlB|Akt&aM z*DdSX$^cAOXE6hqwM)#-V1^ympEEs~^~aikZ5#+Ma>={^{h-Gl=^XizUBc+*;`T{4 zs?MLq4Fz=Q2R7!@@ZK;}WzYGG7Uf~|Nh0}4e$0-6&?gHL$IyxV9RQ2TIrh5alcFCS z8b%Jz1;}|(0n0j~yeO7AUTkks_EQI_sN_>Q3^7hyj`bE{<2|v2UnDe)vXO;%wz6Qp zE30A9Y0g5MX{pj(^9-h&Jj0UZn1gDy^dALUOfqzH^Di8Tik6qKnl?EgAsiYHC{ zv9KvxlC&u*Mv9eJrqk_#!)h$?xrH>9XFs+VtB#ur9RT))o>a6v^5u6QRp7hrY8wqhiiKup3Bk1 zn9*hEbB15Z`CI$p5(d0}EmAyT*{J;?3NDlI%XKDy@?tb!%5|SGv=OE?(kzqy2ymua zv^$^`)3gN-9Y()*?6YG2;JuD@`+~9bY%a5E zP9_P!=o_eWiv0|CP8`CF@EUz@$eCKQu5aHlAqXeT|LOGJnc0t;bzE3=+>vF`f#$ef zGHQ6x4&r$a5Yh!#AF234&8+r3j6M=$ls7~sGnIrqh`XMoK6zev#?{V=B}Y~%02X%g zgX{4$eDb2P#fV|9WFkb(t4_LiG=~C!HE}@01zOE2*V*vU?wj@j6K+*L@XG%IQ;KkG z)amR(-oDbpLQ$O44~PLFlwQ_5t2V_aaaDj5!0Qd?niv#JYXEAZ7|&|knQ$YY!5!jF z+F}W80Wz&Y!{!;qYAlvql--9d*MXtihnA&&4{+`e+ik}(1-PI%i88$7^OUGO%>^J@ zdO#YERm%*L-i0{w-zG9ZkJY3SPw7QIZ6Xu_6u?NcyC^zaLa)_<1BG3L(t-Fk_DF6J zBs(k0^_I>|vvlE`qnYeVJ&Ps2B>+$cAh`tE#s_rl({wf*Q?48Dq!$JDL9>Be!Ek?6 zfOsJ{gmvM(%)(x|C}2ra_{~#Vc-#61JL%fsP$xoj)$|n6mpsX_k~Vmuxm&mev#fYn znEHcNLquLJG1T!mJ_t4Q@`#5=o+SE@nj=&D{A=lCdKnit9x^zhpt7#XEX-!&O zS%xYr!&vT!3Gp0HWA6}Y1ro{^`1 zt2F2Mb19{GWZ{R-8^m;#gz5$;QD=bz@!-cDqw!`mof8nhEjy<hW5 ztfdpE@E=`9{*i#HRo60e^bq|YMX)(Zx{{;I7@$UAcOL3)adV1~A@rzgbAbv;r^`vUpF$=Cgepeciy7`%y;*siGZIlNp!K_coaL{aDO9p*QiOr?G*MvBVbNhzW!n4g z?w&T+LZg}Da4&;J^p`91P$fhWmxwL+UyjB}EL-eOlJZ$i3*9i5Dp)D1-WlQbhTeS8 z1w$Hc;DCbwnltV+!d*^$Bd^=C81V!DW+jGXZ1fUzyYDGjV)zsl2`*jFdlNn=lCh=W zp?M@Z+){j2a#9GO;Mi}AdzWArL-DqZT_68M7e4n~E%UKuf*j?RnP54gOOxrJ`(8Qk?Svy68&+vyIwb<0^l#RbQ@km_G@?pbtUU3GMUl$rdX3oQlN|J z$zCxx8L@dZdM;F@W3L;Ycz;M;HuHkQ8Pe+xFo=3^USgV(sqe`G?6a{OhcA)(5XoMO z)siQ(Idv8cQ#Klb(tPww(jP!1bu600I2&`NRQ$8#i{m-hjn?QYFi0tyPcQZ|stKVc z6bm9a8kOxJ0F6khotE=vScL>Y4R$audpVZQH0zv9qzd6OmGHE|mlB0I+_RoxgKjG` zq>WbCZVsbwH~J+J1wn&cKxbjEH)O_yfaF;Dk`t3BWfyL4#>D%QmQ6&lEepYcg;hf} znHrf-#G1rU<0^0&LwfCnG-9~&8cUSST$?M>04X2Da8x0~n#(MBS*Dz3xFQeG zx5I8615sJ=-Wa~ZFW{1mt2Sv)xpIx|2$MuZe%n@_W*%T!U~b=WmNf``yXU>(KA5eI z=t?-=GjAF$&hhrZPaIFAwURZ_C^`%h$N%3^28{%~ z%#(ut&23s_22LC|+ATHRn?;vyd)2cxiDYQo$p9v}r2tsrXc_H082?}+npPV3VS)+b zjnUsmF%_H_KPS6nB4S9q5Up48o_5~OCJutxbOf&Y<)f~NB$(WbF*Zr;bGm~w6OuD~ zAly;^qx7x*du`m??It6IZv@W7rhxUGZji7Cpy2hwftg1ee_9Y z=5WTOiCrzleqf~TqA=Gs+Kf7|k_XEtnGMmLw$>K28mbjOsdT`4AY;FcP+|j{dA!?p zF+u`ZQthRV8y$`6D6`UjsM)p3^V`goS4ZIRt*K^4g={XRsW@QT4dvbH8a>flM za;3fmU>kiZu~BM`{WjQN&z=;odLD^pK~GfHu7K)NR|hQ@6f)#xz1N{3CAyB*I5;So zh=*g4i3Le#Xqu8@+`M*Vz_CXnun=}X@4b=m-c}XN861d4GePQIO-07|KW6I`tn$31uCUd;g6+!Zd;kC9?GvUV;qGA zGWOe6CXcNdMZ86^6AeJwZ%2V?;5ZV67eG0pkWfuE0^6urKC#rmX*5P4kAx%_7I>t2 z%?OFpSdHUFXGpWFaaOOxuqzn(-s7KD8lkLYIjQC6NP-Fta)UL zFvpD4(HNZNi5h-WrY@KV(~u{$sVPJ1G~YZ%4vjLC_Sp+G1Pbnx%tUhMqhxw z*_xrYF2oyV!NrG+V#TDjHTpcNn!|R|9);omRaLlG8yvaSVOUR3XXw zFPHhhm8}1K;pKrF4ZctSQhYW8bqN!fzZGYrD+xFZAp|r91wGC9OcWs61|3iCm^$6R zyq2-g1kfVOs7LX#VvSZk+yDHX8WlXX^i!~;z?0>(g=x9UA1hL(0B`h37u(59v%?j1 zRDC9Cr}h=z-SBKP*a<^g5utl}`z4zG`<-4R^HfHp>$VeIab)jD4AhvY|8i9Rtu)jW zYyJ_dOW#&k1*|6P$E{Uul%L5}Tty0KZQWV)Uj`N!;LLp1I1$zFyvJ%(x4`w1+L>6D zak~J)n8EnIVkI^ReU?0ActWQ&{sm}_Zmcvln4bS$W&Ee53{5abWwG_A{PdHU;h&H{(a{EUWuHp!^XA)3RC^g|Gg52LvLbz;0!b8+nQ7n zE-Paue!uHqBKzpKb+Wk)nnWa=@aLKFZLQ3ZFCGhl)_7S{^ zh%MDTpZJeGW;~&+ys=Ip><~DAE2qZ3ZD?dtJO;xtu9@>zvXW-f%xTiS`p*6lP0VLA z(LznU*Qg;gDW6d9y|S{y1$o_|zp)P_Ekl5ci2T)O((*GiqPuXQ-DQA5iFdMZ{w}{+ zxBq*#CY9263-7DkNRdcq+Au%;WMfrKFz>F7b;jpznXvpw?34Dn;-pE%w_WR!bE;xc z+z?=jprfWSJ&ke*kW;Q?T!dN{f>LrY+=R3}bszQhejC>I_jgKrh>H9!ppPd+zfV;O ztCFM|=~C_ZHOmzcz6je8tyJKE z7@5EAe}ltDSGonMTOJIz#OvSYu2w5~7qkCj#=QP*B6+ovZ?XJMip#{RdY?a@kZIvIP~M&Cf-I68P-Hz%Z$&Q9;ttRrszfKBT$5wU)D%JR>~47QtWv%eMRju z4*lm_(YZI8RQNO8B(@Ue?{tSa8yVM}XtIH@ayt>o(gCgVgz&TaY6l7*)1A>-2f8+p z5D2aEphm!6sqU5a$8q(!`dsWjciYA5bNgC6kI&8Gd-;uj|NZ^-eXOcieM@A-0^HxrboIk&hVDOAN{7qu^H*)@5DK1cp`5aJO3@Ad*w2VUiGgI4~YGQ(vv57^TjGV(A*vAU7RvV?o8T;V2N+n?w zFQ)jmzpNy>!E@sQ@~heVzi<1$KXYR2`}vu{PNrp3zpR{?P5oR60g-I3fDt@IN>udc zNM_~gvr%r*3a3T_9$P2T1e)cA(9RKt zq52m)en&r;89pG@7EOYq@+RC9Rgta!Ld*U8YVg4BiP<`f( zJ`8{xN@M%BI)+lZELG}NF+LbN1R>tQEo;}fIK=SnA= zb2aZ}*_7U4^=*HA=JeS2^D|{NcsUG)75O@|Gd}Z%gQJ&)dQIH2sWADUpBdaZ#d4;5 z1JY)E=Jb5%$7jw!xqq%S`!{3=S5gznO#il&hf~qQpP#7?FrMh4GJ`JtCZ_y%%KmdF zAWjfEsMLWyNVetq{IPPAaA|VH0ma&gh3UU@M)msRce<)ociLb_QffhP`e*V#CT)fo zg@nX>tXc^ZVybYIC$t{4JOxJNHyYWre3bjPyRU--v=XUgU4s)V&lPl2#Thb)%&z*j zA?-H^b%=d%coxg@Hfi7XR(@NB30RPDzEKAyCFBLnLn6=Rpi+Hi>P-A8*66L18A|4H z&&t7QvD$bL*i0 zX72XyGdazZqFxTM!PohEpaU8gz-xf}bJ=?^e&s*%KA))%>YSzd_1id`f1mlkSGuan zcIZZVQ5Dbj=V!(~3gwIZBwkC^Tlu*%of5?>H!32_vd;doaz>@&{h^8xy(Q^SSCKee z%E6t20(V#4yb@n&LB*zkA|w%H?ZeEfGs9PZW@So4iD6*> zayG(W)>M&6g_?_4Lo2gKqbYl!lwWI1;w##7qsbw4pt+;mW;%G;RYXzg#H1wqon2ym z+0KfNc$I2HirYv{0^cg5hjj*MI5B#Q_es0bnK7zT|09~HVJG5XKUXFWO+&MOSxL+( z7P4GbtyjPE$G7#Fh@hN)2rO8|%~+Z24cm`g$R4^>8OG0VJ3V{*v9e`vPJ3o0n|4>- zE5EFaZF_Jql)5V3{#-ddd;7UkDIv3f98*Lw4W9mNd1g(`!R%`&k8a9vf2MN9T;Y6- zkqx=4J~Iv-z~(hv$E4~e8+z)fweL=T%%^V5HE3~yVn*01RDdaWrcqH{I=5K zqd=q)#s9P9^ZZFaV`WZRu4_ICl(fH5ddJ?1H2H2{W$8=ZNFcO)8X@YcuC|x zw^iM1aCT<~r%B(LP2u5s3GsX$^6Jd0$&3%q>xpN@JJrPb&(rPYKGaOGDL!e{JN>zG zI!Ej0N(jUmj7N2f?p>lwKUc~XbLq#k>JA87G~YpKpBG=t}BKqiF>&d zx^o2~r?Z!&Qt7<$X>!dY*+YDrPKsA8pqoybr=NgWMb6-@I1ozE<~5=h&I(qDSK(6( zRxtsb!w;Gk19%_((sIoS1V2mPpZcWy%e~{raeg5I8L+Sn{&Dr%N;GHXjgQ%MWe&UJC6F8cjJiS7n_dYM ziB*-^xC%NkKM4t5GDdha_=|ETz#m+t!$o23)S07x3Cfv^U7@r0a7 zd_~{GNww*wW zsrdz@)Yg%67fOBsPi(I+ zi;3Wx>+6Mf+jf^BXilHYuHvn`W6RSs5<(m3FPe|B5z!*}3X?}p}U3ugCmY@kd9+GwJd zTv5!g&S#6Uk%x0W3_gw+pGW1!oxUhk`|5Yz*%#q5c)H*=`H;ahB*F3__{*y#IGadou7diZ*nQ$U@xX>NVN)Ek;4l*mqn55sv0mjvhGkO5 zlBd4Ur|gY)K?WT80hg+;!+)%E4-E!W6gsof!uUeo&`uo2cJqRjGGK&n2zRlv(t(=s zHI;_VRxdnQRd8m#VwJVqL9gwxrs(rzxe%3aMX=R1;@mg)eA;uJH*c5$o0Gbjm26_4 znpfh<*p*$Vm{RPa&(Gy{^?EM8K0fvf^9eo|&((Uj;O6m-)W80EJ*(BP({s0fy%t}a z@6}?v|03}H*gY5PkAu!Wk?!{SDn9%5`dS`epNkI#%R*kVUmSPe>&0trSAY0c$d2Qy zL2sS$VP)n(Uwl}xdg{A{wLU||zVU-;H{x_%6nSRniA!_MI_hN0J|a1+a$D$ zzfJV{6MN-Axb;X8fX3W0C&U%nRbwa&mQXYmZ$*PmGl4)>o6%<~E1*?7-ED{u3z%vp zntgN28HOY|e42N}MhbR)-y$D#f4VqP&|-8De%OKePz*mA);qjK>?K_7%saKnXs^Vj z7{Sl#eOP%B*fDn{3+i^fjEsG}OH^_L6um55e49Qk+jrES%<{sE*_eE57$%uxY8PKR zo@()NF(tTsLfxm#J;aRDh|1yH@*AL?W{au$;S#7$HYf17(lM}VwKMG*Ei=tGG?U__U8FAzmI_$;vWfx zbh&y;+=+N{E;CkrR@SO7(;IVvcd8scR866rG%YFqQ^Sx_&R4~^-FBZ*xCEi2+uyh& zpDCq(5HwRH+!f+wED{d5Txq6@(0M|}OAuo5%nKRdxB1zB_pWHymnnNXFypPPadw>N z1g~Jhb01E(iMB?KOP00YXuga5h(m>P#{tdxmhVo-7gpQP*J}H5SbV<@-;3?)bGKkd zxnAfy?~fmAjN}SmwPkH_?eHaDQ9jq>C70wQYC^-c;%u#U!Y<`Au(9FDEbmEwiNKc4 zxwNPcS2>M+kxZT`=y6wdu#(XQnH6cmz|zVsGW5(TCsU-fcxM_eZSz`yj()E~xY+f` zPdWiA5NbZB8~an&PY)5AXi71#liDeV|a8g;)|L_dZ)345A5hx8HZSN)gI?*qzy#1B$EP*?qeACXpY=%z0F_ zJv2REh%;T4fsKF|L%pB#nE@&WTnQ*uO&Vvl&f8bMXlNjtA@f3lJ5p#K2j3odL>c>x#L)VZ zy$^M;!O;tJZ$Z)KvZav=sf1NHnai7;NEM#(0*1MRAAg%1 z{P*5Q?C8wkpwBdBSA(jWjTc)OnJa=*^l$vLi)g|Fvhx-+Go!PD#gnjy4CNfWiLlVg z7}lGl&1orvb-aAMEM@G9P(WJEiu7c0-1w*!;o&OJn+ z)EN^nu{&cQGmg$vM%Bq+GZT^q+|i5$h9GO14x5B?b;=z6&V6g;l~RLSX>)!j4ghZf zkoWOmAgcmG(alF{Ao97j~J@8413mb0m3WqiWBG`Wd{Olx9?b8jGE#hdr9t_&F9)uL(^ z({+kt^2)y)eoc&D#Doi?iF>5cRotJLBykbZ9V3vDhgK^Q^7U4HV(X!C5tE+Y;Xjg4 zmrS)1b5|ygdD`*>3`H!wHg&~_p)w|*)W}ik5cmCCNPEF^Nqbr;DjpcAqjZsQQ{I)qD35>$cV{zsT*WL`Dru z?aZk)KKxX4Q6Rrx+Klcesj9T4d*3I<30|roGV}$eccrw7F-#5mG>@@;Tg*|~UEl;y z8W%_LRsd+eE)&dQOwbJUE<^AfPSnPk9r+cLO$-N{_03m>-a*DNu-HCnzg6G1``POF z#RFZZ1yflZLcOOfFnioAA9jt^XG%iOf*eJ9J7r!lG6FjSn0{I1Ok)~RY#nR zB>wjnA>=#NN`bvG(f`$D)6^-L+`YF0(KWw96sz}V0wEswPm*FG{I{{te=}nYo4t1m zaG9y;TRji%h3~J$$7%hsIDT(+q_)gsbq~J@&=KX1AH`F8p-w$_hsEpoge&c~Ukjw? zZ?NoZzyExG{66>S@3;PbeQnm?r^W8;W4+jaZ+X@pj$aFy>~m|B`4ZW9{Il0f$Op{^ zdUm@J!!1Yk7pSla%APDzESEEvJOq1QWLYoG^~FXzaj8#k1Uc7?$n0+i$h7S0+p=iK zgjD7ae&2Yx%!P3~h$TvP+j@END^oAOcZGlAM`C0|kn>v+S-4qiEnPFqYTz{K(cU9p zGvG^0ullyG>b~8NCexWSgkaP8RsxD0U#o?AoB9H~?;S$=cC}a@p3mjS@nf}neim;A z2?XbqsqXN6qZfc6FUeIRZYW}5m_;{uEYNw=@+0ka9U|}_lZZ{_o~MPxCklUna@!rw z7KBs)-VJg$gr(vz_>v6AXok#AXxX{V!$yqH5V-YF>0DfCAB+SYay0$L_VoQ+JeObl z#Y;A^kr*&v`!1f63BDN=4Edg!ufSt_pD*u1MVA6!jmZz9+sSn^ zVCJ#kK4*L(9wgko`klQuqnSM>o#7A40uxo_J<1EFwGS*pW0%X%jH#AQ^iU;QIk_tE znGc{t3561vuDL1RgTO>NAnt%;|Fd(#2+_;+Crk*oRo_xA882qV{@Y78wUG z6Cj$#nZ+ADE(>@&1jJTF6&^*MQ6(F+t3EXM+Jns~mAM_8tkjHW8TyV3dx^PR?#Kzn z)cK;PZ)NXWff4lOyev0+ZwZ|UlZ@4~x7KN7q2hwc5IuuHfStu2;#Jj`(Pg(}BnT68 z-5zz*mNE3hfbum_EPYP#nFzVcPulJGZ74^g+uU>B(;%z-uGV3B^RO`)!U5w6XQnEv zBJ(?#x9F!-pXnG!Um}LIQ+5bf+nkCs2fS^iMKb~&4xtp>y-zmfxVc8r+$9w#dP}gs z-uKC^>=4&t`AvJazN<1=vtHYpCt~$GyLSQ!2Nk%9Lvwu;V<_Gk6R?Og;?}Dh_D~QC z>Ju{dEnamMZ_NYij0xOFu+>m=p-t(KE25--G`2T+)vc)7)hbFg{VVe>vQilmhb@m9cCxM^5H2qc1+@1AC?bs29WPb9uO~2Mcst$ zpE(2j2gO0d#+ExnHazep;X2B8^=%yq)*OE0%#Ol(_o*?xJucXI%1@sA<>Iu{yne-y zc)R)d{H$F0>6Uuq0^Ss+1>W-ehQMXegX7>RBubo%-)jFE_=%eKjzxNuPWA-1dMT07 z#p>UynTfga4X@DbG-1uu13jziGkG&77Hw&?th6;Uw~KC7$)ih9t;Ao^;QPZdpYA0~ z3~ej%x~1r7_4j1OiqgJ!H|PnT@H;g1nJoC7cda-fb7v}C#<~~3lOvYa6__7MteqDV zLcubwHv zGZa2@iO|?xNn5LY;RN9~qc>XjL^6dlYBWVt+5;}dVjp47p&)Ps#t?^Bp-gga6pO`i zGSDi|l{jXwk+Q5!%1mobk%(2rj)28a^_i!Q$xzS*7eo3Wll!qq^H?r%x{bl}%#~nk z3|29)Wj8yI85awn$tJOyPs2^eEXln~Wtc9kK+H&$;1#zA_q6JFcE^R&6W3W4`|*kS zyj{P(ahi@O(c9PV@TE-nI6aHcJ$Ii|06}LwI_rj+WCzt67#{IKj;$G{FqopcU(UR) zOdaMo6dBVBVS>yI1LlAxdEQt1=-j<`;jEpxkuaN#T29nt&)9V(DN= zo(>usgIDj)q$`({GE=jtj70)7wrXZrw%HI;j^pESGmUkrgeh}fsUuXMlg{kwPK%wU zdA(<;-;kzoJb4Y8KAN6J(dDENWI*f!7Ny(1HS170lCkIt=Mr1EE@jvJlZ|0&w?0J7 zI572E3~(!*&HCDXvKe=99|m3+8ti>KSf2ED1HjV*uekfhh-`tYD$@7K4(D)^8l2pi zd5ZLed1I?1CUe@;gLikih*TZPi>5yzLsBJ(OMUK{N(10P?^(gIQJI^i{pR`zU39es zSMGE6LPeyO;tC*K=4NMcCc)wX1L!dy-1t&I;w>*&Tfn7M)rF2q=UH)CG7HMEfx$a6 zEV;15;qJV3s+&;3dRn6)U|r>`Om|}hY`SYz?6)`nncb2ZwJ<6k!Tzw~+43W2g6zh( zf9;`@{@cyh+;~;2(5mY0j?IzV{triOvU7sc7u8=!Df)gH+;nHeHK>HBK;DLeD$Z(2 z$&EMyAcAu7tKyJB)Z}^k@kVRUJ1h9Wr0U>=LE$=`#m5S`H-dSYoiRZ&m3wyMskGlY zS)(7886pCz?GVAZk+oJ<^n-{A)%iq8>%2eZw4L=CmEMx)3NqDzS+{u0JIxTu%dYY~ zW&cEUen0t7dqSt9?M|JT?T_+WdGVcP!ISeTOOZeklAC^@7%4teH0V1kc_{YZS*x&dCiIiy3q)Ja zVu{;B^_ji*Cm@ZOhcBv{wLM#PD?deE2(rQbZ$o7N^jbD6FbG`AJnMl?tY0X1R7`-J z6g$s7c?t zZX_!ayp<}DftJTV%yl-(8EKNnr{XkisLa>oflC@<-CMU)h%-kToy@JRZhH}0msXOy z=S*QOjr0v5pm}48yNOxF`$S%b_#r&Z?JknkM<0TVwL)WiweM{ zv)xf4;AFI&07jlTLEB3F0=xR|Y_e?g5RB(DR~oCQZWisCI*9!{O`rWP7@&ru_`)j0mv!+J9}g->2{J z&WbmKhv(gp7Qi{({R%AeRnfJzFF98gJ)LMzuYNJdp}$s z1XroKgX+aQ5<#s|#N&Ru^zeYz^P zi_h0`zn~?54mxL8)3@{g0c9`vfA)+xdT_*Gh$svisiraV{Fgt2SBl!uO1TB*!=Fj; zldIKbpVT>Y7S!RYsN$Wr`B+_*kF(O5*IU_{&EXl+ z9}z^@f$8`mNb=jqy1@8-R1Ch2c(TW1&&g^z*)85W4zGJAGY+dKBnqJVJlrwuGu6+c z%7aq>aNAanDyU+;xe9GnX9jtCZa%)h4~zW=OQ7xHEJwF%A)hFWX`U84e?FJ%a z)HYThdX%XOL~#=bbvJ22*~%1836Z7qgj3ab{#N}1lw7sb?COUWtusf`+l*ApILDbB z7?wZ1Pe@W7i!Pef0lxv|x-)2|6$=ul;;me|qvz%5KDclb9~`pJ$VEn)g*##LWYy-9>P}0fy$fCH=++@JT9ePB5vE6sxoQfjhtr!a^QdQ%elj1A%+>b&OSHPKs zPDFfq+V1%ZYLnJDm2awOAR$%_i1Ekx<51rlHW)H|l{tDeZnOg`uw=a98fLwPAD&iK zd9v~HN#LAjCp#;Xre9G9fxFC>s{HznPm;bJy;D1%1j~FKsZ{-e=0z0mQvtGvaS78c z&$VH(BcmNK4dk?|ZhT4}HV+ST=ii+Zb&#wNU;oBdsKRd)pCJQrglPI|FBhc~1gK3f zBv!HSVHOIlzz!dG=I(pMt@^0HX%8iWO=b;l9-ckosG%J*l`5N$<=6L$Ss@mj zut(FPX>&#J;)d`CcV;#&Z9IUT$HaRz7|zkXZ7?<-p5Dg3{GA+wczc<%J47*$>xy}* ztT<)jQt>-is{Fmd+ELq!DAW1Rki4bS!SNNFG;c*X46zq{$TqX$_*jYP`1!fmeLof7 z^!khE?gJ>pPipDKJU?I64#d>%%jt;&B?cHUZ(pcPn#kY}18BHFSGsvb8L_?D8jJM-ix|(W6bZ&$-P@#puQMLKzuOEpjnk3=_emVeh@0*{Kp(!VOhT!$ zQc_Oc9hMRzGW&?)yinC`cCY2)D|*E3?rXDne&Mt)zwG0$Z-~dUz*-G)M745*r7sc3 z!fms^#w9!(#8n{^S&1{a%V^l9?vk{EdxAKTuxFjujqat|~*ROY!mA*2kf zSAG%kT0w*6NLcw5%dLG^(hc=t`<-qFV=7k5?8=fCY$fs)?&dmKYATTUYS60e;c*b0 z;;k^A$I$@t%*~Rw5Dd=GFleqFxzn^9)ssA>R>qQ7ECeevfY(N+>B+9wMq>@2B<8Yl zufJ`YXGEKxM~n57*@_O6tPw@b%*ouNTvbTlZ3dGjz(Ca#!l1(vde9OLsE>*VzgGI; zN5JTcXS*Yv1pYNOc&dTPoNGnCY>cI+WH;~A&Y{*vhu(n^SqE01siCpg%!NS+2cr)gC znjp>AY=}kVHF_?>uwoohs@j39{yX=PIt%5;EjX7cv}GLBm)9Guw3BAU%coUaNJ&`N zC)#Me@WY`%nyh5=Zjd8PqjlpZ&jZI^?~O$g1=A+@VpGp0dpxW$T2NWPlJ#q+&k^++?u6UnD;eqE3$V3ZB zMvkx#k!2qUlE4Y5RvwjQkj>1yx6;q%2MF&$os2HATA70#hZ4-0U`0#5TbVI&*$Zox zD{ThcWR}70cx_LJnJ)G+A88FET!8AWz{)$LCL&wsDbrse`~p)C6S(5flmRQTNQ3xS zn9U6jfcN$vrLqEeDn6y6>bwP6nk#Vj3j3CkPwxrXF(ljMeO_wY31KhCnva#GY@@nA zZ`~P2Mv_o^U`Y=+kx2$zFBujcVk1=D4p!b>*%Im@@0&ck3-l33bd}>!dC>N4_yO7V z2QC`-f+u0KQmNe$UFPD}+q?2r{fCp*a8;bn%Sk6HoHJ%KO9#7UzqSfD6ns?LnH^xKYU>n)SG@v5fkpvLI+eQLQ7OWG`S9%ZgKNqsyQiw>jt5WRbd z@(bhr@<1kOF@Cbz30h4*lvz1Y!(^${3VJ?NsaRFFbro;p<7W$}VxIVB^)G zO3ClkzhmeGll8U$D@7K^`tGoQZdgtly~c9}NPpUX+PmB5(vVX=a z&htGRhbr^X`%sgT`4_t$WZf5&sG}H3`0F3%*4!JUtp)|b-z#T3x2PTmgKA$b@qW5{ ze$IAZZbT2?8&Nih9Xq3A*f_WMIk}tQ4mENvEpl*kDiF=kHuAyyN$P4Y;Dp{CV@IGL8$sn@WLvTi<5CG*6bK`_+b^~b!<0kBaYNaF@<7V|| zwu&y8^8)GjC*fmPE6K5Nq4a~KpiWtcMK~MrAUv)r_aSE@j1r^}e|J{=NCX%@z8ozy zCaPEjEZXz9!I>B)7CT(HzGrsAXI2x{$u>}o^O`nwT?+|c4B#!pj-Na$QH{05 zpChUuYxFyPQ{#i<2C9Nqvsa(leV?i2Q7TN~n>(!phR_?S)w>auF}sR8tcY(eB~XIr zL#T>Xmf@a`uO|v;RW4x$CB+lcMz_~U1?N@+Op<6-6h5;R(mG&RB&Kb_ zTv|++JPF21G+8?EcOE8Z#`rcqJ8lb_W7Y3esLSXH8Mrnlr@x!6tXIjN2P>vv&2WCs zCXi31(QD=8J|U);x5o0#Y=TzxOzvZuM5<@vDLKpSGbh)Dh=H279;gH*D_sV`Bru z;H$bF_TS6TukViq_dx6*%vUV|c zH9@FTRXh+E8_sEjz$A+KUj`1IK)B!c>OOPG(9t378ilEz4NlaOhdL;$4`o(P7>hK5 z(LMC?-82&!%z`r%{!G`B8R7A1k33UZ*gSwS( zJ0r*S3qxl#3#Oqy({VAclEV68i0{8V%#cSDOZc-8XI9^qE$mHV@y9iWl0h#^bTOnB z{$8#8*gg$YHeNgR8I8^2^!(l%0QtPURV+H4T}|ShyP{RG8*NF0X0Ke54HvB9f`Y#Di~Dh|4&4-Hj z)s_S}>m116iv+#31*K5v;o{Md!M@fn9~2x4tn5vSAcVK!!);0;e)T zH;Yq`zJ9S2&fPaK<`l|2DMcoq`b?JTTtYV?>rgY7pLtt5m?jRtnL6qQ`ITNfFB*j8 z>f5^S>D*wmEk|LyDv2ZJw6}JTN~6lU(W^VMDVHaw65zH*&FIT+=}qocL?LvG6&?EC z+-ef-xbu`@Bd&lKp_Vn4;^fH|jC6X>r}x*H#W}#8^%=aR^JMFZE@^h?c3F(reMkK> z7R~`G6pB@qh4wkci0p;@wAg)C(m=46nSHmkk%3*v3lKGSIA~kV z=VrRA-7cM5US{BcwujuFqL46QLie3ktoWU7*v`t1yEr#w-LRM`r-pFcOnb;Eb$#8c zhT!1BE->A@fg>yXLw=AhKd2O{UZEE8QU49yP$){SusrNf?g!YpdF%Ho4)Oil1_;e0 zjKE#9&mryB%U{sHc zd#W&NqNC39X<%UI&70Y~+z`#-88jGn4oo=1hlvu8721O}>S!Y=yOjV`ZY1nR7`tk3 ztut5bcs?5|%`1~ST1-$9Ok|y+pQl28VCf8dXfo^6N?mE=4w!7VYG<-GYj-ymU4Dy! z(p+INd6oQ|J)?#QPOE&#*6WO#_wLyDFwOxSA;Kav&58-$nG|&;XB?`!(0gf20Dq9( z${$$B0b=A8&dzV~d9Y1GM0xApRI`XW&H<+Hwn8Z#vCgydPy`?WkxdM@y-Jb_jx~39 zb!F$`uG$^^Z0pOd>@vt{m0kW=YIVxcKv6{*Ka=2U&|x?&}vVE zaab-^r{`+*%s7{_EA{zdP{Zen!SrgmFv2n(Fq@;1WwD!jIZmgq@z37#r&;M^WWsT) z*clT%0KzTuLa(cpz2^qY8*P*RF;4le3S~bYC)lbvTHcBZWbaNnyMT?AXvoA-j>WK8 zfZOT}ugY(0@K0jIJDLT^aT3rvi55x@seOnbXIaTAiIh4G&Sd7Q%Dam> z^ewWz;$9=SU^}>hU=I^f;pG$OcDblKEcBfEYQ3xdWM8J7MJhQ=gcljZ*yE!Dq@8rvXm%csXZ6`KJ zIV5sBvc`Z7)s!gQ5ni;65DZjnpNYFh(#q)nD%GfR-k9@)$9Xcp>!z@ZH`mN!QxI4F z5_^dPYQ$!p*(xe%#(_sv9gIFI3w)94ce+gQ{)`%C;TD`;+RZE&AA4m;UV){!@%W*c z?Eu@Dx%=!ejo-bIL`}C8c#cmuCV?^GAj#s}?oeM)rrp0}J<0!s!=wNazOb??S8ka< zYJvW zsoZyVGMgT4E|b9O&%7=-CU>Dk4O3=y5aDIA9Xb+p=IYya?_I4A(=5)kRzv6SOzss$ z#ebb{wr8r$ozbRM7N%!NPYnY=V12}rql#h_vzS_n*}3{RpUHW~fo8wbcHd;&Xn#D> zBQk|J%;iz3Y3 zU&qDq^TeF?1AF@U{5Sy_KK7qX{ETw=EcOv8^mgmp=}@)2C{0Fapi)!cX;;O1+W*e; z!fjm(?Cxj`E(Vibh>Sh$jo7l=&gYvBr(65H<=K(@MeEs1-6{TAB2>PsSpVS|sk&9* zEiamimhOk~ba;+)eYcoU^k(I}6=|Xs1WMZQ4(YR5*SMP2zINrWW6B$eX%I)YN?GYT zH71CksQed1TM-UFBDpVUwzhG*T6*nD!#2mn5#Tq-E3sy(j7Uar=5{AHbu!44T@;4 zF@kWc*D|t9(pq4{wSZ1&FzDK=U0(b$^^|fuM7t+f z01}vL=@3=lma{f1ZcY$$V?uPB+{(#5hJ17KEFwn?%9-kFE*lh73G0TSU~sm5yJPUZ z<%H((0)wkKq^sIlpUGrW6oIswBgNV1nw$BcdxOBPxEjT?y}<^Uni(A8KJ->5cj5N8 z?l=@rLgLA#dNiNOI|H~+r>fRZ9aDWs$xA8oRx4%}}xP6X8HjMUidCu6eBifFqJR0GqHw!+fL^Q)UzJUDMR z7<58yyxG~*AvD1;cF4vhBJm(M+o=ZaxS25lLTEgODr@jKJhNGh*ROA43|z&sH5!iu z@^N*t2X5u`lN~?SzB}6|b>6%WAu}(5Ph0L4MFV&f#Y`n^z*YM*XninA}{^c zFkft*LE67xhsD!CCL-3&>$R;u`?dX$;L@tDK7#!0Ui5wj2jEV887hnw`z%+xRSbSu zeDjvGde4ax&wODX@qYVSexYMmxY0NA)>C`n_4)MF$YaS2iEjO%&+qTeCm6H%-3K}e z&R;xo-`S3W+!>*m;DMvsuP2 zn!SD;J!fn=a#Kv5a~0z*jY`|mJcHaZlPd!;<)8!1qRcbxQNa6me7 z_al;k{RbSWSg?qBhnqS^I@#*}6MtKtka7UrEEPlXRygr;6xW7xD+Y~D8;-aKf7TSb z%4e)i8o07VLDgtaXmUqGXS}nHF-Z&b?YLqHycQH{VN)#vN;w0;!~46D8V zJq7VxZP6i9*SoThh{WiC389ohb|6ffWfVE7`6W{60_{p8mq7uo-&|Z-D;Q$}HiF04 z-f0smx^bhvc7YVT!S+FmJ^>nrS_TzE^#%WyN5dehmDCQ0wOI1Gc`jZ?(JXA=VGzhfuNMA5iHx?S$N)k=a1zN`OJm3ii{WrryrE6<$+QBU73 zDI%!TxrR9?Gh>6e&>0&b`Y44wj|cD9>65Zd%I@7qNN#<21EUK??G<`CH|sG}mA1$< zgV}Rstkl=#NW>T*c*Ary4Mke->E3*OZ&$DF!l;+m!iFOF+8kI!Z+0INkk2o(Uy8j8 z+t{{A6Law#F1{;52Z+N`=!Yuu!G!gAt&A$b5{`>n7Kqm6O72IB zaioS6<8%R>v=hY)Np)VUmA#ebVGGyLIMG-bT>QhX$#~Bt!48bF`b@J=Fh1h@)Ow}U zZ(%uof(}X4z&_K3Hm?9{ok9AsjDe^Doe>4z1puJNpkncEPZ=4YxhEMX-KnO*Vi@&C z6lb&ZelaPK)nc-mc1y&q&k%Jv1LMa>rDxBKsLtQH>#gFMaif6|hyiR{UA)nw2#iQb zyFFCSn+Ig0&RZ#buf8ZopvGGf3t@N9DRaf>2AR8KR<6KS-N15NUZ~{u@4GvVL~Kj6 z$4uM!FDpBD!odhvY;I0-$(3bE>|r#}w`Q2(9~YQ?(qjR~5Zc*U^5GNFR<$PRY`FWF z1o2#*z82q44*t>rB~0~Ob#gm%BaDmAXzj&y=GaHGa^md+C{0{Y8w6$s;0nAjn1?Yds7&!oU4dtI{S&SwI$ z$txdt2__Z8EXtMOR)a_~)+Pw?$;@Yeb+uq;S6Tu?Cwilp3>?pg~N zKeYI^aYHo;iw)qkheYc!|?(*E-)+L=AA|u#AE($)Btnw!0Flx3@9_KJ=Jfo{{ zcaqP`lL~eKK&3Z|#Z#s4bQ^PW00iL%H?A;Mxqdc5zYYi5TD9Xtl+btiGf@*i84W{k zhEKe)-u21{xhVd`+__qIOL(`;MuxMZMw~dC<6z`cXUt^?Y8#*>47xb_JF{#=To??} zd6jQU_K@V_)jiaO2Ypj~Cn79IzUuw>f2;9tQ2O`p6bsI*tktLhxDq9hHZg?cuJ?{6 z&NG|7o6a8LSB2Y0aSYPF5c6sN+3^S5eC-3qhU7(+7sG+lfKT_5Fuhfico;Ald_YYG z&E;yxUtz7DM2x0d&k~H7*|1D=dG?igwG>fnBFXUJD>T`Clcf&pR2vsFv&DEN7fDPe zatWkW-}?~Pe-z1+K^)0!rgw0HBzK-q{9L3e;~IzEH(l{h;=@eUS^3j>M0M8{bdeEI zL#vB;%Pl*l#(h1^ap3>PIqG)u$ANmUOLqT+= z^R6rBC2WP8;@4I3J}ZK#ADV$d0!*cI zq&+qRP%b>1n`dl%XXHGtNHcf|>RYtA#~u;lk{XXIu^JHQN6Df03s?`J`RO0(Tw*((Ckf-2AKD}MmD?78e z%qi{MyL7gF*-gANiHEsT{I;dkypQW;LyC^fo7v$zFF7M!Ap8+LH-c{?y>$rmjF?HDAAhW$-u!4f~Q12l8pb_kvkgA3zc;s-$iQ%(BOs?*}9+d^?`H*i+taZ9$h zI2lFU< zQN>%ybiTV(KDu%@gu*=&?jg@+uds$FR~nB3stbuCOblW00@sC%X^vLDx|Wbqu%VG7 zw>^Y{pAmmJcc75UA>2xA#T$9$J1-)cqBle6!G^q5oO<~oU92(Lo4L+bRvJ$$-;+GH zJp`ejHhChhBUsSX`z)ShOX&E~~T~~fv z1PdS>Nkn6roZ@|myu)=~rhBjA?e3c4e&YH(z-`f>=G<`{l9ZJ7^4G+X?(pVxDbYuPdUAeTbv(6}@BBwp7h0j+^Y74AcTS zR1@9Udhhoy1elNS&+qiIJ{FR`&&8T6`=`z>a!J7|@fr5s72SF`FT4&!LY*n0c(3>> zek(9{iNs=u~PWLT78Iub885d zIaRqGWCBQTUYP>2Ul?$4n=e1p93ihW>cX5fbAAky0JmQy0vbluw`J>hxe~M?uqLz7 zL1k&B@FKKSZIvr~W6^=T=*Q~0K=fKKzF!(3Pr5M#E{Xc;WBI-LuHr7UW3pD8X+T3R zD!Lf9Lv%9Au)2W-+Rl3voH1C=x=S%&B=-E6__q58D&6Yaa?j*tnZ#=5Y&NEpy?0tJ z%f5;dT-3Q6Q1Cl%CdW)~oLSRb?FEjdvJBzW5bb5P>*;DlhAoc9z)HfzH&B};9rzP{ zxY)a7kX*a|;YHq~8-{+GkO@Luth{Wl^wt`Nv-3X%VP}qV0YXUTvGQA7kXn&1Xr7T7 zxxUM9D^_trJE8$L?K}(_84pIwR8!ls`kfInNl$e_?RZ29Vv0IFqv|8C6=dULA zNQWbdODxiMo2WYF(b)S|t|3HwHh=qs6m64ySda7|UWvSo5+sq6N5oYZj-@?I#8Z{6 z>d~!D;Nh3ovN%TJq&8+KvoV&XRWbLfsl@6GqomcL)0v>+dhhLPz%i#98X+ul!Rak;Y@v{kdvgAD|fz^5C!U`tkMOe;h~O~4Yt z(?BN|uv>8(l4U4DB~~mBI60^lt8WXqV1T}gAHMggCA-r<(}PKJRk0Ki3{jpWio{o~ zRuZ)?Knfj&xbapIOn2B+E)~w;pKbv{vlk^p#{{h+*nM-OwUe#R)jX?E5PWX3jJf!{ z@EE5O9r@W2Oo#Z+fy$7b)*06^h9_KZx=W0ID*XU!2SRl6mA_bqgl~5Zx=IqYE&7B( zb{O0`-GmE6UQJnU$fKzcs^$vq(c?SZU75lD5)&pX(YXWk?x7ehh9Zvp(JI)@fra8D z_A#!cOSOl9lt{EO_-nP9QkIreWuW(lI#K;j#ez%7@fhl)bjCCLEHe%a`l#w8bYt+6 z-8Va$e16Jozqub7cxM9Gkv|{Yj>F#R^Yvnavwan}uYR$2p0_iX)xigWy9MIg!yOZ? z0Mruh0yBm7KdSqT)P?)s$M<4)^q1!{geJxaT>TjFe+d(Z=jV7-iBN`28yYotW)EGy zc_|yh!FRR3rpzQLWpv>1R9GC+O#DSQw?DINCg*?N1b3fU)y(a!^C9g#;miV|A>!v- zo2%~wM^Oc5oW-qjuTi6P-Y@#Z&2d(}+w_K)$xfqC8>&Wye&(I!PD6vraZ#O7fjx?N zbXwCr>0zM7%~X7y^s>c+^HHUo5|!(-UrE z-1Juq=484Ant^0l#je+e#qxG{2V(M+q*IC#18I2*igDFSL!R=l1FYLE9P3)~kFpt7 zQ2thPwKC^3T%>mMJ3O?$5+2jtO1(8P6~Q$XypL|NH(wflemXPh*=>qj*xeMbt;on5 z?kKr-!65(fMRTJU+2cuqj5?w#IcT3fVNwO69=_X|O;_j@q52nY!1t>{r8p-Z~rPj8$Qu_ zV{F%<3t_Vl+D@ue{2z8mnq)jNi~5^^+GUr#G$s6aYZ2H)l;(nmL^2Y)!Xej zSxI+7nCaZdTt7N*DN@5sh;&m743iY3IDHtC#>_s7t|PvpCM+%M>CJ_fZaebvDK?g+ zaxzQ}WI*##b7E$f%F5omX8XVlXV4hppPis3mig!~Pn`)Pfr|GT_P^?jY4S|Z6soR} zLfG%jTn6vi$$V0N%q!UW2?M9qGP=ic186h>)oD>3@Y@zz z!{VE(inQs}%xfR~ezwyyy7;5@-HHrMI-`lnDLiI9q2VSbo6#DL?`~YHPDQu;ge#^g zd%-nolOMH8srZ?pqpXi2!WuAoco?geV#Mso%qVREnp*G06Kdui_fXmf4p)oM3K+kuy= zh7ZT&xoEUXnWh4}iMViP{ot-$HE!L%T;aH3IkZLK-B-Ozk97-7IU|F#Y)X?K*_(?Q zy05=gnJVe#nl46#-#A>;@m=hN%~B>Tg_@~fKhcNM00DRhHjB<>R6U*}1KFmmr4$IOAssW~LHW)`4>J#$2s@g#$b&p}-%)XKQ>4rryP$FCr!$1n! zXl`;-24%?Ay~1_Cz_OKWqRnK=G>X_O+aQ`xm8*)XQAgG+W0+^6Chnn;W(o=$h|O+w zJ6w5a8cLv7IP}=<4DgsZc!mX5>Q=vteoi(Ku4@oWZboswqr(%@DpoO91{Ho+yoG1_ zPza#p3zm$=fYHekMVc+Wu6Dw$I}Y7e8k+G281U8OQPW*VcF$zhd#NgbTlXHyaLY_d z&0v=xmiJ7h$uK^yYcyBIj@LH=|}$=2G7Pdh7uO<7y>TXH6TnkzpjY(GXT` zVaf&YsixIOsn;3%T+WUo{_${^ohGaMsnI|kB-naBx$PPBI_CTZ0PU*i@(Dr5ag1U| z)%p<_sKUfOb8d_huU6i}eunpjRh%buGYxdfu(ZiZN)!!@fkS^W)wNXs;RQ_N(Vv7! zo_CJcfMaNd=v3~vY=pejRdqW^^UO= zZdjta%LcZEzv1ZOVdXniEFqIQI^U;GO(%xIAlV=f6gMSjQ>%a{O|ihG+fuMxicUGI zDzYjUO2qDV%Hr*-3BAToj<0L+Y!M>>g}T%xd4?q(a!Qbp%(^dGnV=leH426I`;;5; zfiZ~T8VIMT0NkCABNf*gb)`jWG!*d+DVX^tV7L0V5-qF>^2H(Vo6J@;%<=+qrjeWd zZTYV%lUh2IXFHu2?E357L#RM~nG~Wn7ixyTde$wPOlFmMX{!4xop2C&J7KbGEbb^# zvxFwR-qhLZD$`-~ksgN|l4$Wh@{HlF;NGuIXJa{n{bf6rD|>ez=V0gSz{qL9 z7|}5u9p?Hwe`3|yl-*sNPZLpHeV)*Cq>yWsr;#C=6ee;or833A?&b!5&|6ne$mBZR ze-M@@V)r>B6ZyFeuXK0?JIHZ+pC;b|z@YoGFb)yLvo)0iqa-B2O^XReryxk_o*3&F*uO5 zhaL+B8&-=+d&x8_SCbfz)&kB+O!7|BAPE?@ln>3K7&0P7tu2VAKKR z%~ec39MhTNckThieBE3U12%53IFHPD>w~qbsjO$6ODPCav7?rJXhrHz>19x5JZ`ET zZ)gb#q|Up;mD+*sjU0!R`$|AxO%?*$aYQ=uS{?d2OswOJ0o0X#I^)IfJlL`{S=5Vz zmJwT3Y~jNaD6Mi=;?l=)fDe@K+s-HIzWZNq`lh%$RJT2`UFV&B@D|kBI9mhP21AL# zlS@jzX^KPjJ9WYtI)y~l`19%&7HoG9iXf*#VIyl#C!CA^kiAW=0NmHT&o-}3c|xBz z6Sd0_my|g6+%WTVve%52`Ue^@fBf1|w0;Q zCynWMq^6jQ8K1llVtK4${0SQj&&yRgL@G50? zEUNo;HNTL#$eJ%8zAG7w-x(y;lw{bF7QVMt{LV8}nIDZ8GZViB)|04^XKo1;#32!s z?l;1U7{^o`Z!2Tp3B#B>h08cKMn|p|KeA;tlV^sVGtn8Mg?KkGN2cHQa(JZ!{l;83 z3MC`%y2tXT>cFUA^2@+2!_v;x?n8BwkhChNWvMq6&z2wuQ_y~>R%)AqU!%hP8^M6h zX>oJ&zgJm}aR?BFl*+AMQAB8Lx{X-1ntW58Pr=-1yA6ioz^$3=5pvdb=-fm4dYJ)Z zzYQ@S`UOD~v?1#RE5(ji#-m*NbtV|)w&(XNK43l*kwMnVmA&uN#6zUHC8Er(IdsJe zEd`e+PJB{+Ca~sa)~Dm#>g*s9JNN}bcA{VLZB32tj0tc}12eaB3ZE1t>kO3@Nj2JN zPOIH_>pt0t+;SqXI zF*g!T{=~^Q62sm+fwlk#*KH&R%Z;>MV=hyY?Tl4lS5B#fG`(j9bpueRN=duTd__Kt z{B^WSj9)@-E1nSdDX=D}`7oe%zO0!BI1;L)ONpR4kN!2@5V!&Po#L0gPxmQ${#DVL zL39S%q0rzhCu1~)WNNN?*FWSpr3P(G^a~MO%5xSlT2l4z>^`6O481b07i=6XB18J* zD^60gAdM9i|Jzfwu&DAm4ulRiK?dh zOrOX~&XwO3dh#Kj1(CUnGQi_Flh_{zz?)i%fuk`%cU|dZ$+j%Z90hyr1O;7+?{6ySeIN)uK&Kv z6i2N0*k+MJkP43PTT`ly5rCTfxdmyRBpq@2Ei8G{)zyTfX zeYrJ$v6ujtmzO})C|k(Qi_cug+(nd!#{v7bnos%AJj~iU;N7x_$u+dz_NB3 z7T*>)$(_{{z`1kL(35)i(1vosE8M)lt-jSxIT-nyw*R%L=rBEqk)`5z;tg>LBB3^P zEh;|`OLI%_T0Idnd=Alq4z53$*ASoJ%PuKZb+R)x?utfce$#OCPMuw4x8htM9jfUj-n}VLQ25_#NbZPM?Q+aR{Y;3_51-Te@658NMml zPTnVPX?FvlX$^H`z*g)8J5TRht~u->Jsp1xwDQ6IiHxgUd52WnZF6x=T`I!1)C^FZJ8(qx>GJY8@M+xBrfR?yJHd*+sslsG zC=ea22;_qlnzVo(t2;-OL=jVcW<2sPh^-(dkA{Q zMQH9jXU14)26!1rf69pB+hPfCwT%c_2d>>@{5IZ1q9ynA$B?Bq)MbhfryJ zyW(OO`6Ir8%KUpG@&L&;IXfEf|GG{ z+X5uhl~XgLmQn^I9*1jKekNKC-Hb4I%D0Wn+dbYd&0T#{#UYCOo$QuAY^c!jl9?r} zkDv*?wrck=duPm|Iim~hGg)lt7fa%4r%x`|Vh>r$I%0(Ce>&WenKQ_x9o01RDqy{znw@wy}k6?X*ahShk)6P;5?h`=5P?nF*i z?ri5A|4w6F9>{06cq`}b?9Hq1d@D+wT?S8SMz;HN@{BC8+;jrbI>g!RV6G6kX3#$o zuSyI9)&cA=Q<;t+C;Ndo0P!P5B^|5ynsigW=aX|AaF~TG@GFg>4NC<}UICzD@`!W!G0mCBW@Z}{cB@p!XUCpzY3^obZPhXEHRADY|^QmE{x zqYv|^YY{esEs|oQ5_+H0^(z-XB^p938)<`r4^5t4VP$V7W%^0y`4mNVMr-BWwWz73 zu008WhP}zMiuZXn#f|~HH$Ti~fWdPfBWfqI5IL)rE-qfe`jJh00Mj_$s0~5C@$#x- z0!fz^l&*>xpJzMSKP<*rcLVzDpP3y+pGY}w;5f2Mo)F$J%)~#WVPW)R@jDgcj6U7Z ztR`BXfFEjbl@S_ON%(DF{&jj#nsOomZn9icr|X?-jnU@ch+TE!=0xrEv~Lprp7Tb zg$9o}taTJK0+B{c{6$QykV3H?EgBt1anjd2(50ZK0-eO4>AcH;p=~zrS)!58L~q5^6iA2$CF7JflKC~>xPhE* zeFA(2qDBc6j5((NVdQ(n?=Fc*EpO1zj&1Ix$Tf+WI?qZ>8P)bs#<_hY>8Y}nNmv+n z?R^@tKpBaVW|nQ`pI}IVhXYo}S8tdUuH3X48}tL*-br6(S65ca2yN?IOfx)d6Ny3wcvm9}3< z-nritd7l;=^A4famS~*c$)4F5`-JcJ80rmkzJakGnO-xK18%g%w>5~`tWdsC+NVJU z4jTC}SRr@4mh(b=CW<^fQrpt)K$z(bBf_pD3o!zv`pn)eRf80tlq3!-F{r?F2be2% zQ)aDi4a{lv3hYiu(lf!ZU;|%#TUb{qS9m{-0XOpM{~mkI}LkC zWarGRo6jo@%3Z~Hfu!?%xhH{`PItqeXF~7(k?KrWN^Ql+9GJKv`*w0?hUvdk4cz8Vnw3Ui;>i&dn0$xRTYV;HF+&pLJ=q-iF{}{FPQ;v) zJH9~iY~$k%S2xb8Jt3A9%x=x)%}6>F_;RJ8%CTQ#1T4ne1`qZG`tDRXyE510?baC+ zjQ#9{}UXa9_$ZagbJ3?BjjcncouHu=h7EJ+sH`KIShf zF1cK0kev&3QmF#t-u~4)WG0}Ui^#p)Bdb^M6WS4moT_7BaIm#=KFxwn0a2+N@$u|i z9m}|o=B@Y+MjkVcUwXG?eoGdao`WL0IfA*CkAM$J<4RhrI=abp$~&7JB!&<$%2qoZDG>-IwY@;<$oj=8 z)1)(_9b_XU15FizUWS_IOA7(ry>R{wSH8GMY11xUQwTUV$WNKEjFNCg~{Z(=xU_k*iXCSFB4n zHv1I;lQtqx=xq1|%XForVMgXy6mR9K=gRdNxnRgmL^3vPlJm@kC~+_!D$~RBxDBcW z=gr4TI-kXYqv0+hJ?T zDfm5uE4QHGs5XSUwNg}qoluhHe!A#w-`=FG+BF0wC^;3o6p7y+2f#r_vlC`uQYN%I z8HAB>{I(JYB&v4ddWYs;)%NkaKgC8-s^NUPh}|dr5(Ty;KcMOfaqsi9{&XyYxIoaf zUTt7iPqC6(xO3m4xF9&KcGzSypAb)JE)BY_b5)#IgC#mE*E@ibmQf3lHb9)Hd5|1ecXpZuUS7KNZTHcSwARuQC+Awtnvm^ZP4e(yXV*xs zF&10S)9jiTq%5C+kPnV{bY6@jOo686jqbkKL!34OFbmZ)862u3547tp(>E|+UdK~! zIniV?k5L<3iVMq!NHl%|kEGK96^J(-u(QfdGOM^NtHFoZs-z4KR%Rt*M0Yt-55_m- zs!kk#wl3WaBBu!%^7!d4B~~IRqc{CwJB1S~1HiKp8!FC{IjfyJp4Pr|-d5Cx)n$Syk9zsxx04CS{#~+^6;(=KMqTDm|X2T$|yN?mClmBK=nJZ zZ_T`M6}Gz!=a+4qRrCQTEWBbRy|3Js8elF^Pi@AYg1~-*t8}Mr8Sekl;5|=hNJmzPpS}IK6O0skdh!)`^+md@~=$K9uK^ z3tcASaM_)C^Wp>^&_>Qs?xxNNDfXuDX12=i;a=?o_+DSU_2lK^B!!B)A*W`&fqJWs zH2M8eK3h)4EF)?426dz9EwLwyi|LKLvelG)gH^%D$xcYcb{oA+G6nHmBf`5V;U32g3q|lwu7B=2_%4Ghb zf6sR$phQ9h<8}KdCQQ~S$}M_NHrk>QZ}`Hq&*u)Vr$43&;H4(3VbkwI(zSQZC=ZL> zM==*6Ne1ZVludVJS044=*$nMi{6f_ms7PNnwTo#Kd+%A{*G#;ZfYlLU+6qXd3yxsmI%bpKt>`gaEj-imH^=P9E>GiQcFJwzg$h;Yb}@3utu$ofT>gjwp5ut?wWeLkVE z_vQc3+?xl~xUcWSt9jO7s6->muxhm$RvDVhkRh=NQEN(Q&P=T$DN`AXY>AM)QK7+9 zi$cZ>QJN(}8k7dDTI;=@?0w#IobP!~dVlZtea{~bwolLVe1`jT-`9QJ*KLLj22wld zYezOi&P3xr#5q4kqRk)ZToAXwha>zZn;{1z+WM6(f3*)OXyYKm!)XE|`^Vg(9XxC{ zAx{SwmMpf%YYy`MM9usl&nKEHIHRx&ZOeWUK?92cC1@z>1>vk8%^GmE4Ztq&3LwD= z@AB6WG!QybHiJu(DB2d%})<{HHVNXY=Y2J4B;#)E_al@7R4kRBp1 zDSOD*AQXY*rX|#ZyXrHhLhdNKA_ z!wtNf9b`qIo(4ibetv)60eJ}2%p%%02P>5f;Raoi@Fai@{5aVFe86Lb4MjaNkxnJq z37DTjo-zok5Au|O^)pD@2MQX&pG3XtkIX4yLDBjLW)t$aWSj_GCP);3J&A@k#J&ZU z4RyfK`XaiRl2+iM2L!m2P!)qZF%b)*j2T=3)WqS0LoJpJDTMMA+MI)yhYk^hH%?T8 zA|;9@SfHUW$ZWhbO7$R44LI{h-7*UDfJniDLI4seD3bMr4jSwU8Z1Dx79bpYT_F!o zT*jaZg17ZwM*<)E^^wG}gv_7Ak5h)U3T{xM+Y%^Hpd*ny6E)z2+T9{ zMAN;$Ze9cvV3na{8w6go9Qn^P&{80%EA`ZTr=$irSPnJ2wNW|ml4)d25 zZGVJLbipO+I^j!^Jrgx;&_;oBE~p~d@o>oiHJQUjfrtWGuLWn?U~K|sbC8}CI7X-~ zM8|K4!~R+n31v5;fdo|lez5t$tOiRHn$6g>bQ+m53d{g@9xTbv6& z7LvIISqU&I!t7toegy{fPW0q&1*p2HWk3R2Z2b z4?!+SjzU!Z@5q(l_c@_P*3<E#u61{Syu|P%yX!8$d11kPu@gYS6rXcz)k&VPf zLiA08NMirn60?M6t4Oxtb|CXjP`F26y~vyXIN1Yd5E44@Nc*$zpl<-5$IDs} zs$nFUF^DQ?DS# zutVqw1=I~%v6e7Qpm35s)1HpnKn4=ozgYW@{4luq49JUsa!#-jLFz%bdW6bQfCjmq zZZBlQko1(|{$Tv$}ygJ+C#N|16bVZ_OviLOk5yzF7y`|lMXl#sUh?(6J@(Ex7n(in8#!K`@529fW-PpNWpf$mPRZAS%=t zB>pXUW`ngR5Mp$MR|EU^BSY*A(~TS`1c(pe$3A}#~5j&A+qz3XOKt!^e7qte1++|l(185`k=`F#KtvggGpwD#~12{3{{&`|LOfB`*KV1La~-9a`I*a18lI2!$X2&V)h+(0&PMS(X@5`Q9X zggJ!-0T6`#Tac)mK;8lBC4`&|+3o{#%^F;608>BIl0YtnKLjTS(IN0ylD7rd2~2HN#BDDjt;r<{Lq-BzwK$M+8L>O&0 zezAWUq+AEP<57r#s#CalzuF%Ojl&?C3|UVLIQ0EKHSirk0@{$e%=3NwTCYKYQ6 z8o2f7OGNBjf_n=lA`Uwu9{6=6yAM_f&Etr7{!vX1JA~*3$Q0r;7qSXjfM0{Xvk@Rb z>lcCiU$;FFQJfvD6>w4D$z*Rk=sg6!KWfUL4~V)=VunzqOI%KH#ZW#*mgfbZ9S6+` zfX9!wCD^5qxIj(-h#Z|1i9n!(rZ`A2BZp2{6GT1eC1wHk0NNyx?jeFsGdMehE{ImB z?n4Oii=CRmY#Qv%OB68;&YEDgf$Bg$<1b5%y@=2Zm^`jxlq!+z1VS_%;R$~IQJ)Q< z)5*cy6qHi}OC}!)#}pZ9BwPFQf&)LSMf7{A@Q(pVfocr?Fi|B!_Dqx> zpnMm391tk5Z(#%-Y5j875WObjjiXl->Q2xi5S;IR9BjavDlQNtA=<`&_sBRNMREl zhv3qKs|C3(7%)c?dy86!LGC`-8gzg%g_toGgm_z2%OkB$;1Pg3$@CK-+BVqThiBjl zfxH-|3UM}0CD0OtazqDaL{B6$gmf7c-*6-SYPU5Ul0n`ME_7tB2!ZLpS(nHo*(2!2 zNk_7ltkMwvI+8hX;vrAjzi)}l5M3Lf*+g&)N#bmb9Hdu+4D=xH6(ZA#gfOaC;B^wa z4|<}gen%G8ltzSFB&&i^7KCdHE7`w?aIUe3php1ohen2ox&YKc&KscLj~oCjA39OM z973xM86mP60$2=+2@-i|Faz6%(2#+*1TZ2*A>D zB2~7Ag@zg}Y%?nQ`>}BZ>|hxnGXwAjJ>$uqIq0)uKIk1!w2x)b;9A1C0yjYs608Xz z6ZjYw@U+R^7StpFia3gZCkgZeqOiahpcvo;+9*kEPh9rkOF9C(`jMjpjl9255O)&t zu4E@09yRg@z~n(pATo;Jw>qJ*FC>G3f{^uF#xsFMARR<#ixC+`iUI!iL(L? zdf?M&`-8d^Y7C=$RDLO~8#A(>7-(Nq_F976Ho2OkVzEHuBt zv?pY?$>L8Js3qYdWVj&W?6=fb$VR~qLI46ea%Zv~k1pb; z!3q0S4J)EIH_QQQ=l@z2bm_CgZI8lzl$q1X_N~RBcRY}6=n>*8&HJ-nxT;b7j0g#A zLDc1u-S(g&4)Ux(b^*%(Q8Gd!9eap|zzkOlY!DQb%*Z@O5Z=IS0LJq}vjH&#x|=|T z0h{@6qI*y&IWLDqsu zgOHq0lmQd9mxQh*@tr8{!L4NJgxm_Dn?-^OK-v_!U9jW-%G(dTlN~+>D(a{Y`hi41 z!WasIgino@2_Wi`?RcWm?BKj{H=p;h{;2sexz$YW69 z0r>F4zeSymDdHC}1qo<_D=0eT-!CG6`zAugac;_#u38g31;x&3J*h(v^f8d@)yq1>J%clB@2#LY`c z4WKp`kPDfD+P{x9N1_EJT=?VGPzn2mwfhdWz+D>tZ6qqY(Fp}jdZD4}NX8ZTw~+&nbbk6yYg2H4;eFv(u=mKa?>|2i9{d0i z?tdFe6a-)wBjy4aLDq%r=aF#e!2)K$&2ffK9GN=$zm0^(1qcR^DS)dA8MuM(A>a_e zYEZ8MhAMhek=@0Vf%5H6(BBAwMzjw?!8Y7ZgB4D-oC) z9AC1LNS&fs1(H|4Se*g*-wat+fIYuj{|PD-<`HlNnnCsZLui0O#Lt#6+UT=M`kg>v z;hqxBRQhwS4Ad1N;edb;yU>E{wueFyP8BMipqxrD$)IiluNcyFAOq1!y&6zE;rSue z3sn;W2SX(5f;2vZZh^{BG8K5Vsu-+?fvEmpEHwm>h^8ut?+{**buV!Fc_hRX5VE7N zjVMHQCff-=jYK6A!v#AKjslz&OEPlsAHRMxb`+a%oA>fRQQzT0pY*XRhxgCui`+~;6@6A-Zm84Chp8DvBU z*B_n~s7=4h{U8{|zBS6fFIZ&E#o{xAQOGVp~;3c z8p%$u{%ObK=7p{XEDVqd6x98~$N{TbKvwl%PZ`R-kkfHM0T;@mNMnbeMnXgnsE4_! z6I2!Oc(Qxt=aDGk1488tI1KDmQq1_Lkw94ndY2*j2k8am0H8@mV8#RPm;f>;L{*UG z{VOjB78s=M;KA90r^9e0^BBR<1r-6<-Vfy>6vmSzn#+D{~+dDd$d@#rpK<*HfBBI)i z{LV&;@IhjXAdw9cDFg==MG9DU=u~2gU|gWQgu{v7p_+@#zlD_RAa5j);UPH%gyx5@ zIq<&H5avQNC7c&$2us1k7~oGXSbaBg@SE^h`kAW&ncGnFkuGs5T^fCe$e$apB{C z1hNG35+)TbWB@8gw8KoG@(7YBl0HD zNg;V#I241#-cZo`FZPze69&Zj1sUpJM-Du;|7j%nX9MJG{`|~eu+%_lkw*j-;fI>DojD7hTsdhutWw5 zZBLOTfj$-BF{JKEo(WibkQNL$#UL#hiW*SjfU;^31aVdf_AlaKI)rG@fW?6|1oCLA4gR2*28$3(W^nYmb$O!bAT~KqET@c@V5Kx3(}N z>r4n3X|Ns!^yQIvLY^4(3#^Yd(ibSoAQTR)A?ZPeL=d{AgUe#N4ElWbSBL!*HjX_6 zwkHZ@aX66?BUzQ7M-DuS{AuJbV5u%BSw?an6;u714OXxxVDkeSLf8XwC$hIi(`20C z{#HIjR(inPW~3eA%Q^%0`%9@D-{}G}Eb8^mXjo9PXA)30fFQFUctXFDh6Nq^(qKGb zp~*%LIu=A)170EurcwO|tBHMxzV!q<8&r9e`;$Er?ITe0XZBYe`oR4mra7I71iuxP z!==-xs3PT1L5r}Ya;%vaW~M%-K0a2Q|NLC0r4Nl|>O-gcSo?6P<~}SomBnDvsFrLd zhfU*h0OqhsMgw+1Ck!T)LF1TF@ws>b8q=C;&7!lR;%3IOVv>wzp-hOu<}#>sABHK_ zoatjp#h1{i=4=+t2W=pHd}t)=%c7fc%-AeqAkBixq?!9rSsc0@zqq5kROscsRhe-u)%AqpZ)>dpY3nrvFNM?;?MdNaPtgWaPrffQu!8W&~ zLS~ACUo6b%_zx_O1&id_$b|ctaxAC}A1;?l)N4|iEK46M&4PpKd^j=4`;d%gnKLbc zEKq?Ev#DH~Ih$(jV{J*Ln=@HVgsw~rHpzFhE$K`vCd-FvPBUjw%~^P9D>Dv_%Ce^W zAn(crE=sauY%5bHlgnUJiMhvUYjY~g0_)9XnX`$s9Fs{WIah3Jwz-cL9&2XJGN&R* z%cU~8OdqO`l_l5IoXa)^Rqj83Hya;kMPpe|k+Q~$u`DrbTo#k+gWiuI1e!CU%}+L( z#f1_Ei)!k_U{K8=gn;ih=Mr1S)Pl=KyI-=Hg9A-Y45U+8bQV^OW^P5brgK)j*Qn=RdzWhhR=XxPxlSrdboG z1mBIbNi}1#X&kzbCDWQia!+u~IhHK=!mtSzcsADpZ^7cSs6JfufTS@kSY}Kv$+Imu zOdn+CaJ*n5%(-+^>@^E(*cKljwiV7OgH4jj=UAF@iIyQ$I@cPu#TqYd&1S(mSaA_d zVY_k7N&F5D&~_BtGEae?C@bY$_U0a;O|E0oI#tMWtIZ%{eIFWLR=YW{ofna9Ux= zSWJ93oA_>1E7%q@bB+&wXT`Q4nKiDD1>4G;jr9hJ1{Rraj+1V|pu(nG(l{ua=aR8W zxjvQ*j2+?|&8==(BbMGlXlu5^q0E-<}i5;_ABn{aw0_j=DyR zR|RkaH-wJYH~EjR9vHBm8}d&tAH)n{`g7NELqezN`-BAg>#p)=uH>2o1+3Iv8>q_+ z3i4aUW)h!mvOa)g%p@La{Ex}}&(8^Et|y-$xK5U)*ez5u7EUaPU_P)L*pH?xxD|9J zozC|8#{~cL1X&XkM5j-t6LU293Hod4*uKnwm0Zp=eUtwOp91+^>_-}|XyU>|Cu-c4 z=D3B}947wOl1;ZT_xXDffBF=&g{J0{P0c2oG5%LRh5cVw|Gz9au_ykqLH_fUakwF? z{%QUHd5`_$6?FS|+y9y8{ny; zK6U?ChyQmEDve9Gwq}}|QW*>u?r9=;fNzN_+Q$?{Bam6(np^#csk2;Y%9u zU)kONbr&Fe3_b}y#hP$XiQ0E~Sr+DSo6O)aqD}}=*uQ;>HDQ!#W(#RnlTGQ9&1nBC zpYo4W^7rff^AY+#cyI>3+y0jhDhG|xd?15CwZwgn+ua;4odq0AI-6TANg9ce6Y(6@NZE3;v@j*|p|M^qQ-~w1rrdi{X zn}}b3#RY&DVXbR4hsj&A1i- z0f-MGep+D2s+Fq(n0|k|bALNuoPj!Dgp1Ta4NlxpcF3wA!pHghuiTwnh~I9UjJbew z#Ka$LHwH1;Yq)E5xdCt&LiBBI!p3L+L7~hvndxM|aO3f&-!~+U&N=#?SM_96(vno8 z*kOmy*U~y4fB6#reR=VhFTQ@R+1t3g)SOLsXC8Ij(4up0g@n4go!Gj-mB$;-pS!d7 zw;iF?5$b72-`h@lPSY@xx*sAiJA8jujzGv(I-;l zmfo2+L4K^@Oqw2>`X=Fqh}+oe@mcg(QZQzp#juldcGl4z>v>z2Hs+Pi>U5nyfpR9Q zWm)kv6LDczL*Yp|d$D7b`uXgpH8X9$WmQh@)H$aqen{RfpsH)Bk$4y@UO&8DUjCV+ z?!K9I-Fjn8-pmcN>D>HjQ>xpV@BTLqb&svcEKZ2AZe7l?J=fTdUR^l?;=Lm-PN0k} zEWY;rOw^^}5lc-o`j)hG``vEnafleDnxJ1XY^(8lzcfnf)NStR_~$z>j-yx<&bwA| zCTgE*uzO14-btbS_1@Dns$Ww>tLTjv3u@%$@;+Wvk6g@u6wr67B0K9A!}F~DmqV?G zqKC|p-JJ28 zuUWnEMc4X{t7w?!=(RPAbsC(~k=#d_e&fB0zQD%iqG&GX{mP;F!>5#%Wtm2O-I_Zj zHYe()#88QLacw=-T$#$|BU7p}cUHBf3MwbwE08tVI-@O7HtK|ZvTl96{OFLB!svTG z!y;bk3gi8kO`6NNqa!&);(o-?s-77ew(2~+So0ultg_?IRVSUsY@M`0Wb#dWgz7b_ z=fR?XiF?2qf-blTRcp(?ad z+rEb15g?emq1W{ECP^>FBQq4X%@fYtp{qW^Z=`D3ByqRZ9j-II9NH3hFNV`}S|OE62#^+;|tKp*(zRV1c@r z!MT>+uNIqEs23ZiUy<3T6uEZ-B`Y(0)z;)I&2NRHo|fylIN2)P+*{c@v`C@M z(IAYv)pX;w3!*v0w0k28YbUpjcswztV@%0LxofSu!z6XI%C*X3#NF!J5|;KI-!ZRg znP0xn5&IcrEgSpX`I09~J_{Wa4KFzuBHgXBL%L>NW8)tA+xnNz;d8u(9nl!R-M!1|H%3OY zv$uh)-M(InMfrudy50$_@5PGrF87>s+nTSiyuhbvvHF!<`*~a3$~wliYPU(}aXn=( z^jf{PaY*NP4ViUAX+bsr`iRI5W6Ht!{Nh*hsG&yTjJjkA8@s)Y>*}{Pv)0m{Y_{`i zn~*)k^x%^FN3!lI22?s~X!nlp){8$ zTM5**h85;q5Xw3Aw#W3IoByu-N%>IQ*S~WQuNTsmYvrvm4w~fJ%y`i{`8#`f_FHMq zrq=8Rja$uY4hs@L2~R8SId)&}c1=Y3ew9kj)zXv2p|dwhTzls2_+IMuY09B{g0V;P z4|P`rU8>i_1JahY$Qs}CYP#3Nurm0n;3899HhI*h2IZGZ&bQSiH1bE-ykQsY>r1(R zLu2$w&hWi!=D+fF$VeBM?2K36ARWsu<&P~n*ebb!+T+Cc?qVIe9PeG#AoV(lGW*ec z<(z`I8B8CZ_T6|L@nqdc!Qu(8ye>-n?R`+xTv}{bC3oCNCsk}8>vUXibjDKR&tb&mT_vYk04|VxA zB`vuky2Qqb<$W-)dqiHUt=0=A+VItPFJ-mpl}V1-vfrr8%}48vRoqbNiF?XFgf+}! zo)yF$F|gXx+_H?HkXBkd`FgeGV*3e&itXa@L*y)HukgMl%O6v368t?(tW~Q(v-V|x z_0Exv#*6Zp#}4y$Obl9Lci2Dwq{}9W{o!I3&!)$;`n+kHa%aqMdp+p78yb1Cxsf+Sk=8)M~BO(WZY^D!eJ~cwxiX;o28mRXx^-8R_vRRHp}bU7RPPeJ&Oa zZ(TENsJ6>%=9$XAj6<(i8DUyz08ohHrw=d`F&$QQ*(F!Ia;>3xJLPwvF-U|H`E(lBwvS2 z;-?aG-S@p?uKpU)vRZa(ufRjI^yP(!yWf01nHX$nZwL_CT#BwT4EtlAt;;u0p6w1n zw8k5@Ku^`Z;=G_?u}8C`Ha~cb+9=WS`6jQG&*f!@_iay`9~NCC(mtIjG-oP(mI~SKU;A)uWzeGCjmf5S zdpzIig-*&DBJz@BIqlwW>v?VxH`hb?e(3QH-V$5))kQBzzO9sTx$uj*R7Kl}&MKK8 zxyL5Y!?y5!t0FFjerz1;!8pNRD$Bq6K*u2OTJLn#x4fN_wTjmtyxiBZx4SsVaj{8? zbeNoag{{w8hus>V1fD|oM_#Kt78Grg7<+s)FJbt*oc zn7DrIhe@+M^P;Ejb4+X6THO~{v1g~GaI(_`(~s=sG5pmEcGT~oM}-<+FK&70DQxH4 z8YGA~8>Sx54^Y$inyk+djr_wtKf*v5=DFT_L}^0qo->7Bu3{PMk~@^)K^0yykyd`z zaU<%F?@vz#YXqLYd0X%7vZL19%b!j)o@o`Qc&{`!UUyx!UhkFz6AiM9(_>{XoTbcm z2(RcIy46&yY+pcGlF{?7EejuRIrOQsQ7}TJ9nxfSGJ3PzCd!8oT3;RSbS)8j-bpWM z(wQJ`-6jy65m#vzbZvN0!FaUFp;dg-oDD%MZfmZcSHtDr^Ldq^J;vcy#?&Uxn#8|$EQBU!k97G>)^zOsVPc6PXPeHbT}2L;Y?lIMa` zZkr$2v4{~Q&iMYw{*Z33c5L2zDR&R~?9wx`7osV%5B7$A70U4QPa5Yjf|m$adCMhK z-q>~FIU~*9({OI@%%bGW-G4fV7nnQoYv%5dx^jEHpZ|^p3N!q8PU5|)0arg<{lkhn z#_I9M&{69RZs**L5QXzbvzG7TFO83UeT@tVdt*Ddl~dSVZYSoJtM2S z_=}6gm+RR`%-Pp_#?SN4;G$ zJ8;zfQToD)+>nN8zO!%7sXuy%6(f7WXwgzi$X>S5cmE2P*54gcMT`>4T-s;_Z%){* z65({G-axnQH@0idKk`}O#M$>Y~sPfxO>W3fGOLUUGkWg!WsbrJkLj_OvRO>{?i?SEeG%lo43XNTAo|udktxqjX zKb_adb7Tl*uiGE|Lr8yGCDPQ>eZxYjz&64OgkeAbWcVK%M zEzodB<+>9S7R{o#@6~%RS29N6SGSWf|5R}iFXq!2!HE5*Y8--v2{S0+<3=|$#Mq`L z#npOjoa*#P@a)siHS}DoWr80KKTvIZWN1X|4R;4`eQ#xrYdfQAC$tTzab(t|R`JbZ zzD?iHFLV1OayiwWD^NbudMm!qvwnt}__OK4LybD}_PcroD&Ki=MIy0%y$AWM zKB@XTk<@3dOnHuAVtp`=({lWyfaIlI}ickc4OB+A`Z zaw^M-XV7K_cuOaYnp*sEs!YQE4P`P@)NO~!#v3uWbNQBh-9JT!hw8r{eeV8j)c2hp;rbQA$(6=$;+VdtW8aHK>$}vahaUIF&cu;#AO$&%MvM(Gb3 zPp{~Df8-D)%392Jl5D$#<@tyAjZzq&yfgfZC}FjI&59GGwc1tB3k=8%S-Nh z9}`!(5pzvzsl!&+Zq4r^&F*BYws~sBM<4NYu6^e@EUD6oZ}c@Q>Rcjpqd2HWn15v- z$%|^8*i@dN+o=<6{xtDXlDx8yrh0^0Apg#``yZ>TU8?z0(jt5h#PgkM!(56*(>We@ z1hY9hZLzmo`xXq1TqS2`+^jQe>%^w4sN}o7FLgy~`rRhJ9~&BsKICspQn_>CerkYi zY#8tNil?KO1Dg6V(k?bpQ(Q5&FHA2ZTv!_XMz|`zM!Ln*IatU`$?Hqn)7e;hz;+zP zsZ(x+LfrFEztbn@DfZZCg^C>KdyM;CQ8D3KKp%xMufQQ^>#p*nO2#9%xk*l1+k14r zYDT+4%dO&A4`V*B=DNI?dvN~fiC-xn^dA|UZ);)f8P_{*tjIC-G0*9UNPL$7`K9o^%V@LVZ(_vbE=&ShbO=I@jjhGAZ> zZ4D{3>ashcgB3LsisT~mbp)Ol!rrW0_}a3(49 zdv(azJX0~<%T4J8O=ZGGcir?dN?8vawp?_g9GS0OJKbqf(&oOYtG76Ami2KKwWRkJ|t$TWu)DJ|K&ZAr6zVr2cM zk1a}mduLCA(Xc0a-$HuZ?d-`_nYhbk1IHihgXGjK^PIg{= z{=G?N&))8r)Ay=;+a%F>qU2Uv+%#vlOmAnaWLj?mI7gfAEuFCySMk{^gZ2S-y>ffxBJ{RDeAbr zf7@as6N@pXA4_E?otzq>w0!RGjv9ATr$*jRvD-_jpKHbK2-S|b+qZK5nPl$roiQ)y zmzy`Z-ARw@y_Rdc^1I$Vp-b=KyKaHHi#&&hul&+p9Hj5<(r8wnIpovP^62vJO_iss zyq>kJitd#cZHZx-CYsBrOz_*=7${-r1Y2^p3IZ@%n<#dheC$9mGS=CFr+2xLqt)w$Qub`HVZ|LF=Rw zhV@eAm-z3xc3if5nuJQjE}qh^Lhq$)vDM2(eQEV=Ile2pBoucG65L83I@j7p+74}L z&~XrcXbV(~sZQy^`(yzZjMTbm^&Xyt1tHD*KU4+11X@Q4jJ` zy)SJzJu*x@{)$i-KJ#|mHDTasn^!r{r7ryTlsn9%ZyQ~he0s^&Nk`Ro3$(uH3}IE< zJso*uquA@GP4m5#pO=Y=R@tY|ubF1G+sgZRZlvT{lVPo*8@%vnmdVL%tqk+;>hjx@ zJ4A|%gDvs7Jk{~p`-lFQ%!J#t6t!8@u{Bi)`*Ak zP5ar*6%QL0ew0g9+jgs{?~P~n^L+`|MHO!MzJ)DZHtp)8KsoMg6a7abrCBEvD~5#% z7KHVMeJ(G`&)lyTSt$suwkvz;buzn4Y1YTlcDE$5X4)%A&D9U^yghAeN%hsmVzNS~>g`>(YyQc~p z_m$Ur#YbLI$;o!DaEQDTV^FH0B;_DBL(V>?Iy*@%VrgUY?!Jq!MF!?ic`-?n#?2!6 zb$31WM1FZb-?dK8mFztsUNAeO$t2xP>*lud`uStT4d>rTkDR05rZ>x1v`s<`q0)2 zh13es#F7y_C;xWYwvB5-JkscZ<*)^%^ZZDLqN_r}0GP?Fyz z?xQ9M+vdGsYgp8Qk&Z4sw;Y^Ry*4~DDVdg@zCT}Y=Tg<8{Wa=hTk4NzRaB%=OTDFo zKbY4&8(;ZyPiRze@Cf};-|%%gH95j|qfTA|b2x8NYrDi}cAq9=>1wsQ`!Ur;`Rn$t zRz4_uTYvSRwx_cN6009w(?6|o!J&0|;h9Zm8>D*QZJZ#PSkja1)VzEA;)@-;$j_(p z9hyrdJYVyCD_aacNSXW1`IfbVm_y5sE~%2}z>}8Cw`(3Z>=UaiF~1q1`gn7oe%sj> zk9it`{JmSgg#

>Yvp|~X6yDxUT?flxj)t$a%;QQ z@JCYhxtaz-+0q5p5jKrAr7a@uxtXgki0A={EsObM>uu&d>Ih5?{8(_+$y>EU-qXSV z`-IgM7XHs}MjVV;U>Fo<8n;9I)e1YWAty^Zl5hXkcWcM)CD9(DEomp~rkKxkNN&`r zjww@$dah6tbyCmC%l%nrsK3bw`O3Rl4Oe5;G+g{4Hq?H>iC(xfu(n%1Ir zp5j<)^it8ew8r|RfWAWyWI7AO&!y-^=dB;wP6^S^m~nHCa))^J-D$=q^qNCGP8GoS)i}RHqWF# z=Ezpt`EN;6_YPfkEV63uCfWGe($UrtdG%fowaWH-9tu@VGO1#T6w+$GiJczK%iMkZ zO1G)Gp0AO!zft_^_S~>?5GCuP|XJ@+77R%HIHq`MNd!}rkkg$4ygM=X&_ju+sr$lLl~~N! zdQ{|icIuJdEm~cbS4Vn$;i;}W>cLxeu|xU!j2ap>wS4hjb?**pg4?qEdAHOvMhAZx zb@~B+;c~mj60N518Ym&nyoawcKIN^Lz978mTy;;y>6<3CC+e25`UGk`VW7FWqWuNx z;c*h|ZLOoSzAEH0o3d8$!wa((#B6yzw(R~ZUyWyrWlPjWfsP#yR|YxDTRg}gYks$F ztohsf&&q6_Li@DN?(keT@|Z%mT-NuURbSm7>+Typ{rHrqMkS*;())Bvl|0nM%aS+k zX6~@HEUxL=b6~Lra+p|+_$)7FxmW2|uGp1c4YRNRyG2b^R&pEqMVsVH1iVT}aHP14R zd~BDzlkN8^lHpd7e?Nm$t6DhCR?+Bvi*5W!1-H*a9mm5nWzyyempIf<_k6;W`!2X6 z#;bZpqkh{Yc81BP2kha;Vb?cT;PzaUc*6X9>`;xD3iJg}RXH)Za1hHj~ZcZI<^%2@4)7NL%lJck== zv2n++R<&7CQE{CQ?+VAaF&b+!UKC|z4*Rsgzej4dAVEca^*N<0D!u0K+9eNdrkIUa z)}1{n%8QdFI;ZivOQd+s#@1}|^OYBG)J%$^DjL;-Kd^ZPol?ul%Ubp(1~e=VIO>n$i&k-0>lq*%7&-CON@ z1r)Uk1M3LADqd&rl}*vtL|txQLX{JG8+VJs%<+~h++MgW@UJ*w#PLj1lE^j~a)Pt= zRD4}C-(!4G4eP5YwOM45x|vzU^KVOEB8q->&&@hJQf}=7AzRd z-ZthIZXEBUq8_Ikuyk6)ts_q)4(*Y?nsr!5+hqkmyKtpDO)NJyROEUw%#~v4H|Ee) zE-&-Vwo7J{l)X=UXBIwRxFcP3bb>O!N7Qv{dRT=@p?mj(eHFPfVIO2yI0(ndE3Mh< zSux4K!qTC2NL^9o@+wq;8eN1`?s|Vk%pGwL;8>(EXZiy(qKZ@$@UMv6Yn8M81ex=cQe1+D2I| zg}b;-xB0xi>7AhgT~B0c^NYjzixb^%I>_;FSapqaULT|2;8@_|ILE_#L4DQTO%E(+ z%2kSMALqt>ox;#45=C@S9>2T1=ZuHT`+W*kEVihxbXjjrhNfGROlxLT2wQo+@kvWn zjd#Ar%9Q;ox`&lp^S2hY)~rl>_{sFEkf$%OHdpoa2sHA~xptF3v2ed2*WsFby`=8F zPrR+N_ug%HqfUAd6wi$73y+@rusFbScTL8d`4#sPRHipCu%$mMmMGk_wx}^vMYmJB zMxt!TE~Pu&r<6a4RT^rG91XSaEQeKV86U%5rb4kvzpT&|O*S?q8Mdzp+&!dK0BWb@~RgV`=k#>)ChSX%;p6 zwD|VrCANL{|2VE)CrN3`jE{5ajousM{%%AxrC4?G7i&$6M8{E8+pV<5eZRl4x>Uit zV#;-si|a(x%&!P~cv138wvUrmY8-y>D)-$E&BunhyWY5aXk;XOy*7FC7O^{wo>|OG z&P|fKkJ6^RGj-3tTbrdfjiRKarD?59DO7zo`y!>gdb5=)%_%qOkyv+&sj~Ns-atck z-?2;u6@ll_?a@!RE!;A92j3)K#%-B`w|ItDuqETQOiTmij&irx(kYS~HP3r|=7gw^ z?bwWkb@1A`J3d( zm8+$6>sKF1zc$LQ`^=OZ>y#$Rbsy@M<(CN-+)uJI_ZwD{{goyuYrSCm@yvZ6DDDCI zJU^{n<5$awMb53WerI@aN@m_Ddpy?eQuWv_&15r+jAOrREIwU1_cw(df#344O25AN zb=!PpK5ts*(aJ6ula(deH6Dv=Cl(&PTevbL(eW#Cv>G{ohEO`4Y8O`7$>&kJ4HqnZ zUn*OZ@qB*yqxYd~)w`lB@!C*}uU%>*H@;Bl&ZEV~$yv`#Ec@jE=8iynefeou-ONCs1QET}f%#@Potd z|F)^ok<}9&9evn2r1j}laCu(3r{a&{vkmHv>(=b0D2|^+F{C`H@bxuvyfo27#vp7` z{k{-}^FxpQb2I9lhoo5RqyeO=p!%7b1$vG5kp6)N$`&-r7=x;~( zIx{#i!CN%PP{Otk9XI;K$9d!2KR2&5r2F}n>9$q`ou|#7EH=;Y8+AZ7A$hO z^rV<>leJ+y%k$zxu~bj#q|~4^$`?K1?8pO&X(>#D)0Pbu1)P_y_s0#}`>LbYKqkIN zzd-HaoCSV{Y;l!8yjC5#n!-GNDm`Ihy>rn~_ipjlZO;P)b+hdGM-ZnuQO28s5Msl6i`UhF=!IS?1E;~!O>nmL+< zdzZ&dykXE%d|UZdpJ8ytng^roKF`T>pQOiKa?(aI(b+IGZP|UFiL0z5qAzYc9&C2$ z@w;4i`A|{Vz7IDy%Q{4qY4w`$q^Ev-ouwU-zB;t}s}0xh)U%BJu$Q>6qpTJ*icOt9 zD(qqI{(bA)i(RT!8l^lw=Y6w5bgs;o^foyi%TJjmMqH;_vnu?JyDWBI7Y(Vq^WMFC zl!aJP@W_uXQZYzREDy;DeMNJ+$#9I`0f57BtUqr;NnSxaYVDMG)k2*;2@h&ix+s{DB=yA^Z0> zifm0xsA=k+`I7gVfG@uF~dD_hMzFFsW__GM-W^R1D|p?<7Vtd>5w>vrgg2IO!x6^9#f8toL9w`G2ht8W+aC zi!re9-z*pWbwr&{nMC|@_ja+u&C*{YC6ptzir%L^k@#ZA-DeoYw3GYZ*W&BzQ5+NN zFjsPtKs2)MUVJwX9#QTpWvK^?C?i7_Y`eQ}**reAc)V<0nAkIR;oTd#^8BDtcJ7MqL$`fxHVMNWnet&T)5=P9rdLuhJV)aij<{iQ9DnU zKId;w7|zo;^mVG_%{Lca??hDY2=MiAJ#_W?aY0LHN~gW9iK z^LEW5m2a*&B}b%^KHu8snd}j0H;1JXm=7a4i=Eb?Tyd2viqkVLxh`5HTr+jgaLWEN z;ZBWI7m1$LstXPz7=1__qi7{|Gg4mX%gi(jczNO@Uyr7y-W^37V-bDGC zp*Z0Px<6_OY;pD%%|UcAPqpiksSU-*S(qjh!PDKye%w=0UKjT$O(g~Xe=I$DrRE&z z*NbGLjT7?&d=p2o{4cDPtGGH=^i+4qm()v7jP;)O?UAM|(SF+C8!O{xF_Jbyy3$cR zXlGA#g!Nk0r>92xif-$L>Dy&gJqL&KF+o}?%d@adT$GKzHX5)HoGv++t9sm zL$KU_n->WirT>NjI+pYt*XZ`|lPoyEQ&uf?eUm|Zz{h_rl7qP*CVXTGUk5xc84 z&!7isTkTx<+{fwY5%p)!XyeZ^`}|!ln5A7THk^_^-jgAvU+6McU zI~;G95v3Ld#zcP@%NFZtqbwR4GWUWng~pcQ6TaVy$@K{no%w3Jzb`YB;#BRSFK%gBtN(e#h2Kqrq&xPWkhalnlKK`) ziS3Jy)+@?7KkEB}*CL6MrfK!hYv%3m5NR(KcpdEB9o=&+fM1rGSmzPQH&}RdZKhzm zvl>pAHOw}Fsb!Cl(K?9y{h~ViBN63RqoG3zeh)FAZ@X5<7Y!5%m4c8o@Fs%kWfqGi~DJh z3bn^AJ*eKs$7|<9z;1jD?BP`Qi`QsS(}*BnY6tE*P+Xc)X-ddy{Z;78PdqmbjagO9j#W4SrL#>&GHB-`* z@}tYwDn0J;lE~xkTF$fS38B1IFO=1}Zm>vWt?F26^ZXX6$EQ9O)JWSnz8v>P!ui}= zscv8WjD9%wx^RnIQlDbh+pyHV{_?wHrl<#5TQv4O3eGSQ-nZ3GiuS!WWu~YgyQV?v zaqZ2BHGIZL@yDlkyE$h2`(7=Xo|au}{O(Qh@z3uMO`CeZb*q-8ZO?&l$4a4fq>GRj;L(S5nQU6p{euRi3@w@v_8;2VPdXYCSSBa z*&FnSWOii-Q0iFQKfmWkXGgsxW@9g!7 zY4S~MHB8(6)y8$~D!T^AimxNL09; zA%D{@Q^4>YvUZEg41b59DGx7&Z*$_SZHqGy-M0c`SFW$j5_n5e2mf=`<16Svr)O@7UgZGjjG3;Ia=#AH;J8_pWuCQ z*3*-t?r6BgDCj8G>{=Ml+-(4^F3NI@$?v|cQo0DhX=dBYI zo39gTqPp1cdit??ufrWXZ&FG&i<#Xap&?$TRV9}1|8Tf! z3-8Rc5OFP?Go~VyN4C3fmKE@K@>}L7jdE|jvggj5HAQyeJCmBuMR%WX8P~q~w@8P* z>MG49b=Rfxofhanc~^O6WLeDr)xw$hL)C|Ed@va6m}JRhk0^v<3fZ$~tEa5llBcXu zmM}9UOS0ynQpQ%P5DH_Rv6Kl#Wgm<+G8p?{mh+zX{k-QNIKT5-&biNheXpwnepg{5 z_g=lvI_K+FY`)~=ScMYCS^`IKc}I&)Wy)#?mCNFv*8|CTpDA?v?#B#9I{CIW%KM3+ zru{u2Iw5yw9VCZsP%Haf7Q?>>xlebaFKr<+>B}&0b?EFlk1st&a$ak#?6nQ$R zeuP#N?#}TJC0`jaS`;Ei7t+8*_bOdh+%t=}HYwCwMIP0cHm$xIJbCURQkUs|4}mL_ z?0uZ%3+O~?fb%hW>j+th${WAgPMDI|9zf6`4JGpJp&S5|ne5XQXR_8)$M%x5pY(4X zAe>v5P5Ko=%#(o3bIt@zfHipmo^jUs2dg5sM)&9jl?zRYrwN?%|25xBtLrWX0Kc1| z)5Jq_p@V>jYex_Ktd08i0iOD%vw~C_uj>I>WgZBa^mx(K?RC4+D`z+5n*rn@;AePn zR-iY~wkr~(mUr_o7;|Kc%ykhX#1yy_QSY#) z<0YG`m@@rq)WfG^A5DW%-jAR4bwu7)*??s{I1B|jd!I46$H(qzSUL`1 zF2B!4+&7+FmfX6B~G|i<#3|<>8FSO zoYB(z5|u&41^Rti3;kEv`(G6iU!LJ@Op3>QE)v&y^R~_(GcqW)>Ut@Q8};0GC8D7L zkSelVFGYoV-_QPZ*zlu?d7)3XZ;!les0h5p_r$>E0fp)+;Cdq(OCm_BJ;k&Ck<`9o zP5HKD%Hhr^!Dfi<^YanW9)`ub9ooO+M?7@(Zz+g8;CyGXh_m!MKSOAY;reZvHcW54 zvWf2B>Zt^DEz|+PH2ClNJMLyATEQVYbD|lPJ@2w2D(-|n{R0Wl*SJ@51=af=`mtm^ z`=QGGg9dlFSb`|=V;G8Vd#-is_Qnm&8ejwvQhaaG1O_fH3W>yon#q7X{(U>>VI51A zPN`$%Z=CHjQa!+xu7e<7no;50hb-o67VsgLE4Ad@(Pio}P#V}exgfJH1VSgLCi_97*z)v`Gu48Ws?~Zn}ux zytMlJx9y7ukTrB&d6h1aIR?38_)OXXYiqi#g0ys<2pEsE6$P^n3YBp`fLG(}s5Z~yAaV<<^UZmK)aPvN_#v-_z}=m!pA{yi9TGmWkU z^QLFNBof_1iS1_^3IF6Mwyzbqu%$pXmU)c~D4#kFo<`xjYfdNId;S!0r`JZ<$$x5brD@>P6Dc$r=Qj0s#^x>To-K{>;F6cA93@ zapP#UJwtbfuSiR6{?XR7+(dHAqCvB(8L-{3zD+A>O?-4rBHP*h*j;PC?d@ar)lxxg zgT>w8UW%ZXBs1F~E+~r6kPJGd5^yIWC7l=iYE(A~+-$6=eIYsYmmYAuk;ex_T72cP zZQ|8_7rC?yz9>0F76XSIU0($y{|pKlrWho!rnZEdtsTNZ)wB(kH2Xs^MhisDFhOOC zk+=x0_Taab80%J?A|R9ZxAB{S;iXN&#Z0tHyG{-0?BFES04!d z*@Wj>K|ES2N^Jyb#aO&-ZhJCQoQAU~2c(ZA_>mMPwQG937XXkQTdKJD+(}g_UTq!M zObvY&DGZ{>te?4^QiQu>afy+l0oi2TG+4g^d`9QxNzJUudfBi~!Ud_=SG3$l+3D^) zmF{etHz821WyLmv?590;&+A9%1GA^>C>GDzlww5uw&1`HGHwgdUs-Q?G7<7@ zr;pD>LlAq{>6zs39f-{~MpbEdlb(D&EQus1bBSJ$d z`Vn%1!8-$J-I%8=?T!A)eG~cdx;B*Ls%CUz1Mx|i#|yezI$qOnZB8s&+SA>FX3EL$ zpMF2>R*d<4O@*gKV({(C?EMc-9a?bt-6FRkvSA$Z*DWz%h1fYs`h$cfDRj3))D9LT-F)!6 zi9;cfoSA02-!9dW8vlkC-8+{;b?(Eh%sVu(Mm}OBoA#HS^rMkejn<4kp*|9$KQXchK>8kT zPy3aN>^)kg=9=wQGH`uzBm5K7Hs}5mrcxSVi1q3e2;;}uxkCjV1~X#PLcaQ)i=Ydgf51=~u#vD{wEhC<)4tpebl*5RkC4>O`uOS3+B~DEt(8z~Q--gST7^PIQ zV@bAWqcF~!C^d!9?SQK<*f_9a&Nxl;RNeY`{+b!Y_W(l9EOCJP_U;Qd-8z#aXJ8Hw z+2D<|yTf-}v{^Idsd*^QFeC-7C(GWimu278`@_|%=T+ZnIdy$(QZHeQ*~cRX)y!lOPqxDC$x@u5x)|Jh)i?FDlLUO+N) zFgcsCZR;d>Ad++FF&1DS=di2AMUvr;2vVTkTO^5{eo?vC#?H$!WEpNHs3D&1>w@VHsL2K{hU+L%T5hiQhI4Ww{XfJli&2e@5mIi;DeMw+@`rqg%L-8Dm7_P+! z><{i)UIpR6=rbo!_n!LB*dF5 z{C74kCVE=IZdjUzaWC>}l*pu$@f*Y_rK*l{0}kz;@Icw$A|R>cHOIYQ-e?#Xv7J>K z7{&AD;BAXv{$!!nklP)-lI)-k+XgUtFl#Z%Ta8?Hxq+x`L+xKrZX$B3R)q z(ES+kqzPHen?~sm!ZPPP|It3;1eo3CSDcl!84_9s&Wl`0YUU;l#pk=~r~8#}I!j+Uw6ta0Am*7=cI7fSmfGduq`zTkmmf9t#g0IasHEEUtbXNcs{t$RaE~ zK{Zbvn9D1Ra-+oRQTs`ejsJ*8nn$8?R4_l) zC0hJS`f#FHe_c87ak(MAmxwn$ea*HJ`~%xMilC|1wW6|Naj8R+e{ZXeEAS>eCU-n5xB$fb-m$VbbZOHv<|_}EnOtaYV*m@yv#iDUiYXUp4cBnD#iuL5C#{srpoSHqFrJ+uzC)}PHdji7U8$o zpD@%dgenkkj{i{cV9>_$?Rdee;tu~_>Db=op=EKFg~Z2ol!a_^cNicE91~5V zLGa+4wbVee&+*XNECkxC(ZvIhvRRQfQwW0u{B5k&u5E+2Scp>r>>QH-76lsB?>`RA z72G=Xk$x&Wk^(U%!1^`Owao5fN|;S2Cv_?QiB+Uf8Zo8PPRA9dm@d}1YStYsn!`j? z)8~YbJo0i>^jx0a{_I|)HSuY1R8D9Xtv2nTw9b6e_PwqZi2xu9`?Vn#62s+ro3}qd zL%&uB?@tl1+8|t70EGF1|J{w?6p5gtP8|5KU=Trxgy%DDpQa-<&UmBf#}p>+53#oS zRwDH}dLMBcnhtow!_y>=sfVKbD>NsmBl-H#j}4GWN&limb=A>fwwYB}#BA{RG>af@ z^*M6h_uydNXkb3Iz0rMjTsSh*2UeTAF~uqb-E|+WAR1-V0;wJZzp)k5qZN+CGHcqQ zkd@8^k@k}_0tvuW?U%|f#iLn364EJBTtd9^vk0-GzCP*sWk+AWcM=sp4ZC6>1KHQT zr@Q=U`w=fSyOrN2IsHOzW-ROfJfN+x(_J?&PH#?4?+MdlnTEh{5`r?xl6BG=7_HFX z(=B^?f6sq6u$k`bGunK@sM3f78&0JWMo-#XTV-$Y6e*@Z;L^#F}Gn^eW z-Zqip8$i&~nEP`{6Fwsr11E-7zN=(4cx%Cl9{U%*wxtX!x4>J(2ORPDxSI~VN>QVo0TsN*&KV4%G6(eGk5sJNyZ7 zfn%n5E@d9`tJL*$bt<>vCavT=h3EG8zu$Rx2G(2(7sz26nAF`t#12u55 zbdCpvpa5!m-<4Ak~n#(3?B@XO=699c3q<4jQB+> z6GN)rFI|%|9Iq_>sm6*o567hL1Rwva-r(@x;v2R2A1ejThlM{@7+|V{H|V2pFftc( z=5*Y8p^kQ?8SwLEDw>=h4<1m|f>D>u&iE_JeXgFxC#R%NHfE<|s>to>g?iurTvAc( zSBV0}yke*?%#oMB^5UD|K_XY)r-v*@R@GC6 zu2Fh!i9)PIG0E)0>!+b-J0k5wg7lAaQTrMkJD?pFW8;3kS*5;ZTvLH;V&N;iPhV{u zPtJY(8ZI`-C|_jz8@p6r7K6>raBEE|OkT5(T9wb-C<(-^qzoWhPLzyFkV7xbA$7|j zl5qmE>keixXNKxrPv*EUUkw@m?$h2(^3Vhn$?H_%o&HF$U2<&Dllp4I^3Ru6UaOYh z*Mx45MRhUf!?nngDd)!eIUzBjM9M&X z`x#4-*eoz%rq-iq_MaLc)oE*@zljY!uB$=m?o9ssqQJYfM!MiiO%X4~I43m0D<%-> z`R`3s!E|b{=+kE{G`Pdw(m+;*d6M4NNNZ!>KIk;eTnp6;isaC=J0d>D@atB(YoH|f zPU&dMmu`lPwqf))gJkIrJ&S#pitO;Uc$1nFMSO{bA3`%XmBC|qd2%s-EH*WW9F<&R@l5VXyoP}TM*oD(sV?F96FZH8q)@_@t?9|A z6hMiYRT}}0;X#lNIEE`QX7KXK-S9v0wevecJtMP`K?L}P@P*2i6TT3CaId+)116(F z-1*)Afe1c@C7gl1hVBs>Y4Pt^VbY4M@�R>aQjB9oI!EpJo00!>LKYVJ-AR(0vZg zg(M;+D_&!VKQ61dJ8`r%>#-JP%tglYrkj=v#g(@o}~;adv+E4 zg)sKLsQNd>h3)K6oM)_4gawM6N87$mj529pb`4~XgfNnj8H@FkH^s{wx`kZ6iK$?O zs>qL(-!S Date: Sun, 3 Oct 2021 15:40:32 +0200 Subject: [PATCH 2218/2442] Proxy slider head circle number along with overlay --- .../Skinning/Legacy/LegacyMainCirclePiece.cs | 22 ++++++++++++------- .../Legacy/LegacySliderHeadHitCircle.cs | 8 +++---- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index 3afd814174..8b45513a2e 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -35,8 +35,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private Drawable hitCircleSprite; - protected Drawable HitCircleOverlay { get; private set; } + protected Container OverlayLayer; + private Drawable hitCircleOverlay; private SkinnableSpriteText hitCircleText; private readonly Bindable accentColour = new Bindable(); @@ -78,17 +79,22 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Anchor = Anchor.Centre, Origin = Anchor.Centre, }, - HitCircleOverlay = new KiaiFlashingSprite + OverlayLayer = new Container { - Texture = overlayTexture, Anchor = Anchor.Centre, Origin = Anchor.Centre, - }, + Child = hitCircleOverlay = new KiaiFlashingSprite + { + Texture = overlayTexture, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + } }; if (hasNumber) { - AddInternal(hitCircleText = new SkinnableSpriteText(new OsuSkinComponent(OsuSkinComponents.HitCircleText), _ => new OsuSpriteText + OverlayLayer.Add(hitCircleText = new SkinnableSpriteText(new OsuSkinComponent(OsuSkinComponents.HitCircleText), _ => new OsuSpriteText { Font = OsuFont.Numeric.With(size: 40), UseFullGlyphHeight = false, @@ -102,7 +108,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy bool overlayAboveNumber = skin.GetConfig(OsuSkinConfiguration.HitCircleOverlayAboveNumber)?.Value ?? true; if (overlayAboveNumber) - ChangeInternalChildDepth(HitCircleOverlay, float.MinValue); + OverlayLayer.ChangeChildDepth(hitCircleOverlay, float.MinValue); accentColour.BindTo(drawableObject.AccentColour); indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable); @@ -147,8 +153,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy hitCircleSprite.FadeOut(legacy_fade_duration, Easing.Out); hitCircleSprite.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); - HitCircleOverlay.FadeOut(legacy_fade_duration, Easing.Out); - HitCircleOverlay.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); + hitCircleOverlay.FadeOut(legacy_fade_duration, Easing.Out); + hitCircleOverlay.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); if (hasNumber) { diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderHeadHitCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderHeadHitCircle.cs index 13ba42ba50..7de2b8c7fa 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderHeadHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderHeadHitCircle.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy [Resolved(canBeNull: true)] private DrawableHitObject drawableHitObject { get; set; } - private Drawable proxiedHitCircleOverlay; + private Drawable proxiedOverlayLayer; public LegacySliderHeadHitCircle() : base("sliderstartcircle") @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy protected override void LoadComplete() { base.LoadComplete(); - proxiedHitCircleOverlay = HitCircleOverlay.CreateProxy(); + proxiedOverlayLayer = OverlayLayer.CreateProxy(); if (drawableHitObject != null) { @@ -35,11 +35,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private void onHitObjectApplied(DrawableHitObject drawableObject) { - Debug.Assert(proxiedHitCircleOverlay.Parent == null); + Debug.Assert(proxiedOverlayLayer.Parent == null); // see logic in LegacyReverseArrow. (drawableObject as DrawableSliderHead)?.DrawableSlider - .OverlayElementContainer.Add(proxiedHitCircleOverlay.With(d => d.Depth = float.MinValue)); + .OverlayElementContainer.Add(proxiedOverlayLayer.With(d => d.Depth = float.MinValue)); } protected override void Dispose(bool isDisposing) From 5e5cdaab5ef1a931541fe76941a4770624b0eed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 3 Oct 2021 19:14:01 +0200 Subject: [PATCH 2219/2442] Privatise setter Co-authored-by: Dean Herbert --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index 8b45513a2e..d1c9b1bf92 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private Drawable hitCircleSprite; - protected Container OverlayLayer; + protected Container OverlayLayer { get; private set; } private Drawable hitCircleOverlay; private SkinnableSpriteText hitCircleText; From ba2c44a2f4325d567b2b79f8d04b7a61e295e822 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Sun, 3 Oct 2021 04:35:37 +0000 Subject: [PATCH 2220/2442] reworked strain to fix issue with overlapping hitwindo --- .../Difficulty/Skills/Speed.cs | 57 +++++++++---------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 6b21ae7ac6..2193d503ea 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills public class Speed : OsuStrainSkill { private const double single_spacing_threshold = 125; - private const double rhythm_multiplier = 4.0; + private const double rhythm_multiplier = 0.675; private const int history_time_max = 5000; // 5 seconds of calculatingRhythmBonus max. private const double min_speed_bonus = 75; // ~200BPM private const double max_speed_bonus = 45; @@ -44,14 +44,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills ///

/// Calculates a rhythm multiplier for the difficulty of the tap associated with historic data of the current . /// - private double calculateRhythmBonus(DifficultyHitObject current, double greatWindowFull) + private double calculateRhythmBonus(DifficultyHitObject current) { if (current.BaseObject is Spinner) return 0; - int previousIslandSize = -1; + int previousIslandSize = 0; + double rhythmComplexitySum = 0; - int islandSize = 0; + int islandSize = 1; double startRatio = 0; // store the ratio of the current start of an island to buff for tighter rhythms bool firstDeltaSwitch = false; @@ -66,71 +67,67 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills if (currHistoricalDecay != 0) { - currHistoricalDecay = Math.Min(currHistoricalDecay, (double)(Previous.Count - i) / Previous.Count); // either we're limited by time or limited by object count. + currHistoricalDecay = Math.Min((double)(Previous.Count - i) / Previous.Count, currHistoricalDecay); // either we're limited by time or limited by object count. double currDelta = Math.Max(25, currObj.DeltaTime); double prevDelta = Math.Max(25, prevObj.DeltaTime); double lastDelta = ((OsuDifficultyHitObject)lastObj).StrainTime; - double effectiveRatio = Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta); + double currRatio = 1.0 + Math.Min(4.5, 6 * Math.Pow(Math.Sin(Math.PI / (Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta))), 2)); // fancy function to calculate rhythmbonuses. - if (effectiveRatio > 0.5) - effectiveRatio = 0.5 + (effectiveRatio - 0.5) * 6; // large buff for 1/3 -> 1/4 type transitions. - else - effectiveRatio = 0.5; + double windowPenalty = Math.Min(1, Math.Max(0, Math.Max(prevDelta, currDelta) - Math.Min(prevDelta, currDelta) - greatWindow) / greatWindow); - effectiveRatio *= currHistoricalDecay; // scale with time + windowPenalty = Math.Min(1, windowPenalty * (previousIslandSize + islandSize)); if (firstDeltaSwitch) { - if (Precision.AlmostEquals(prevDelta, currDelta, 15)) + if (!(prevDelta > 1.25 * currDelta || prevDelta * 1.25 < currDelta)) { islandSize++; // island is still progressing, count size. } else { - if (islandSize > 12) - islandSize = 12; + double effectiveRatio = windowPenalty * currRatio; + + if (islandSize > 7) + islandSize = 7; if (Previous[i - 1].BaseObject is Slider) // bpm change is into slider, this is easy acc window - effectiveRatio *= 0.25; + effectiveRatio *= 0.125; if (Previous[i].BaseObject is Slider) // bpm change was from a slider, this is easier typically than circle -> circle - effectiveRatio *= 0.5; + effectiveRatio *= 0.25; if (previousIslandSize == islandSize) // repeated island size (ex: triplet -> triplet) - effectiveRatio *= 0.35; + effectiveRatio *= 0.25; if (previousIslandSize % 2 == islandSize % 2) // repeated island polartiy (2 -> 4, 3 -> 5) - effectiveRatio *= 0.75; + effectiveRatio *= 0.50; if (lastDelta > prevDelta + 10 && prevDelta > currDelta + 10) // previous increase happened a note ago, 1/1->1/2-1/4, dont want to buff this. effectiveRatio *= 0.125; - rhythmComplexitySum += effectiveRatio * startRatio; + rhythmComplexitySum += Math.Sqrt(effectiveRatio * startRatio) * currHistoricalDecay * Math.Sqrt(4 + islandSize) / 2 * Math.Sqrt(4 + previousIslandSize) / 2; - startRatio = Math.Sqrt(Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta)); + startRatio = windowPenalty * currRatio; previousIslandSize = islandSize; // log the last island size. if (prevDelta * 1.25 < currDelta) // we're slowing down, stop counting firstDeltaSwitch = false; // if we're speeding up, this stays true and we keep counting island size. - islandSize = 0; + islandSize = 1; } } else if (prevDelta > 1.25 * currDelta) // we want to be speeding up. { // Begin counting island until we change speed again. firstDeltaSwitch = true; - islandSize = 0; - startRatio = Math.Sqrt(Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta)); + startRatio = windowPenalty * currRatio; + islandSize = 1; } } } - if (greatWindowFull > 62) - rhythmComplexitySum *= Math.Sqrt(62 / greatWindowFull); - return Math.Sqrt(4 + rhythmComplexitySum * rhythm_multiplier) / 2; //produces multiplier that can be applied to strain. range [1, infinity) (not really though) } @@ -156,7 +153,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000); - protected override double CalculateInitialStrain(double time) => (currentMovementStrain + currentTapStrain * currentRhythm) * strainDecay(time - Previous[0].StartTime); + protected override double CalculateInitialStrain(double time) => ((currentMovementStrain + currentTapStrain) * currentRhythm) * strainDecay(time - Previous[0].StartTime); protected override double StrainValueAt(DifficultyHitObject current) { @@ -182,9 +179,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills if (strainTime < min_speed_bonus) speedBonus = 1 + 0.75 * Math.Pow((min_speed_bonus - strainTime) / speed_balancing_factor, 2); - currentRhythm = calculateRhythmBonus(current, greatWindowFull); + currentRhythm = calculateRhythmBonus(current); double decay = strainDecay(current.DeltaTime); + double tapStrain = tapStrainOf(current, speedBonus, strainTime) * skillMultiplier; + double maxStrain = (1 / decay) * tapStrain; currentTapStrain *= decay; currentTapStrain += tapStrainOf(current, speedBonus, strainTime) * skillMultiplier; @@ -192,7 +191,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills currentMovementStrain *= decay; currentMovementStrain += movementStrainOf(current, speedBonus, strainTime) * skillMultiplier; - return currentMovementStrain + currentTapStrain * currentRhythm; + return (currentMovementStrain + currentTapStrain) * currentRhythm; } } } From 6d134b2a83e4fde69cfae550d56352550c64c7a5 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Sun, 3 Oct 2021 17:06:59 +0000 Subject: [PATCH 2221/2442] resolved code cleanliness issues --- .../Difficulty/Skills/Speed.cs | 71 +++++++------------ 1 file changed, 25 insertions(+), 46 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 2193d503ea..4048d84d35 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -19,14 +19,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private const double rhythm_multiplier = 0.675; private const int history_time_max = 5000; // 5 seconds of calculatingRhythmBonus max. private const double min_speed_bonus = 75; // ~200BPM - private const double max_speed_bonus = 45; private const double speed_balancing_factor = 40; private double skillMultiplier => 1375; private double strainDecayBase => 0.3; - private double currentTapStrain = 1; - private double currentMovementStrain = 1; + private double currentStrain = 1; private double currentRhythm = 1; protected override int ReducedSectionCount => 5; @@ -59,9 +57,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills for (int i = Previous.Count - 2; i > 0; i--) { - DifficultyHitObject currObj = Previous[i - 1]; - DifficultyHitObject prevObj = Previous[i]; - DifficultyHitObject lastObj = Previous[i + 1]; + OsuDifficultyHitObject currObj = (OsuDifficultyHitObject)Previous[i - 1]; + OsuDifficultyHitObject prevObj = (OsuDifficultyHitObject)Previous[i]; + OsuDifficultyHitObject lastObj = (OsuDifficultyHitObject)Previous[i + 1]; double currHistoricalDecay = Math.Max(0, (history_time_max - (current.StartTime - currObj.StartTime))) / history_time_max; // scales note 0 to 1 from history to now @@ -69,9 +67,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { currHistoricalDecay = Math.Min((double)(Previous.Count - i) / Previous.Count, currHistoricalDecay); // either we're limited by time or limited by object count. - double currDelta = Math.Max(25, currObj.DeltaTime); - double prevDelta = Math.Max(25, prevObj.DeltaTime); - double lastDelta = ((OsuDifficultyHitObject)lastObj).StrainTime; + double currDelta = currObj.StrainTime; + double prevDelta = prevObj.StrainTime; + double lastDelta = lastObj.StrainTime; double currRatio = 1.0 + Math.Min(4.5, 6 * Math.Pow(Math.Sin(Math.PI / (Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta))), 2)); // fancy function to calculate rhythmbonuses. double windowPenalty = Math.Min(1, Math.Max(0, Math.Max(prevDelta, currDelta) - Math.Min(prevDelta, currDelta) - greatWindow) / greatWindow); @@ -82,15 +80,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { if (!(prevDelta > 1.25 * currDelta || prevDelta * 1.25 < currDelta)) { - islandSize++; // island is still progressing, count size. + if (islandSize < 7) + islandSize++; // island is still progressing, count size. } else { double effectiveRatio = windowPenalty * currRatio; - if (islandSize > 7) - islandSize = 7; - if (Previous[i - 1].BaseObject is Slider) // bpm change is into slider, this is easy acc window effectiveRatio *= 0.125; @@ -131,32 +127,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills return Math.Sqrt(4 + rhythmComplexitySum * rhythm_multiplier) / 2; //produces multiplier that can be applied to strain. range [1, infinity) (not really though) } - private double tapStrainOf(DifficultyHitObject current, double speedBonus, double strainTime) + private double strainValueOf(DifficultyHitObject current) { if (current.BaseObject is Spinner) return 0; - return speedBonus / strainTime; - } - - private double movementStrainOf(DifficultyHitObject current, double speedBonus, double strainTime) - { - if (current.BaseObject is Spinner) - return 0; - - var osuCurrObj = (OsuDifficultyHitObject)current; - - double distance = Math.Min(single_spacing_threshold, osuCurrObj.TravelDistance + osuCurrObj.JumpDistance); - - return (speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / strainTime; - } - - private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000); - - protected override double CalculateInitialStrain(double time) => ((currentMovementStrain + currentTapStrain) * currentRhythm) * strainDecay(time - Previous[0].StartTime); - - protected override double StrainValueAt(DifficultyHitObject current) - { // derive strainTime for calculation var osuCurrObj = (OsuDifficultyHitObject)current; var osuPrevObj = Previous.Count > 0 ? (OsuDifficultyHitObject)Previous[0] : null; @@ -179,19 +154,23 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills if (strainTime < min_speed_bonus) speedBonus = 1 + 0.75 * Math.Pow((min_speed_bonus - strainTime) / speed_balancing_factor, 2); + double distance = Math.Min(single_spacing_threshold, osuCurrObj.TravelDistance + osuCurrObj.JumpDistance); + + return (speedBonus + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / strainTime; + } + + private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000); + + protected override double CalculateInitialStrain(double time) => (currentStrain * currentRhythm) * strainDecay(time - Previous[0].StartTime); + + protected override double StrainValueAt(DifficultyHitObject current) + { + currentStrain *= strainDecay(current.DeltaTime); + currentStrain += strainValueOf(current) * skillMultiplier; + currentRhythm = calculateRhythmBonus(current); - double decay = strainDecay(current.DeltaTime); - double tapStrain = tapStrainOf(current, speedBonus, strainTime) * skillMultiplier; - double maxStrain = (1 / decay) * tapStrain; - - currentTapStrain *= decay; - currentTapStrain += tapStrainOf(current, speedBonus, strainTime) * skillMultiplier; - - currentMovementStrain *= decay; - currentMovementStrain += movementStrainOf(current, speedBonus, strainTime) * skillMultiplier; - - return (currentMovementStrain + currentTapStrain) * currentRhythm; + return currentStrain * currentRhythm; } } } From bc3ae4c4f839d9cc9693259953db570d68bc9025 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Sun, 3 Oct 2021 17:36:34 +0000 Subject: [PATCH 2222/2442] changed function names to be consistent --- osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs | 6 ++---- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 4 +--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index 93df6e90f8..d8f4aa1229 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private double skillMultiplier => 26.25; private double strainDecayBase => 0.15; - private double aimStrainOf(DifficultyHitObject current) + private double strainValueOf(DifficultyHitObject current) { if (current.BaseObject is Spinner) return 0; @@ -69,10 +69,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override double StrainValueAt(DifficultyHitObject current) { - double aimStrain = aimStrainOf(current); - currentStrain *= strainDecay(current.DeltaTime); - currentStrain += aimStrain * skillMultiplier; + currentStrain += strainValueOf(current) * skillMultiplier; return currentStrain; } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 7121582da7..e3abe7d700 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -70,10 +70,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override double StrainValueAt(DifficultyHitObject current) { - double flashlightStrain = strainValueOf(current); - currentStrain *= strainDecay(current.DeltaTime); - currentStrain += flashlightStrain * skillMultiplier; + currentStrain += strainValueOf(current) * skillMultiplier; return currentStrain; } From 94f8692b007fe0912bdf5d0b5c5a14a862923e7e Mon Sep 17 00:00:00 2001 From: Xexxar Date: Sun, 3 Oct 2021 17:42:49 +0000 Subject: [PATCH 2223/2442] removed acc changes to put in seperate PR --- .../Difficulty/OsuPerformanceCalculator.cs | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 87aecd32f7..bf4d92652c 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -160,28 +160,25 @@ namespace osu.Game.Rulesets.Osu.Difficulty private double computeAccuracyValue() { + // This percentage only considers HitCircles of any value - in this part of the calculation we focus on hitting the timing hit window. + double betterAccuracyPercentage; int amountHitObjectsWithAccuracy = Attributes.HitCircleCount; - // This section should be documented by Tr3, but effectively we're calculating the exact same way as before, but - // we calculate a variance based on the object count and # of 50s, 100s, etc. This prevents us from having cases - // where an SS on lower OD is actually worth more than a 95% on OD11, even though the OD11 requires a greater - // window of precision. + if (amountHitObjectsWithAccuracy > 0) + betterAccuracyPercentage = ((countGreat - (totalHits - amountHitObjectsWithAccuracy)) * 6 + countOk * 2 + countMeh) / (double)(amountHitObjectsWithAccuracy * 6); + else + betterAccuracyPercentage = 0; - double p100 = (2 * (double)countOk) / amountHitObjectsWithAccuracy; // this is multiplied by two to encourage better accuracy. (scales better) - double p50 = (1 * (double)countMeh) / amountHitObjectsWithAccuracy; - double pm = (1 * (double)countMiss) / amountHitObjectsWithAccuracy; - double p300 = Math.Max(0, 1.0 - pm - p100 - p50); + // It is possible to reach a negative accuracy with this formula. Cap it at zero - zero points. + if (betterAccuracyPercentage < 0) + betterAccuracyPercentage = 0; - double m300 = 79.5 - 6.0 * Attributes.OverallDifficulty; - double m100 = 139.5 - 8.0 * Attributes.OverallDifficulty; - double m50 = 199.5 - 10.0 * Attributes.OverallDifficulty; + // Lots of arbitrary values from testing. + // Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution. + double accuracyValue = Math.Pow(1.52163, Attributes.OverallDifficulty) * Math.Pow(betterAccuracyPercentage, 24) * 2.83; - double variance = p300 * Math.Pow(m300 / 2.0, 2.0) + - p100 * Math.Pow((m300 + m100) / 2.0, 2.0) + - p50 * Math.Pow((m100 + m50) / 2.0, 2.0); - - double accuracyValue = 2.83 * Math.Pow(1.52163, (79.5 - 2 * Math.Sqrt(variance)) / 6.0) - * Math.Pow(Math.Log(1.0 + (Math.E - 1.0) * (Math.Min(amountHitObjectsWithAccuracy, 1600) / 1000.0)), 0.5); + // Bonus for many hitcircles - it's harder to keep good accuracy up for longer. + accuracyValue *= Math.Min(1.15, Math.Pow(amountHitObjectsWithAccuracy / 1000.0, 0.3)); if (mods.Any(m => m is OsuModHidden)) accuracyValue *= 1.08; From 86240cc8ecf570e9c9eba1760db1d651d67c2c9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 3 Oct 2021 23:36:39 +0200 Subject: [PATCH 2224/2442] Add alternate Torus font --- osu.Game/Graphics/OsuFont.cs | 6 ++++++ osu.Game/OsuGameBase.cs | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/osu.Game/Graphics/OsuFont.cs b/osu.Game/Graphics/OsuFont.cs index b6090d0e1a..edb484021c 100644 --- a/osu.Game/Graphics/OsuFont.cs +++ b/osu.Game/Graphics/OsuFont.cs @@ -21,6 +21,8 @@ namespace osu.Game.Graphics public static FontUsage Torus => GetFont(Typeface.Torus, weight: FontWeight.Regular); + public static FontUsage TorusAlternate => GetFont(Typeface.TorusAlternate, weight: FontWeight.Regular); + public static FontUsage Inter => GetFont(Typeface.Inter, weight: FontWeight.Regular); /// @@ -57,6 +59,9 @@ namespace osu.Game.Graphics case Typeface.Torus: return "Torus"; + case Typeface.TorusAlternate: + return "Torus-Alternate"; + case Typeface.Inter: return "Inter"; } @@ -113,6 +118,7 @@ namespace osu.Game.Graphics { Venera, Torus, + TorusAlternate, Inter, } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index adb819bf20..02de92e805 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -347,6 +347,11 @@ namespace osu.Game AddFont(Resources, @"Fonts/Torus/Torus-SemiBold"); AddFont(Resources, @"Fonts/Torus/Torus-Bold"); + AddFont(Resources, @"Fonts/Torus-Alternate/Torus-Alternate-Regular"); + AddFont(Resources, @"Fonts/Torus-Alternate/Torus-Alternate-Light"); + AddFont(Resources, @"Fonts/Torus-Alternate/Torus-Alternate-SemiBold"); + AddFont(Resources, @"Fonts/Torus-Alternate/Torus-Alternate-Bold"); + AddFont(Resources, @"Fonts/Inter/Inter-Regular"); AddFont(Resources, @"Fonts/Inter/Inter-RegularItalic"); AddFont(Resources, @"Fonts/Inter/Inter-Light"); From 67d08a3eeee036fc94fca611e4986efa3002c372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 4 Oct 2021 00:20:16 +0200 Subject: [PATCH 2225/2442] Add test scene for previewing Torus alternates --- .../Visual/UserInterface/TestSceneOsuFont.cs | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneOsuFont.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuFont.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuFont.cs new file mode 100644 index 0000000000..eedafce271 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuFont.cs @@ -0,0 +1,77 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneOsuFont : OsuTestScene + { + private OsuSpriteText spriteText; + + private readonly BindableBool useAlternates = new BindableBool(); + private readonly Bindable weight = new Bindable(FontWeight.Regular); + + [BackgroundDependencyLoader] + private void load() + { + Child = spriteText = new OsuSpriteText + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + RelativeSizeAxes = Axes.X, + AllowMultiline = true, + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + useAlternates.BindValueChanged(_ => updateFont()); + weight.BindValueChanged(_ => updateFont(), true); + } + + private void updateFont() + { + FontUsage usage = useAlternates.Value ? OsuFont.TorusAlternate : OsuFont.Torus; + spriteText.Font = usage.With(size: 40, weight: weight.Value); + } + + [Test] + public void TestTorusAlternates() + { + AddStep("set all ASCII letters", () => spriteText.Text = @"ABCDEFGHIJKLMNOPQRSTUVWXYZ +abcdefghijklmnopqrstuvwxyz"); + AddStep("set all alternates", () => spriteText.Text = @"A Á Ă Â Ä À Ā Ą Å Ã +Æ B D Ð Ď Đ E É Ě Ê +Ë Ė È Ē Ę F G Ğ Ģ Ġ +H I Í Î Ï İ Ì Ī Į K +Ķ O Œ P Þ Q R Ŕ Ř Ŗ +T Ŧ Ť Ţ Ț V W Ẃ Ŵ Ẅ +Ẁ X Y Ý Ŷ Ÿ Ỳ a á ă +â ä à ā ą å ã æ b d +ď đ e é ě ê ë ė è ē +ę f g ğ ģ ġ k ķ m n +ń ň ņ ŋ ñ o œ p þ q +t ŧ ť ţ ț u ú û ü ù +ű ū ų ů w ẃ ŵ ẅ ẁ x +y ý ŷ ÿ ỳ"); + + AddToggleStep("toggle alternates", alternates => useAlternates.Value = alternates); + + addSetWeightStep(FontWeight.Light); + addSetWeightStep(FontWeight.Regular); + addSetWeightStep(FontWeight.SemiBold); + addSetWeightStep(FontWeight.Bold); + + void addSetWeightStep(FontWeight newWeight) => AddStep($"set weight {newWeight}", () => weight.Value = newWeight); + } + } +} From 017756cbcae754236a4e6cdb3b37f0301121b6be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 4 Oct 2021 00:21:36 +0200 Subject: [PATCH 2226/2442] Use Torus alternates on online play screens as per design --- osu.Game/Screens/OnlinePlay/Header.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Header.cs b/osu.Game/Screens/OnlinePlay/Header.cs index b0db9256f5..2d4b5cc527 100644 --- a/osu.Game/Screens/OnlinePlay/Header.cs +++ b/osu.Game/Screens/OnlinePlay/Header.cs @@ -72,21 +72,21 @@ namespace osu.Game.Screens.OnlinePlay { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Font = OsuFont.GetFont(size: 24), + Font = OsuFont.TorusAlternate.With(size: 24), Text = mainTitle }, dot = new OsuSpriteText { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Font = OsuFont.GetFont(size: 24), + Font = OsuFont.TorusAlternate.With(size: 24), Text = "·" }, pageTitle = new OsuSpriteText { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Font = OsuFont.GetFont(size: 24), + Font = OsuFont.TorusAlternate.With(size: 24), Text = "Lounge" } } From 11e9c16b92eec768d23c389329600ca89cf0e2aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Oct 2021 11:13:46 +0900 Subject: [PATCH 2227/2442] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index b84f1730ac..eeca40e73d 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c162025f1f..33d4e5a6c8 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 8597a06c03..e30722c334 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From 537b29654e60e738128fa123345a01ab39074b22 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Oct 2021 14:30:22 +0900 Subject: [PATCH 2228/2442] Fix stream being held open causing windows CI failures --- osu.Game.Tests/Database/RealmTest.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Database/RealmTest.cs b/osu.Game.Tests/Database/RealmTest.cs index 219690db30..576f901c1a 100644 --- a/osu.Game.Tests/Database/RealmTest.cs +++ b/osu.Game.Tests/Database/RealmTest.cs @@ -70,7 +70,8 @@ namespace osu.Game.Tests.Database { try { - return testStorage.GetStream(realmFactory.Filename)?.Length ?? 0; + using (var stream = testStorage.GetStream(realmFactory.Filename)) + return stream?.Length ?? 0; } catch { From c6aba3e78b3a9f8cf89e60c5f4b069c2873532ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Oct 2021 14:44:16 +0900 Subject: [PATCH 2229/2442] Ensure a `DrawableChannel` is not attempted to be added after disposal --- osu.Game/Overlays/ChatOverlay.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 25c5154d4a..a61b80cc8e 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -284,6 +284,10 @@ namespace osu.Game.Overlays if (currentChannel.Value != e.NewValue) return; + // check once more to ensure the channel hasn't since been removed from the loaded channels like (may have been left by some automated means). + if (loadedChannels.Contains(loaded)) + return; + loading.Hide(); currentChannelContainer.Clear(false); @@ -426,7 +430,7 @@ namespace osu.Game.Overlays base.PopOut(); } - private void joinedChannelsChanged(object sender, NotifyCollectionChangedEventArgs args) + private void joinedChannelsChanged(object sender, NotifyCollectionChangedEventArgs args) => Schedule(() => { switch (args.Action) { @@ -444,10 +448,9 @@ namespace osu.Game.Overlays if (loaded != null) { - loadedChannels.Remove(loaded); - // Because the container is only cleared in the async load callback of a new channel, it is forcefully cleared // to ensure that the previous channel doesn't get updated after it's disposed + loadedChannels.Remove(loaded); currentChannelContainer.Remove(loaded); loaded.Dispose(); } @@ -455,7 +458,7 @@ namespace osu.Game.Overlays break; } - } + }); private void availableChannelsChanged(object sender, NotifyCollectionChangedEventArgs args) { From c19c2335eccdd752a521db4b869abf7d96428c19 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Oct 2021 14:58:54 +0900 Subject: [PATCH 2230/2442] Remove added schedule due to changing flow --- osu.Game/Overlays/ChatOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index a61b80cc8e..7be9258248 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -430,7 +430,7 @@ namespace osu.Game.Overlays base.PopOut(); } - private void joinedChannelsChanged(object sender, NotifyCollectionChangedEventArgs args) => Schedule(() => + private void joinedChannelsChanged(object sender, NotifyCollectionChangedEventArgs args) { switch (args.Action) { @@ -458,7 +458,7 @@ namespace osu.Game.Overlays break; } - }); + } private void availableChannelsChanged(object sender, NotifyCollectionChangedEventArgs args) { From bc984dff4f2b48d1a4975dfdb031eb378e4d2045 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 4 Oct 2021 15:35:28 +0900 Subject: [PATCH 2231/2442] Fix typo --- osu.Game/Overlays/ChatOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 7be9258248..20d637d957 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -284,7 +284,7 @@ namespace osu.Game.Overlays if (currentChannel.Value != e.NewValue) return; - // check once more to ensure the channel hasn't since been removed from the loaded channels like (may have been left by some automated means). + // check once more to ensure the channel hasn't since been removed from the loaded channels list (may have been left by some automated means). if (loadedChannels.Contains(loaded)) return; From 5aaafce597e370f9c6c900d3c9f9e992499e0361 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Oct 2021 15:40:00 +0900 Subject: [PATCH 2232/2442] Make `AuthenticateWithLogin` throw instead of return a `bool` success status --- .../ErrorTextFlowContainer.cs | 2 +- osu.Game/Online/API/OAuth.cs | 37 ++++++++++++++++--- 2 files changed, 32 insertions(+), 7 deletions(-) rename osu.Game/{Overlays/AccountCreation => Graphics}/ErrorTextFlowContainer.cs (95%) diff --git a/osu.Game/Overlays/AccountCreation/ErrorTextFlowContainer.cs b/osu.Game/Graphics/ErrorTextFlowContainer.cs similarity index 95% rename from osu.Game/Overlays/AccountCreation/ErrorTextFlowContainer.cs rename to osu.Game/Graphics/ErrorTextFlowContainer.cs index 87ff4dd398..f17a2a2c3d 100644 --- a/osu.Game/Overlays/AccountCreation/ErrorTextFlowContainer.cs +++ b/osu.Game/Graphics/ErrorTextFlowContainer.cs @@ -6,7 +6,7 @@ using osu.Framework.Graphics; using osu.Game.Graphics.Containers; using osuTK.Graphics; -namespace osu.Game.Overlays.AccountCreation +namespace osu.Game.Graphics { public class ErrorTextFlowContainer : OsuTextFlowContainer { diff --git a/osu.Game/Online/API/OAuth.cs b/osu.Game/Online/API/OAuth.cs index bdc47aab8d..693e7c5336 100644 --- a/osu.Game/Online/API/OAuth.cs +++ b/osu.Game/Online/API/OAuth.cs @@ -1,8 +1,10 @@ // 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.Diagnostics; using System.Net.Http; +using Newtonsoft.Json; using osu.Framework.Bindables; namespace osu.Game.Online.API @@ -32,10 +34,10 @@ namespace osu.Game.Online.API this.endpoint = endpoint; } - internal bool AuthenticateWithLogin(string username, string password) + internal void AuthenticateWithLogin(string username, string password) { - if (string.IsNullOrEmpty(username)) return false; - if (string.IsNullOrEmpty(password)) return false; + if (string.IsNullOrEmpty(username)) throw new ArgumentException("Missing username."); + if (string.IsNullOrEmpty(password)) throw new ArgumentException("Missing password."); using (var req = new AccessTokenRequestPassword(username, password) { @@ -49,13 +51,27 @@ namespace osu.Game.Online.API { req.Perform(); } - catch + catch (Exception ex) { - return false; + Token.Value = null; + + var throwableException = ex; + + try + { + // attempt to decode a displayable error string. + var error = JsonConvert.DeserializeObject(req.GetResponseString() ?? string.Empty); + if (error != null) + throwableException = new APIException(error.Message, ex); + } + catch + { + } + + throw throwableException; } Token.Value = req.ResponseObject; - return true; } } @@ -182,5 +198,14 @@ namespace osu.Game.Online.API base.PrePerform(); } } + + private class OAuthError + { + [JsonProperty("error")] + public string ErrorType { get; set; } + + [JsonProperty("hint")] + public string Message { get; set; } + } } } From 266b4c7124fe4b578087afaaba46e18ec4ee0423 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Oct 2021 15:40:24 +0900 Subject: [PATCH 2233/2442] Expose login errors from `IAPIProvider` and show on the login form --- .../Visual/Menus/TestSceneLoginPanel.cs | 16 ++++++++++- osu.Game/Online/API/APIAccess.cs | 28 ++++++++++++------- osu.Game/Online/API/DummyAPIAccess.cs | 18 ++++++++++++ osu.Game/Online/API/IAPIProvider.cs | 6 ++++ osu.Game/Overlays/Login/LoginForm.cs | 14 +++++++++- 5 files changed, 70 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneLoginPanel.cs b/osu.Game.Tests/Visual/Menus/TestSceneLoginPanel.cs index 5fdadfc2fb..4754a73f83 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneLoginPanel.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneLoginPanel.cs @@ -6,6 +6,7 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; using osu.Game.Overlays.Login; namespace osu.Game.Tests.Visual.Menus @@ -30,12 +31,25 @@ namespace osu.Game.Tests.Visual.Menus } [Test] - public void TestBasicLogin() + public void TestLoginSuccess() { AddStep("logout", () => API.Logout()); AddStep("enter password", () => loginPanel.ChildrenOfType().First().Text = "password"); AddStep("submit", () => loginPanel.ChildrenOfType().First(b => b.Text.ToString() == "Sign in").TriggerClick()); } + + [Test] + public void TestLoginFailure() + { + AddStep("logout", () => + { + API.Logout(); + ((DummyAPIAccess)API).FailNextLogin(); + }); + + AddStep("enter password", () => loginPanel.ChildrenOfType().First().Text = "password"); + AddStep("submit", () => loginPanel.ChildrenOfType().First(b => b.Text.ToString() == "Sign in").TriggerClick()); + } } } diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index af14cdc7b3..94508e3a81 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -35,9 +35,8 @@ namespace osu.Game.Online.API public string WebsiteRootUrl { get; } - /// - /// The username/email provided by the user when initiating a login. - /// + public Exception LastLoginError { get; private set; } + public string ProvidedUsername { get; private set; } private string password; @@ -136,14 +135,23 @@ namespace osu.Game.Online.API // save the username at this point, if the user requested for it to be. config.SetValue(OsuSetting.Username, config.Get(OsuSetting.SaveUsername) ? ProvidedUsername : string.Empty); - if (!authentication.HasValidAccessToken && !authentication.AuthenticateWithLogin(ProvidedUsername, password)) + if (!authentication.HasValidAccessToken) { - //todo: this fails even on network-related issues. we should probably handle those differently. - //NotificationOverlay.ShowMessage("Login failed!"); - log.Add(@"Login failed!"); - password = null; - authentication.Clear(); - continue; + LastLoginError = null; + + try + { + authentication.AuthenticateWithLogin(ProvidedUsername, password); + } + catch (Exception e) + { + //todo: this fails even on network-related issues. we should probably handle those differently. + LastLoginError = e; + log.Add(@"Login failed!"); + password = null; + authentication.Clear(); + continue; + } } var userReq = new GetUserRequest(); diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs index 1ba31db9fa..8f91a4d198 100644 --- a/osu.Game/Online/API/DummyAPIAccess.cs +++ b/osu.Game/Online/API/DummyAPIAccess.cs @@ -32,6 +32,8 @@ namespace osu.Game.Online.API public string WebsiteRootUrl => "http://localhost"; + public Exception LastLoginError { get; private set; } + /// /// Provide handling logic for an arbitrary API request. /// Should return true is a request was handled. If null or false return, the request will be failed with a . @@ -40,6 +42,8 @@ namespace osu.Game.Online.API private readonly Bindable state = new Bindable(APIState.Online); + private bool shouldFailNextLogin; + /// /// The current connectivity state of the API. /// @@ -74,6 +78,18 @@ namespace osu.Game.Online.API public void Login(string username, string password) { + state.Value = APIState.Connecting; + + if (shouldFailNextLogin) + { + LastLoginError = new APIException("Not powerful enough to login.", new ArgumentException(nameof(shouldFailNextLogin))); + + state.Value = APIState.Offline; + shouldFailNextLogin = false; + return; + } + + LastLoginError = null; LocalUser.Value = new User { Username = username, @@ -102,5 +118,7 @@ namespace osu.Game.Online.API IBindable IAPIProvider.LocalUser => LocalUser; IBindableList IAPIProvider.Friends => Friends; IBindable IAPIProvider.Activity => Activity; + + public void FailNextLogin() => shouldFailNextLogin = true; } } diff --git a/osu.Game/Online/API/IAPIProvider.cs b/osu.Game/Online/API/IAPIProvider.cs index 5ad5367924..72ca37bcf4 100644 --- a/osu.Game/Online/API/IAPIProvider.cs +++ b/osu.Game/Online/API/IAPIProvider.cs @@ -3,6 +3,7 @@ #nullable enable +using System; using System.Threading.Tasks; using osu.Framework.Bindables; using osu.Game.Users; @@ -55,6 +56,11 @@ namespace osu.Game.Online.API /// string WebsiteRootUrl { get; } + /// + /// The last login error that occurred, if any. + /// + Exception? LastLoginError { get; } + /// /// The current connection state of the API. /// This is not thread-safe and should be scheduled locally if consumed from a drawable component. diff --git a/osu.Game/Overlays/Login/LoginForm.cs b/osu.Game/Overlays/Login/LoginForm.cs index e43b84d52a..f7842dcd30 100644 --- a/osu.Game/Overlays/Login/LoginForm.cs +++ b/osu.Game/Overlays/Login/LoginForm.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Game.Configuration; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; @@ -42,6 +43,9 @@ namespace osu.Game.Overlays.Login Spacing = new Vector2(0, 5); AutoSizeAxes = Axes.Y; RelativeSizeAxes = Axes.X; + + ErrorTextFlowContainer errorText; + Children = new Drawable[] { username = new OsuTextBox @@ -57,6 +61,11 @@ namespace osu.Game.Overlays.Login RelativeSizeAxes = Axes.X, TabbableContentContainer = this, }, + errorText = new ErrorTextFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }, new SettingsCheckbox { LabelText = "Remember username", @@ -97,6 +106,9 @@ namespace osu.Game.Overlays.Login }; password.OnCommit += (sender, newText) => performLogin(); + + if (api?.LastLoginError?.Message is string error) + errorText.AddErrors(new[] { error }); } public override bool AcceptsFocus => true; @@ -108,4 +120,4 @@ namespace osu.Game.Overlays.Login Schedule(() => { GetContainingInputManager().ChangeFocus(string.IsNullOrEmpty(username.Text) ? username : password); }); } } -} \ No newline at end of file +} From 4e1322effac966e1b3fec6dab2231d131648defc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 4 Oct 2021 16:02:45 +0900 Subject: [PATCH 2234/2442] Fix typo --- osu.Game/Beatmaps/BeatmapModelManager.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapModelManager.cs b/osu.Game/Beatmaps/BeatmapModelManager.cs index aa14f95863..250d6653d5 100644 --- a/osu.Game/Beatmaps/BeatmapModelManager.cs +++ b/osu.Game/Beatmaps/BeatmapModelManager.cs @@ -185,12 +185,12 @@ namespace osu.Game.Beatmaps /// /// Saves an file against a given . /// - /// The to save the content against. The file referenced by will be replaced. + /// The to save the content against. The file referenced by will be replaced. /// The content to write. /// The beatmap content to write, null if to be omitted. - public virtual void Save(BeatmapInfo baetmapInfo, IBeatmap beatmapContent, ISkin beatmapSkin = null) + public virtual void Save(BeatmapInfo beatmapInfo, IBeatmap beatmapContent, ISkin beatmapSkin = null) { - var setInfo = baetmapInfo.BeatmapSet; + var setInfo = beatmapInfo.BeatmapSet; using (var stream = new MemoryStream()) { @@ -201,7 +201,7 @@ namespace osu.Game.Beatmaps using (ContextFactory.GetForWrite()) { - var beatmapInfo = setInfo.Beatmaps.Single(b => b.ID == baetmapInfo.ID); + beatmapInfo = setInfo.Beatmaps.Single(b => b.ID == beatmapInfo.ID); var metadata = beatmapInfo.Metadata ?? setInfo.Metadata; // grab the original file (or create a new one if not found). @@ -219,7 +219,7 @@ namespace osu.Game.Beatmaps } } - WorkingBeatmapCache?.Invalidate(baetmapInfo); + WorkingBeatmapCache?.Invalidate(beatmapInfo); } /// From 3a0b7ba8fffe61e85b8adceebf48a7eeffb10fd8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Oct 2021 16:18:55 +0900 Subject: [PATCH 2235/2442] Add fallback to use `Message` when `Hint` is not available --- osu.Game/Online/API/OAuth.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/OAuth.cs b/osu.Game/Online/API/OAuth.cs index 693e7c5336..d79fc58d1c 100644 --- a/osu.Game/Online/API/OAuth.cs +++ b/osu.Game/Online/API/OAuth.cs @@ -62,7 +62,7 @@ namespace osu.Game.Online.API // attempt to decode a displayable error string. var error = JsonConvert.DeserializeObject(req.GetResponseString() ?? string.Empty); if (error != null) - throwableException = new APIException(error.Message, ex); + throwableException = new APIException(error.UserDisplayableError, ex); } catch { @@ -201,10 +201,15 @@ namespace osu.Game.Online.API private class OAuthError { + public string UserDisplayableError => !string.IsNullOrEmpty(Hint) ? Hint : ErrorIdentifier; + [JsonProperty("error")] - public string ErrorType { get; set; } + public string ErrorIdentifier { get; set; } [JsonProperty("hint")] + public string Hint { get; set; } + + [JsonProperty("message")] public string Message { get; set; } } } From 3c15ef720f96e0cfd52cd4f7b90929a834ff2f6f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Oct 2021 16:26:28 +0900 Subject: [PATCH 2236/2442] Remove setter from `IHasGuidPrimaryKey` interface --- osu.Game/Database/IHasGuidPrimaryKey.cs | 2 +- osu.Game/Database/ILive.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Database/IHasGuidPrimaryKey.cs b/osu.Game/Database/IHasGuidPrimaryKey.cs index c9cd9b257a..f52dc5c8ef 100644 --- a/osu.Game/Database/IHasGuidPrimaryKey.cs +++ b/osu.Game/Database/IHasGuidPrimaryKey.cs @@ -11,6 +11,6 @@ namespace osu.Game.Database { [JsonIgnore] [PrimaryKey] - Guid ID { get; set; } + Guid ID { get; } } } diff --git a/osu.Game/Database/ILive.cs b/osu.Game/Database/ILive.cs index 29e5756dba..9359b09eaf 100644 --- a/osu.Game/Database/ILive.cs +++ b/osu.Game/Database/ILive.cs @@ -9,7 +9,7 @@ namespace osu.Game.Database /// A wrapper to provide access to database backed classes in a thread-safe manner. /// /// The databased type. - public interface ILive where T : class + public interface ILive where T : class // TODO: Add IHasGuidPrimaryKey once we don't need EF support any more. { Guid ID { get; } From 857000b756765a99337d4b22c5085d4a27fdabfd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Oct 2021 16:29:46 +0900 Subject: [PATCH 2237/2442] Mark `IPresentImports` as covariant --- osu.Game/Database/IPresentImports.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/IPresentImports.cs b/osu.Game/Database/IPresentImports.cs index 6aa29a5083..fb3aad7ee1 100644 --- a/osu.Game/Database/IPresentImports.cs +++ b/osu.Game/Database/IPresentImports.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; namespace osu.Game.Database { - public interface IPresentImports + public interface IPresentImports where TModel : class { /// From e631653f4b1b471ec3794e7845b34492da88e801 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Oct 2021 16:30:12 +0900 Subject: [PATCH 2238/2442] Remove incorrectly committed `FodyWeavers` file --- osu.Game/FodyWeavers.xml | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 osu.Game/FodyWeavers.xml diff --git a/osu.Game/FodyWeavers.xml b/osu.Game/FodyWeavers.xml deleted file mode 100644 index cc07b89533..0000000000 --- a/osu.Game/FodyWeavers.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file From 63f0b0c93215cf1b0266b4891e6e82a160641951 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Oct 2021 16:35:55 +0900 Subject: [PATCH 2239/2442] Rename out of place interface name --- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- osu.Game/Database/ArchiveModelManager.cs | 8 ++++---- osu.Game/Database/{IPresentImports.cs => IPostImports.cs} | 4 ++-- osu.Game/OsuGame.cs | 2 +- osu.Game/Scoring/ScoreManager.cs | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) rename osu.Game/Database/{IPresentImports.cs => IPostImports.cs} (76%) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index b302df1516..f8181cd010 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -178,7 +178,7 @@ namespace osu.Game.Beatmaps /// /// Fired when the user requests to view the resulting import. /// - public Action>> PresentImport { set => beatmapModelManager.PresentImport = value; } + public Action>> PresentImport { set => beatmapModelManager.PostImport = value; } /// /// Delete a beatmap difficulty. diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 403bfdf621..9ad2dec12e 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -30,7 +30,7 @@ namespace osu.Game.Database /// /// The model type. /// The associated file join type. - public abstract class ArchiveModelManager : ICanAcceptFiles, IModelManager, IModelFileManager, IPresentImports + public abstract class ArchiveModelManager : ICanAcceptFiles, IModelManager, IModelFileManager, IPostImports where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete where TFileModel : class, INamedFileInfo, new() { @@ -200,12 +200,12 @@ namespace osu.Game.Database ? $"Imported {imported.First()}!" : $"Imported {imported.Count} {HumanisedModelName}s!"; - if (imported.Count > 0 && PresentImport != null) + if (imported.Count > 0 && PostImport != null) { notification.CompletionText += " Click to view."; notification.CompletionClickAction = () => { - PresentImport?.Invoke(imported); + PostImport?.Invoke(imported); return true; }; } @@ -249,7 +249,7 @@ namespace osu.Game.Database return import; } - public Action>> PresentImport { protected get; set; } + public Action>> PostImport { protected get; set; } /// /// Silently import an item from an . diff --git a/osu.Game/Database/IPresentImports.cs b/osu.Game/Database/IPostImports.cs similarity index 76% rename from osu.Game/Database/IPresentImports.cs rename to osu.Game/Database/IPostImports.cs index fb3aad7ee1..f09285089a 100644 --- a/osu.Game/Database/IPresentImports.cs +++ b/osu.Game/Database/IPostImports.cs @@ -6,12 +6,12 @@ using System.Collections.Generic; namespace osu.Game.Database { - public interface IPresentImports + public interface IPostImports where TModel : class { /// /// Fired when the user requests to view the resulting import. /// - public Action>> PresentImport { set; } + public Action>> PostImport { set; } } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 35ec213755..64c77c370e 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -627,7 +627,7 @@ namespace osu.Game BeatmapManager.PresentImport = items => PresentBeatmap(items.First().Value); ScoreManager.PostNotification = n => Notifications.Post(n); - ScoreManager.PresentImport = items => PresentScore(items.First().Value); + ScoreManager.PostImport = items => PresentScore(items.First().Value); // make config aware of how to lookup skins for on-screen display purposes. // if this becomes a more common thing, tracked settings should be reconsidered to allow local DI. diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index aa0ee4bbbb..922b4f0a38 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -25,7 +25,7 @@ using osu.Game.Rulesets.Scoring; namespace osu.Game.Scoring { - public class ScoreManager : IModelManager, IModelFileManager, IModelDownloader, ICanAcceptFiles, IPresentImports + public class ScoreManager : IModelManager, IModelFileManager, IModelDownloader, ICanAcceptFiles, IPostImports { private readonly Scheduler scheduler; private readonly Func difficulties; @@ -365,9 +365,9 @@ namespace osu.Game.Scoring #region Implementation of IPresentImports - public Action>> PresentImport + public Action>> PostImport { - set => scoreModelManager.PresentImport = value; + set => scoreModelManager.PostImport = value; } #endregion From 8bfdfe3672997fcacfed8a571629c17403348d9e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Oct 2021 16:54:00 +0900 Subject: [PATCH 2240/2442] Add literal string marker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Beatmaps/IBeatmapSetInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/IBeatmapSetInfo.cs b/osu.Game/Beatmaps/IBeatmapSetInfo.cs index 548a48367c..d5aded7b53 100644 --- a/osu.Game/Beatmaps/IBeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapSetInfo.cs @@ -53,6 +53,6 @@ namespace osu.Game.Beatmaps /// /// The filename for the storyboard. /// - string StoryboardFile => Files.FirstOrDefault(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename ?? string.Empty; + string StoryboardFile => Files.FirstOrDefault(f => f.Filename.EndsWith(@".osb", StringComparison.OrdinalIgnoreCase))?.Filename ?? string.Empty; } } From 4df5f931522f63553e4d2049b3147f5c7fbb650b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Oct 2021 16:50:29 +0900 Subject: [PATCH 2241/2442] Inline single usage of `StoryboardFile` to avoid interface default method woes --- osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs | 6 ++++-- osu.Game/Beatmaps/BeatmapSetInfo.cs | 4 ---- osu.Game/Beatmaps/IBeatmapSetInfo.cs | 6 ------ 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index 45112ae74c..f9889474ac 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -107,12 +107,14 @@ namespace osu.Game.Beatmaps { var decoder = Decoder.GetDecoder(stream); + var storyboardFilename = BeatmapSetInfo?.Files.FirstOrDefault(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename; + // todo: support loading from both set-wide storyboard *and* beatmap specific. - if (BeatmapSetInfo?.StoryboardFile == null) + if (string.IsNullOrEmpty(storyboardFilename)) storyboard = decoder.Decode(stream); else { - using (var secondaryStream = new LineBufferedReader(GetStream(BeatmapSetInfo.GetPathForFile(BeatmapSetInfo.StoryboardFile)))) + using (var secondaryStream = new LineBufferedReader(GetStream(BeatmapSetInfo.GetPathForFile(storyboardFilename)))) storyboard = decoder.Decode(stream, secondaryStream); } } diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 9f5a07ec43..8b01831b3c 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using JetBrains.Annotations; -using Newtonsoft.Json; using osu.Framework.Testing; using osu.Game.Database; @@ -62,9 +61,6 @@ namespace osu.Game.Beatmaps public string Hash { get; set; } - [JsonIgnore] - public string StoryboardFile => ((IBeatmapSetInfo)this).StoryboardFile; - /// /// Returns the storage path for the file in this beatmapset with the given filename, if any exists, otherwise null. /// The path returned is relative to the user file storage. diff --git a/osu.Game/Beatmaps/IBeatmapSetInfo.cs b/osu.Game/Beatmaps/IBeatmapSetInfo.cs index d5aded7b53..0cfb0c4242 100644 --- a/osu.Game/Beatmaps/IBeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapSetInfo.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using osu.Game.Database; #nullable enable @@ -49,10 +48,5 @@ namespace osu.Game.Beatmaps /// The maximum BPM of all beatmaps in this set. /// double MaxBPM { get; } - - /// - /// The filename for the storyboard. - /// - string StoryboardFile => Files.FirstOrDefault(f => f.Filename.EndsWith(@".osb", StringComparison.OrdinalIgnoreCase))?.Filename ?? string.Empty; } } From fd6b10656c638753a15efcb004f8848e109852bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Oct 2021 16:55:16 +0900 Subject: [PATCH 2242/2442] Add TODO reminder about ruleset reference transfer quirk --- osu.Game/Rulesets/IRulesetInfo.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Rulesets/IRulesetInfo.cs b/osu.Game/Rulesets/IRulesetInfo.cs index d4dec0de64..ded3ac4b58 100644 --- a/osu.Game/Rulesets/IRulesetInfo.cs +++ b/osu.Game/Rulesets/IRulesetInfo.cs @@ -38,6 +38,7 @@ namespace osu.Game.Rulesets var ruleset = Activator.CreateInstance(type) as Ruleset; // overwrite the pre-populated RulesetInfo with a potentially database attached copy. + // TODO: figure if we still want/need this after switching to realm. // ruleset.RulesetInfo = this; return ruleset; From 51b7dce16f2bfb031a135183cdabecbbf2a2359b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Oct 2021 16:55:51 +0900 Subject: [PATCH 2243/2442] Remove reference to `osu-web-10` --- osu.Game/Beatmaps/IBeatmapInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/IBeatmapInfo.cs b/osu.Game/Beatmaps/IBeatmapInfo.cs index 6153a0af08..d552a3f1e3 100644 --- a/osu.Game/Beatmaps/IBeatmapInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapInfo.cs @@ -50,7 +50,7 @@ namespace osu.Game.Beatmaps string Hash { get; } /// - /// MD5 is kept for legacy support (matching against replays, osu-web-10 etc.). + /// MD5 is kept for legacy support (matching against replays etc.). /// string MD5Hash { get; } From f293e008d953d334ebb291481502679519d2c88c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Oct 2021 17:00:22 +0900 Subject: [PATCH 2244/2442] Move `BeatmapInfo`'s `SearchableTerms` implementation to interface --- osu.Game/Beatmaps/BeatmapInfo.cs | 5 +---- osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs | 1 + osu.Game/Beatmaps/IBeatmapInfo.cs | 13 +++++++++++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 09f237a5de..cd5f8fb9a1 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -152,10 +152,7 @@ namespace osu.Game.Beatmaps [JsonIgnore] public DifficultyRating DifficultyRating => BeatmapDifficultyCache.GetDifficultyRating(StarDifficulty); - public string[] SearchableTerms => new[] - { - Version - }.Concat(Metadata?.SearchableTerms ?? Enumerable.Empty()).Where(s => !string.IsNullOrEmpty(s)).ToArray(); + public IEnumerable SearchableTerms => ((IBeatmapInfo)this).SearchableTerms; public override string ToString() => ((IBeatmapInfo)this).DisplayTitle; diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index f9889474ac..5fec8f7a89 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Linq; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; using osu.Framework.Logging; diff --git a/osu.Game/Beatmaps/IBeatmapInfo.cs b/osu.Game/Beatmaps/IBeatmapInfo.cs index d552a3f1e3..fb30b0279c 100644 --- a/osu.Game/Beatmaps/IBeatmapInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapInfo.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using osu.Framework.Localisation; using osu.Game.Database; using osu.Game.Rulesets; @@ -22,7 +23,7 @@ namespace osu.Game.Beatmaps /// /// The metadata representing this beatmap. May be shared between multiple beatmaps. /// - IBeatmapMetadataInfo Metadata { get; } + IBeatmapMetadataInfo? Metadata { get; } /// /// The difficulty settings for this beatmap. @@ -76,12 +77,20 @@ namespace osu.Game.Beatmaps { get { - var metadata = Metadata.DisplayTitleRomanisable; + var metadata = closestMetadata.DisplayTitleRomanisable; return new RomanisableString($"{metadata.GetPreferred(true)} {versionString}".Trim(), $"{metadata.GetPreferred(false)} {versionString}".Trim()); } } + string[] SearchableTerms => new[] + { + DifficultyName + }.Concat(closestMetadata.SearchableTerms).Where(s => !string.IsNullOrEmpty(s)).ToArray(); + private string versionString => string.IsNullOrEmpty(DifficultyName) ? string.Empty : $"[{DifficultyName}]"; + + // temporary helper methods until we figure which metadata should be where. + private IBeatmapMetadataInfo closestMetadata => (Metadata ?? BeatmapSet.Metadata)!; } } From 95f1cc85d4b57a5eb710da9ace0cc00d87952197 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Oct 2021 17:12:40 +0900 Subject: [PATCH 2245/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 8fad10d247..fb3e4b3bbd 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ba118c5240..e53b83100e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 37931d0c38..4e7053d816 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 6ac0601d2cdba14f61f3bc589ab8198f5ecb63da Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Oct 2021 17:18:08 +0900 Subject: [PATCH 2246/2442] Fix incorrect csproj merge --- 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 36c5fd89bf..4877ddf725 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From 853cf6feaa165e833ecb7ca18c6cdffe8ca6e005 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Oct 2021 17:35:53 +0900 Subject: [PATCH 2247/2442] Rename last remaining `BeatmapInfo Beatmap` usage --- .../TestSceneAccuracyHeatmap.cs | 2 +- .../Beatmaps/IO/ImportBeatmapTest.cs | 2 +- osu.Game.Tests/Scores/IO/ImportScoreTest.cs | 4 ++-- .../Background/TestSceneUserDimBackgrounds.cs | 2 +- .../Gameplay/TestSceneReplayRecorder.cs | 2 +- .../Gameplay/TestSceneReplayRecording.cs | 2 +- .../Gameplay/TestSceneSpectatorPlayback.cs | 2 +- .../TestSceneMultiplayerResults.cs | 2 +- .../TestSceneMultiplayerTeamResults.cs | 2 +- .../Navigation/TestScenePresentScore.cs | 2 +- .../Online/TestSceneUserProfileScores.cs | 8 ++++---- .../Visual/Ranking/TestSceneAccuracyCircle.cs | 2 +- .../TestSceneExpandedPanelMiddleContent.cs | 8 ++++---- .../Visual/Ranking/TestSceneResultsScreen.cs | 4 ++-- .../SongSelect/TestSceneBeatmapLeaderboard.cs | 20 +++++++++---------- .../SongSelect/TestScenePlaySongSelect.cs | 4 ++-- .../TestSceneDeleteLocalScore.cs | 2 +- .../Requests/Responses/APILegacyScoreInfo.cs | 2 +- osu.Game/Online/Rooms/MultiplayerScore.cs | 2 +- osu.Game/Online/Spectator/SpectatorClient.cs | 2 +- osu.Game/OsuGame.cs | 2 +- osu.Game/OsuGameBase.cs | 2 +- .../Overlays/BeatmapSet/Scores/ScoreTable.cs | 2 +- .../BeatmapSet/Scores/ScoresContainer.cs | 2 +- .../Scores/TopScoreStatisticsSection.cs | 2 +- .../Sections/Ranks/DrawableProfileScore.cs | 4 ++-- osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs | 4 ++-- osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs | 4 ++-- osu.Game/Scoring/ScoreInfo.cs | 5 +++-- osu.Game/Scoring/ScoreManager.cs | 12 +++++------ osu.Game/Scoring/ScorePerformanceCache.cs | 2 +- osu.Game/Scoring/ScoreStore.cs | 6 +++--- .../Multiplayer/Spectate/PlayerArea.cs | 2 +- osu.Game/Screens/Play/Player.cs | 2 +- osu.Game/Screens/Play/SoloPlayer.cs | 2 +- .../Expanded/ExpandedPanelMiddleContent.cs | 2 +- osu.Game/Screens/Ranking/SoloResultsScreen.cs | 4 ++-- .../Ranking/Statistics/StatisticsPanel.cs | 2 +- .../Select/BeatmapClearScoresDialog.cs | 2 +- .../Select/Leaderboards/BeatmapLeaderboard.cs | 2 +- osu.Game/Screens/Select/PlaySongSelect.cs | 2 +- osu.Game/Screens/Spectate/SpectatorScreen.cs | 2 +- osu.Game/Tests/TestScoreInfo.cs | 2 +- 43 files changed, 74 insertions(+), 73 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneAccuracyHeatmap.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneAccuracyHeatmap.cs index 10d9d7ffde..79150a1941 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneAccuracyHeatmap.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneAccuracyHeatmap.cs @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Tests { Position = new Vector2(100, 300), }, - accuracyHeatmap = new TestAccuracyHeatmap(new ScoreInfo { Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }) + accuracyHeatmap = new TestAccuracyHeatmap(new ScoreInfo { BeatmapInfo = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index b536fc61b7..dce01448f4 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -950,7 +950,7 @@ namespace osu.Game.Tests.Beatmaps.IO return ImportScoreTest.LoadScoreIntoOsu(osu, new ScoreInfo { OnlineScoreID = 2, - Beatmap = beatmapInfo, + BeatmapInfo = beatmapInfo, BeatmapInfoID = beatmapInfo.ID }, new ImportScoreTest.TestArchiveReader()); } diff --git a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs index cd7d744f53..2cd02329b7 100644 --- a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs +++ b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs @@ -141,7 +141,7 @@ namespace osu.Game.Tests.Scores.IO var beatmapManager = osu.Dependencies.Get(); var scoreManager = osu.Dependencies.Get(); - beatmapManager.Delete(beatmapManager.QueryBeatmapSet(s => s.Beatmaps.Any(b => b.ID == imported.Beatmap.ID))); + beatmapManager.Delete(beatmapManager.QueryBeatmapSet(s => s.Beatmaps.Any(b => b.ID == imported.BeatmapInfo.ID))); Assert.That(scoreManager.Query(s => s.ID == imported.ID).DeletePending, Is.EqualTo(true)); var secondImport = await LoadScoreIntoOsu(osu, imported); @@ -181,7 +181,7 @@ namespace osu.Game.Tests.Scores.IO { var beatmapManager = osu.Dependencies.Get(); - score.Beatmap ??= beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps.First(); + score.BeatmapInfo ??= beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps.First(); score.Ruleset ??= new OsuRuleset().RulesetInfo; var scoreManager = osu.Dependencies.Get(); diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index 12a85c3f26..693c66ccb0 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -232,7 +232,7 @@ namespace osu.Game.Tests.Visual.Background AddStep("Transition to Results", () => player.Push(results = new FadeAccessibleResults(new ScoreInfo { User = new User { Username = "osu!" }, - Beatmap = new TestBeatmap(Ruleset.Value).BeatmapInfo, + BeatmapInfo = new TestBeatmap(Ruleset.Value).BeatmapInfo, Ruleset = Ruleset.Value, }))); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs index d89fd322d1..c8040f42f0 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs @@ -60,7 +60,7 @@ namespace osu.Game.Tests.Visual.Gameplay Recorder = recorder = new TestReplayRecorder(new Score { Replay = replay, - ScoreInfo = { Beatmap = gameplayState.Beatmap.BeatmapInfo } + ScoreInfo = { BeatmapInfo = gameplayState.Beatmap.BeatmapInfo } }) { ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos), diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs index 07514ad51a..3545fc96e8 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs @@ -51,7 +51,7 @@ namespace osu.Game.Tests.Visual.Gameplay Recorder = new TestReplayRecorder(new Score { Replay = replay, - ScoreInfo = { Beatmap = gameplayState.Beatmap.BeatmapInfo } + ScoreInfo = { BeatmapInfo = gameplayState.Beatmap.BeatmapInfo } }) { ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs index 07ff35f77b..b4de060578 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs @@ -356,7 +356,7 @@ namespace osu.Game.Tests.Visual.Gameplay internal class TestReplayRecorder : ReplayRecorder { public TestReplayRecorder() - : base(new Score { ScoreInfo = { Beatmap = new BeatmapInfo() } }) + : base(new Score { ScoreInfo = { BeatmapInfo = new BeatmapInfo() } }) { } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerResults.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerResults.cs index ff06d4d9c7..5032cdaec7 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerResults.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerResults.cs @@ -30,7 +30,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Accuracy = 0.8, MaxCombo = 500, Combo = 250, - Beatmap = beatmapInfo, + BeatmapInfo = beatmapInfo, User = new User { Username = "Test user" }, Date = DateTimeOffset.Now, OnlineScoreID = 12345, diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs index 0a8bda7ec0..99d5fd46e9 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs @@ -34,7 +34,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Accuracy = 0.8, MaxCombo = 500, Combo = 250, - Beatmap = beatmapInfo, + BeatmapInfo = beatmapInfo, User = new User { Username = "Test user" }, Date = DateTimeOffset.Now, OnlineScoreID = 12345, diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs index 52b577b402..ee84d775d2 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs @@ -130,7 +130,7 @@ namespace osu.Game.Tests.Visual.Navigation { Hash = Guid.NewGuid().ToString(), OnlineScoreID = i, - Beatmap = beatmap.Beatmaps.First(), + BeatmapInfo = beatmap.Beatmaps.First(), Ruleset = ruleset ?? new OsuRuleset().RulesetInfo }).Result; }); diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs index 5dca218531..513631a221 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs @@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.Online { PP = 1047.21, Rank = ScoreRank.SH, - Beatmap = new BeatmapInfo + BeatmapInfo = new BeatmapInfo { Metadata = new BeatmapMetadata { @@ -46,7 +46,7 @@ namespace osu.Game.Tests.Visual.Online { PP = 134.32, Rank = ScoreRank.A, - Beatmap = new BeatmapInfo + BeatmapInfo = new BeatmapInfo { Metadata = new BeatmapMetadata { @@ -68,7 +68,7 @@ namespace osu.Game.Tests.Visual.Online { PP = 96.83, Rank = ScoreRank.S, - Beatmap = new BeatmapInfo + BeatmapInfo = new BeatmapInfo { Metadata = new BeatmapMetadata { @@ -84,7 +84,7 @@ namespace osu.Game.Tests.Visual.Online var noPPScore = new ScoreInfo { Rank = ScoreRank.B, - Beatmap = new BeatmapInfo + BeatmapInfo = new BeatmapInfo { Metadata = new BeatmapMetadata { diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs index a5e2f02f31..df8500fab2 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs @@ -71,7 +71,7 @@ namespace osu.Game.Tests.Visual.Ranking Id = 2, Username = "peppy", }, - Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo, + BeatmapInfo = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo, Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() }, TotalScore = 2845370, Accuracy = accuracy, diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs index 5180854aba..899f351a2a 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs @@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.Ranking AddStep("show example score", () => showPanel(new TestScoreInfo(new OsuRuleset().RulesetInfo) { - Beatmap = createTestBeatmap(author) + BeatmapInfo = createTestBeatmap(author) })); } @@ -46,7 +46,7 @@ namespace osu.Game.Tests.Visual.Ranking AddStep("show excess mods score", () => showPanel(new TestScoreInfo(new OsuRuleset().RulesetInfo, true) { - Beatmap = createTestBeatmap(author) + BeatmapInfo = createTestBeatmap(author) })); AddAssert("mapper name present", () => this.ChildrenOfType().Any(spriteText => spriteText.Current.Value == "mapper_name")); @@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.Ranking { AddStep("show example score", () => showPanel(new TestScoreInfo(new OsuRuleset().RulesetInfo) { - Beatmap = createTestBeatmap(null) + BeatmapInfo = createTestBeatmap(null) })); AddAssert("mapped by text not present", () => @@ -79,7 +79,7 @@ namespace osu.Game.Tests.Visual.Ranking showPanel(new TestScoreInfo(ruleset.RulesetInfo) { Mods = mods, - Beatmap = beatmap, + BeatmapInfo = beatmap, Date = default, }); }); diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs index 631455b727..8d5d0ba8c7 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs @@ -337,8 +337,8 @@ namespace osu.Game.Tests.Visual.Ranking public UnrankedSoloResultsScreen(ScoreInfo score) : base(score, true) { - Score.Beatmap.OnlineBeatmapID = 0; - Score.Beatmap.Status = BeatmapSetOnlineStatus.Pending; + Score.BeatmapInfo.OnlineBeatmapID = 0; + Score.BeatmapInfo.Status = BeatmapSetOnlineStatus.Pending; } protected override void LoadComplete() diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index 95cf6a9903..13b769c80a 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -197,7 +197,7 @@ namespace osu.Game.Tests.Visual.SongSelect MaxCombo = 244, TotalScore = 1707827, //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - Beatmap = beatmapInfo, + BeatmapInfo = beatmapInfo, User = new User { Id = 6602580, @@ -216,7 +216,7 @@ namespace osu.Game.Tests.Visual.SongSelect MaxCombo = 244, TotalScore = 1707827, //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - Beatmap = beatmapInfo, + BeatmapInfo = beatmapInfo, User = new User { Id = 4608074, @@ -235,7 +235,7 @@ namespace osu.Game.Tests.Visual.SongSelect MaxCombo = 244, TotalScore = 1707827, //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - Beatmap = beatmapInfo, + BeatmapInfo = beatmapInfo, User = new User { Id = 1014222, @@ -254,7 +254,7 @@ namespace osu.Game.Tests.Visual.SongSelect MaxCombo = 244, TotalScore = 1707827, //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - Beatmap = beatmapInfo, + BeatmapInfo = beatmapInfo, User = new User { Id = 1541390, @@ -273,7 +273,7 @@ namespace osu.Game.Tests.Visual.SongSelect MaxCombo = 244, TotalScore = 1707827, //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - Beatmap = beatmapInfo, + BeatmapInfo = beatmapInfo, User = new User { Id = 2243452, @@ -292,7 +292,7 @@ namespace osu.Game.Tests.Visual.SongSelect MaxCombo = 244, TotalScore = 1707827, //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - Beatmap = beatmapInfo, + BeatmapInfo = beatmapInfo, User = new User { Id = 2705430, @@ -311,7 +311,7 @@ namespace osu.Game.Tests.Visual.SongSelect MaxCombo = 244, TotalScore = 1707827, //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - Beatmap = beatmapInfo, + BeatmapInfo = beatmapInfo, User = new User { Id = 7151382, @@ -330,7 +330,7 @@ namespace osu.Game.Tests.Visual.SongSelect MaxCombo = 244, TotalScore = 1707827, //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - Beatmap = beatmapInfo, + BeatmapInfo = beatmapInfo, User = new User { Id = 2051389, @@ -349,7 +349,7 @@ namespace osu.Game.Tests.Visual.SongSelect MaxCombo = 244, TotalScore = 1707827, //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - Beatmap = beatmapInfo, + BeatmapInfo = beatmapInfo, User = new User { Id = 6169483, @@ -368,7 +368,7 @@ namespace osu.Game.Tests.Visual.SongSelect MaxCombo = 244, TotalScore = 1707827, //Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, - Beatmap = beatmapInfo, + BeatmapInfo = beatmapInfo, User = new User { Id = 6702666, diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index f9e81d3da6..78040b3d6a 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -805,7 +805,7 @@ namespace osu.Game.Tests.Visual.SongSelect songSelect.PresentScore(new ScoreInfo { User = new User { Username = "woo" }, - Beatmap = getPresentBeatmap(), + BeatmapInfo = getPresentBeatmap(), Ruleset = getPresentBeatmap().Ruleset }); }); @@ -837,7 +837,7 @@ namespace osu.Game.Tests.Visual.SongSelect songSelect.PresentScore(new ScoreInfo { User = new User { Username = "woo" }, - Beatmap = getPresentBeatmap(), + BeatmapInfo = getPresentBeatmap(), Ruleset = getPresentBeatmap().Ruleset }); }); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index f58dbef145..c237fcaebf 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -92,7 +92,7 @@ namespace osu.Game.Tests.Visual.UserInterface var score = new ScoreInfo { OnlineScoreID = i, - Beatmap = beatmapInfo, + BeatmapInfo = beatmapInfo, BeatmapInfoID = beatmapInfo.ID, Accuracy = RNG.NextDouble(), TotalScore = RNG.Next(1, 1000000), diff --git a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs index 18a0db3928..aaf2dccc82 100644 --- a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs @@ -37,7 +37,7 @@ namespace osu.Game.Online.API.Requests.Responses OnlineScoreID = OnlineScoreID, Date = Date, PP = PP, - Beatmap = BeatmapInfo, + BeatmapInfo = BeatmapInfo, RulesetID = OnlineRulesetID, Hash = Replay ? "online" : string.Empty, // todo: temporary? Rank = Rank, diff --git a/osu.Game/Online/Rooms/MultiplayerScore.cs b/osu.Game/Online/Rooms/MultiplayerScore.cs index 30c1d2f826..7ec34e70d5 100644 --- a/osu.Game/Online/Rooms/MultiplayerScore.cs +++ b/osu.Game/Online/Rooms/MultiplayerScore.cs @@ -70,7 +70,7 @@ namespace osu.Game.Online.Rooms OnlineScoreID = ID, TotalScore = TotalScore, MaxCombo = MaxCombo, - Beatmap = playlistItem.Beatmap.Value, + BeatmapInfo = playlistItem.Beatmap.Value, BeatmapInfoID = playlistItem.BeatmapID, Ruleset = playlistItem.Ruleset.Value, RulesetID = playlistItem.RulesetID, diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index d55ad45ff5..b597b2f214 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -144,7 +144,7 @@ namespace osu.Game.Online.Spectator IsPlaying = true; // transfer state at point of beginning play - currentState.BeatmapID = score.ScoreInfo.Beatmap.OnlineBeatmapID; + currentState.BeatmapID = score.ScoreInfo.BeatmapInfo.OnlineBeatmapID; currentState.RulesetID = score.ScoreInfo.RulesetID; currentState.Mods = score.ScoreInfo.Mods.Select(m => new APIMod(m)).ToArray(); diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 99925bb1fb..9dd879fd7e 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -482,7 +482,7 @@ namespace osu.Game return; } - var databasedBeatmap = BeatmapManager.QueryBeatmap(b => b.ID == databasedScoreInfo.Beatmap.ID); + var databasedBeatmap = BeatmapManager.QueryBeatmap(b => b.ID == databasedScoreInfo.BeatmapInfo.ID); if (databasedBeatmap == null) { diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 02de92e805..aec06e18f6 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -250,7 +250,7 @@ namespace osu.Game List getBeatmapScores(BeatmapSetInfo set) { var beatmapIds = BeatmapManager.QueryBeatmaps(b => b.BeatmapSetInfoID == set.ID).Select(b => b.ID).ToList(); - return ScoreManager.QueryScores(s => beatmapIds.Contains(s.Beatmap.ID)).ToList(); + return ScoreManager.QueryScores(s => beatmapIds.Contains(s.BeatmapInfo.ID)).ToList(); } BeatmapManager.ItemRemoved.BindValueChanged(i => diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index 8fe1d35b62..018faf2011 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -172,7 +172,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { Text = score.MaxCombo.ToLocalisableString(@"0\x"), Font = OsuFont.GetFont(size: text_size), - Colour = score.MaxCombo == score.Beatmap?.MaxCombo ? highAccuracyColour : Color4.White + Colour = score.MaxCombo == score.BeatmapInfo?.MaxCombo ? highAccuracyColour : Color4.White } }; diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index fb1769fbe1..82657afc86 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -74,7 +74,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores var topScore = ordered.Result.First(); - scoreTable.DisplayScores(ordered.Result, topScore.Beatmap?.Status.GrantsPerformancePoints() == true); + scoreTable.DisplayScores(ordered.Result, topScore.BeatmapInfo?.Status.GrantsPerformancePoints() == true); scoreTable.Show(); var userScore = value.UserScore; diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs index 883e83ce6e..630aa8fe53 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs @@ -115,7 +115,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores accuracyColumn.Text = value.DisplayAccuracy; maxComboColumn.Text = value.MaxCombo.ToLocalisableString(@"0\x"); - ppColumn.Alpha = value.Beatmap?.Status.GrantsPerformancePoints() == true ? 1 : 0; + ppColumn.Alpha = value.BeatmapInfo?.Status.GrantsPerformancePoints() == true ? 1 : 0; ppColumn.Text = value.PP?.ToLocalisableString(@"N0"); statisticsColumns.ChildrenEnumerable = value.GetStatisticsForDisplay().Select(createStatisticsColumn); diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs index c221f070df..3561e9700e 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs @@ -78,7 +78,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks Spacing = new Vector2(0, 2), Children = new Drawable[] { - new ScoreBeatmapMetadataContainer(Score.Beatmap), + new ScoreBeatmapMetadataContainer(Score.BeatmapInfo), new FillFlowContainer { AutoSizeAxes = Axes.Both, @@ -88,7 +88,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks { new OsuSpriteText { - Text = $"{Score.Beatmap.Version}", + Text = $"{Score.BeatmapInfo.Version}", Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular), Colour = colours.Yellow }, diff --git a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs index 2e1a29372d..a1658b4cf3 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs @@ -70,7 +70,7 @@ namespace osu.Game.Scoring.Legacy scoreInfo.Mods = scoreInfo.Mods.Append(currentRuleset.CreateMod()).ToArray(); currentBeatmap = workingBeatmap.GetPlayableBeatmap(currentRuleset.RulesetInfo, scoreInfo.Mods); - scoreInfo.Beatmap = currentBeatmap.BeatmapInfo; + scoreInfo.BeatmapInfo = currentBeatmap.BeatmapInfo; /* score.HpGraphString = */ sr.ReadString(); @@ -119,7 +119,7 @@ namespace osu.Game.Scoring.Legacy // before returning for database import, we must restore the database-sourced BeatmapInfo. // if not, the clone operation in GetPlayableBeatmap will cause a dereference and subsequent database exception. - score.ScoreInfo.Beatmap = workingBeatmap.BeatmapInfo; + score.ScoreInfo.BeatmapInfo = workingBeatmap.BeatmapInfo; return score; } diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs index 288552879c..58e4192f77 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs @@ -34,7 +34,7 @@ namespace osu.Game.Scoring.Legacy this.score = score; this.beatmap = beatmap; - if (score.ScoreInfo.Beatmap.RulesetID < 0 || score.ScoreInfo.Beatmap.RulesetID > 3) + if (score.ScoreInfo.BeatmapInfo.RulesetID < 0 || score.ScoreInfo.BeatmapInfo.RulesetID > 3) throw new ArgumentException("Only scores in the osu, taiko, catch, or mania rulesets can be encoded to the legacy score format.", nameof(score)); } @@ -44,7 +44,7 @@ namespace osu.Game.Scoring.Legacy { sw.Write((byte)(score.ScoreInfo.Ruleset.ID ?? 0)); sw.Write(LATEST_VERSION); - sw.Write(score.ScoreInfo.Beatmap.MD5Hash); + sw.Write(score.ScoreInfo.BeatmapInfo.MD5Hash); sw.Write(score.ScoreInfo.UserString); sw.Write($"lazer-{score.ScoreInfo.UserString}-{score.ScoreInfo.Date}".ComputeMD5Hash()); sw.Write((ushort)(score.ScoreInfo.GetCount300() ?? 0)); diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 890ead40e3..5cf22f7945 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -150,7 +150,8 @@ namespace osu.Game.Scoring public int BeatmapInfoID { get; set; } [JsonIgnore] - public virtual BeatmapInfo Beatmap { get; set; } + [Column("Beatmap")] + public virtual BeatmapInfo BeatmapInfo { get; set; } [JsonIgnore] public long? OnlineScoreID { get; set; } @@ -252,7 +253,7 @@ namespace osu.Game.Scoring return clone; } - public override string ToString() => $"{User} playing {Beatmap}"; + public override string ToString() => $"{User} playing {BeatmapInfo}"; public bool Equals(ScoreInfo other) { diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index d83b4e3f1d..27d087dc30 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -67,7 +67,7 @@ namespace osu.Game.Scoring // Compute difficulties asynchronously first to prevent blocking via the GetTotalScore() call below. foreach (var s in scores) { - await difficultyCache.GetDifficultyAsync(s.Beatmap, s.Ruleset, s.Mods, cancellationToken).ConfigureAwait(false); + await difficultyCache.GetDifficultyAsync(s.BeatmapInfo, s.Ruleset, s.Mods, cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); } } @@ -126,7 +126,7 @@ namespace osu.Game.Scoring /// The total score. public async Task GetTotalScoreAsync([NotNull] ScoreInfo score, ScoringMode mode = ScoringMode.Standardised, CancellationToken cancellationToken = default) { - if (score.Beatmap == null) + if (score.BeatmapInfo == null) return score.TotalScore; int beatmapMaxCombo; @@ -147,18 +147,18 @@ namespace osu.Game.Scoring // This score is guaranteed to be an osu!stable score. // The combo must be determined through either the beatmap's max combo value or the difficulty calculator, as lazer's scoring has changed and the score statistics cannot be used. - if (score.Beatmap.MaxCombo != null) - beatmapMaxCombo = score.Beatmap.MaxCombo.Value; + if (score.BeatmapInfo.MaxCombo != null) + beatmapMaxCombo = score.BeatmapInfo.MaxCombo.Value; else { - if (score.Beatmap.ID == 0 || difficulties == null) + if (score.BeatmapInfo.ID == 0 || difficulties == null) { // We don't have enough information (max combo) to compute the score, so use the provided score. return score.TotalScore; } // We can compute the max combo locally after the async beatmap difficulty computation. - var difficulty = await difficulties().GetDifficultyAsync(score.Beatmap, score.Ruleset, score.Mods, cancellationToken).ConfigureAwait(false); + var difficulty = await difficulties().GetDifficultyAsync(score.BeatmapInfo, score.Ruleset, score.Mods, cancellationToken).ConfigureAwait(false); beatmapMaxCombo = difficulty.MaxCombo; } } diff --git a/osu.Game/Scoring/ScorePerformanceCache.cs b/osu.Game/Scoring/ScorePerformanceCache.cs index bb15983de3..82685e9a04 100644 --- a/osu.Game/Scoring/ScorePerformanceCache.cs +++ b/osu.Game/Scoring/ScorePerformanceCache.cs @@ -34,7 +34,7 @@ namespace osu.Game.Scoring { var score = lookup.ScoreInfo; - var attributes = await difficultyCache.GetDifficultyAsync(score.Beatmap, score.Ruleset, score.Mods, token).ConfigureAwait(false); + var attributes = await difficultyCache.GetDifficultyAsync(score.BeatmapInfo, score.Ruleset, score.Mods, token).ConfigureAwait(false); // Performance calculation requires the beatmap and ruleset to be locally available. If not, return a default value. if (attributes.Attributes == null) diff --git a/osu.Game/Scoring/ScoreStore.cs b/osu.Game/Scoring/ScoreStore.cs index f5c5cd5dad..fd1f5ae3ec 100644 --- a/osu.Game/Scoring/ScoreStore.cs +++ b/osu.Game/Scoring/ScoreStore.cs @@ -17,9 +17,9 @@ namespace osu.Game.Scoring protected override IQueryable AddIncludesForConsumption(IQueryable query) => base.AddIncludesForConsumption(query) - .Include(s => s.Beatmap) - .Include(s => s.Beatmap).ThenInclude(b => b.Metadata) - .Include(s => s.Beatmap).ThenInclude(b => b.BeatmapSet).ThenInclude(s => s.Metadata) + .Include(s => s.BeatmapInfo) + .Include(s => s.BeatmapInfo).ThenInclude(b => b.Metadata) + .Include(s => s.BeatmapInfo).ThenInclude(b => b.BeatmapSet).ThenInclude(s => s.Metadata) .Include(s => s.Ruleset); } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerArea.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerArea.cs index 95ccc08608..c3190cd845 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerArea.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerArea.cs @@ -84,7 +84,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate Score = score; - gameplayContent.Child = new PlayerIsolationContainer(beatmapManager.GetWorkingBeatmap(Score.ScoreInfo.Beatmap), Score.ScoreInfo.Ruleset, Score.ScoreInfo.Mods) + gameplayContent.Child = new PlayerIsolationContainer(beatmapManager.GetWorkingBeatmap(Score.ScoreInfo.BeatmapInfo), Score.ScoreInfo.Ruleset, Score.ScoreInfo.Mods) { RelativeSizeAxes = Axes.Both, Child = stack = new OsuScreenStack() diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a05a8f5056..69a1c6c8ce 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -164,7 +164,7 @@ namespace osu.Game.Screens.Play Score = CreateScore(); // ensure the score is in a consistent state with the current player. - Score.ScoreInfo.Beatmap = Beatmap.Value.BeatmapInfo; + Score.ScoreInfo.BeatmapInfo = Beatmap.Value.BeatmapInfo; Score.ScoreInfo.Ruleset = ruleset.RulesetInfo; Score.ScoreInfo.Mods = Mods.Value.ToArray(); diff --git a/osu.Game/Screens/Play/SoloPlayer.cs b/osu.Game/Screens/Play/SoloPlayer.cs index d90e8e0168..675cb71311 100644 --- a/osu.Game/Screens/Play/SoloPlayer.cs +++ b/osu.Game/Screens/Play/SoloPlayer.cs @@ -38,7 +38,7 @@ namespace osu.Game.Screens.Play protected override APIRequest CreateSubmissionRequest(Score score, long token) { - var beatmap = score.ScoreInfo.Beatmap; + var beatmap = score.ScoreInfo.BeatmapInfo; Debug.Assert(beatmap.OnlineBeatmapID != null); diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index bcb5e7999f..262d1e8293 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -60,7 +60,7 @@ namespace osu.Game.Screens.Ranking.Expanded [BackgroundDependencyLoader] private void load(BeatmapDifficultyCache beatmapDifficultyCache) { - var beatmap = score.Beatmap; + var beatmap = score.BeatmapInfo; var metadata = beatmap.BeatmapSet?.Metadata ?? beatmap.Metadata; var creator = metadata.Author?.Username; diff --git a/osu.Game/Screens/Ranking/SoloResultsScreen.cs b/osu.Game/Screens/Ranking/SoloResultsScreen.cs index 9bc696948f..5e582a8dcb 100644 --- a/osu.Game/Screens/Ranking/SoloResultsScreen.cs +++ b/osu.Game/Screens/Ranking/SoloResultsScreen.cs @@ -27,10 +27,10 @@ namespace osu.Game.Screens.Ranking protected override APIRequest FetchScores(Action> scoresCallback) { - if (Score.Beatmap.OnlineBeatmapID == null || Score.Beatmap.Status <= BeatmapSetOnlineStatus.Pending) + if (Score.BeatmapInfo.OnlineBeatmapID == null || Score.BeatmapInfo.Status <= BeatmapSetOnlineStatus.Pending) return null; - getScoreRequest = new GetScoresRequest(Score.Beatmap, Score.Ruleset); + getScoreRequest = new GetScoresRequest(Score.BeatmapInfo, Score.Ruleset); getScoreRequest.Success += r => scoresCallback?.Invoke(r.Scores.Where(s => s.OnlineScoreID != Score.OnlineScoreID).Select(s => s.CreateScoreInfo(rulesets))); return getScoreRequest; } diff --git a/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs b/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs index f1ae1f9d73..bc62bcf2b2 100644 --- a/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs +++ b/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs @@ -102,7 +102,7 @@ namespace osu.Game.Screens.Ranking.Statistics // Todo: The placement of this is temporary. Eventually we'll both generate the playable beatmap _and_ run through it in a background task to generate the hit events. Task.Run(() => { - playableBeatmap = beatmapManager.GetWorkingBeatmap(newScore.Beatmap).GetPlayableBeatmap(newScore.Ruleset, newScore.Mods ?? Array.Empty()); + playableBeatmap = beatmapManager.GetWorkingBeatmap(newScore.BeatmapInfo).GetPlayableBeatmap(newScore.Ruleset, newScore.Mods ?? Array.Empty()); }, loadCancellation.Token).ContinueWith(t => Schedule(() => { var rows = new FillFlowContainer diff --git a/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs index 8c33b1ea0b..4970db8955 100644 --- a/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs +++ b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs @@ -29,7 +29,7 @@ namespace osu.Game.Screens.Select Text = @"Yes. Please.", Action = () => { - Task.Run(() => scoreManager.Delete(scoreManager.QueryScores(s => !s.DeletePending && s.Beatmap.ID == beatmapInfo.ID).ToList())) + Task.Run(() => scoreManager.Delete(scoreManager.QueryScores(s => !s.DeletePending && s.BeatmapInfo.ID == beatmapInfo.ID).ToList())) .ContinueWith(_ => onCompletion); } }, diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 2fdb41a1a1..07300635aa 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -141,7 +141,7 @@ namespace osu.Game.Screens.Select.Leaderboards if (Scope == BeatmapLeaderboardScope.Local) { var scores = scoreManager - .QueryScores(s => !s.DeletePending && s.Beatmap.ID == BeatmapInfo.ID && s.Ruleset.ID == ruleset.Value.ID); + .QueryScores(s => !s.DeletePending && s.BeatmapInfo.ID == BeatmapInfo.ID && s.Ruleset.ID == ruleset.Value.ID); if (filterMods && !mods.Value.Any()) { diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 418cf23ce7..94aa165785 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -39,7 +39,7 @@ namespace osu.Game.Screens.Select } protected void PresentScore(ScoreInfo score) => - FinaliseSelection(score.Beatmap, score.Ruleset, () => this.Push(new SoloResultsScreen(score, false))); + FinaliseSelection(score.BeatmapInfo, score.Ruleset, () => this.Push(new SoloResultsScreen(score, false))); protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea(); diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs index 71bcc336f3..7861d4cb72 100644 --- a/osu.Game/Screens/Spectate/SpectatorScreen.cs +++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs @@ -165,7 +165,7 @@ namespace osu.Game.Screens.Spectate { ScoreInfo = new ScoreInfo { - Beatmap = resolvedBeatmap, + BeatmapInfo = resolvedBeatmap, User = user, Mods = spectatorState.Mods.Select(m => m.ToMod(resolvedRuleset)).ToArray(), Ruleset = resolvedRuleset.RulesetInfo, diff --git a/osu.Game/Tests/TestScoreInfo.cs b/osu.Game/Tests/TestScoreInfo.cs index 5ce6aae647..719d31b092 100644 --- a/osu.Game/Tests/TestScoreInfo.cs +++ b/osu.Game/Tests/TestScoreInfo.cs @@ -23,7 +23,7 @@ namespace osu.Game.Tests CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", }; - Beatmap = new TestBeatmap(ruleset).BeatmapInfo; + BeatmapInfo = new TestBeatmap(ruleset).BeatmapInfo; Ruleset = ruleset; RulesetID = ruleset.ID ?? 0; From 2e3450b3f58e4290289194c0fd58ecf28fe3931c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 4 Oct 2021 20:20:24 +0900 Subject: [PATCH 2248/2442] Make Mods readonly --- osu.Game/Screens/Play/GameplayState.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/GameplayState.cs b/osu.Game/Screens/Play/GameplayState.cs index ba08c946d2..d4da17ce37 100644 --- a/osu.Game/Screens/Play/GameplayState.cs +++ b/osu.Game/Screens/Play/GameplayState.cs @@ -30,7 +30,7 @@ namespace osu.Game.Screens.Play /// /// The mods applied to the gameplay. /// - public IReadOnlyList Mods; + public readonly IReadOnlyList Mods; /// /// A bindable tracking the last judgement result applied to any hit object. From 5aae673240e2263cb15d2b9e7c1ad055d17ecaf9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 4 Oct 2021 20:33:54 +0900 Subject: [PATCH 2249/2442] Use GameplayState --- osu.Game/Screens/Play/GameplayState.cs | 6 +++++ .../HUD/DefaultPerformancePointsCounter.cs | 22 +++++++++---------- osu.Game/Screens/Play/Player.cs | 2 +- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayState.cs b/osu.Game/Screens/Play/GameplayState.cs index d4da17ce37..ef4967b34d 100644 --- a/osu.Game/Screens/Play/GameplayState.cs +++ b/osu.Game/Screens/Play/GameplayState.cs @@ -7,6 +7,7 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; +using osu.Game.Scoring; #nullable enable @@ -32,6 +33,11 @@ namespace osu.Game.Screens.Play /// public readonly IReadOnlyList Mods; + /// + /// The gameplay score. + /// + public Score? Score { get; set; } = null; + /// /// A bindable tracking the last judgement result applied to any hit object. /// diff --git a/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs index d93d626c72..3c31848c32 100644 --- a/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs @@ -36,9 +36,9 @@ namespace osu.Game.Screens.Play.HUD [Resolved(CanBeNull = true)] private ScoreProcessor scoreProcessor { get; set; } + [Resolved] [CanBeNull] - [Resolved(CanBeNull = true)] - private Player player { get; set; } + private GameplayState gameplayState { get; set; } private TimedDifficultyAttributes[] timedAttributes; private Ruleset gameplayRuleset; @@ -53,10 +53,10 @@ namespace osu.Game.Screens.Play.HUD { Colour = colours.BlueLighter; - if (player != null) + if (gameplayState != null) { - gameplayRuleset = player.GameplayRuleset; - timedAttributes = gameplayRuleset.CreateDifficultyCalculator(new GameplayWorkingBeatmap(player.GameplayBeatmap)).CalculateTimed(player.Mods.Value.ToArray()).ToArray(); + gameplayRuleset = gameplayState.Ruleset; + timedAttributes = gameplayRuleset.CreateDifficultyCalculator(new GameplayWorkingBeatmap(gameplayState.Beatmap)).CalculateTimed(gameplayState.Mods.ToArray()).ToArray(); } } @@ -70,7 +70,7 @@ namespace osu.Game.Screens.Play.HUD private void onNewJudgement(JudgementResult judgement) { - if (player == null || timedAttributes.Length == 0) + if (gameplayState?.Score == null || timedAttributes.Length == 0) return; var attribIndex = Array.BinarySearch(timedAttributes, 0, timedAttributes.Length, new TimedDifficultyAttributes(judgement.HitObject.GetEndTime(), null)); @@ -78,7 +78,7 @@ namespace osu.Game.Screens.Play.HUD attribIndex = ~attribIndex - 1; attribIndex = Math.Clamp(attribIndex, 0, timedAttributes.Length - 1); - var ppProcessor = gameplayRuleset.CreatePerformanceCalculator(timedAttributes[attribIndex].Attributes, player.Score.ScoreInfo); + var ppProcessor = gameplayRuleset.CreatePerformanceCalculator(timedAttributes[attribIndex].Attributes, gameplayState.Score.ScoreInfo); Current.Value = (int)(ppProcessor?.Calculate() ?? 0); } @@ -134,18 +134,18 @@ namespace osu.Game.Screens.Play.HUD private class GameplayWorkingBeatmap : WorkingBeatmap { - private readonly GameplayBeatmap gameplayBeatmap; + private readonly IBeatmap gameplayBeatmap; - public GameplayWorkingBeatmap(GameplayBeatmap gameplayBeatmap) + public GameplayWorkingBeatmap(IBeatmap gameplayBeatmap) : base(gameplayBeatmap.BeatmapInfo, null) { this.gameplayBeatmap = gameplayBeatmap; } public override IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null) - => gameplayBeatmap.PlayableBeatmap; + => gameplayBeatmap; - protected override IBeatmap GetBeatmap() => gameplayBeatmap.PlayableBeatmap; + protected override IBeatmap GetBeatmap() => gameplayBeatmap; protected override Texture GetBackground() => throw new NotImplementedException(); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 15cf8388f0..00907584e1 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -127,7 +127,7 @@ namespace osu.Game.Screens.Play [Cached] [Cached(Type = typeof(IBindable>))] - public new readonly Bindable> Mods = new Bindable>(Array.Empty()); + protected new readonly Bindable> Mods = new Bindable>(Array.Empty()); /// /// Whether failing should be allowed. From 221cc1747c1ec62e05cf6288cda88f876f31f58d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 4 Oct 2021 20:34:08 +0900 Subject: [PATCH 2250/2442] Drop "default" prefix --- ...erformancePointsCounter.cs => PerformancePointsCounter.cs} | 4 ++-- osu.Game/Skinning/DefaultSkin.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename osu.Game/Screens/Play/HUD/{DefaultPerformancePointsCounter.cs => PerformancePointsCounter.cs} (97%) diff --git a/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs similarity index 97% rename from osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs rename to osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index 3c31848c32..ad0a928d1c 100644 --- a/osu.Game/Screens/Play/HUD/DefaultPerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -28,7 +28,7 @@ using osuTK; namespace osu.Game.Screens.Play.HUD { - public class DefaultPerformancePointsCounter : RollingCounter, ISkinnableDrawable + public class PerformancePointsCounter : RollingCounter, ISkinnableDrawable { public bool UsesFixedAnchor { get; set; } @@ -43,7 +43,7 @@ namespace osu.Game.Screens.Play.HUD private TimedDifficultyAttributes[] timedAttributes; private Ruleset gameplayRuleset; - public DefaultPerformancePointsCounter() + public PerformancePointsCounter() { Current.Value = DisplayedCount = 0; } diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 41b7875cd1..8c1e5313d5 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -68,7 +68,7 @@ namespace osu.Game.Skinning var score = container.OfType().FirstOrDefault(); var accuracy = container.OfType().FirstOrDefault(); var combo = container.OfType().FirstOrDefault(); - var ppCounter = container.OfType().FirstOrDefault(); + var ppCounter = container.OfType().FirstOrDefault(); if (score != null) { @@ -131,7 +131,7 @@ namespace osu.Game.Skinning new SongProgress(), new BarHitErrorMeter(), new BarHitErrorMeter(), - new DefaultPerformancePointsCounter() + new PerformancePointsCounter() } }; From fb63e5ed87fab819899e8a137d0bf7b67ce720c8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 4 Oct 2021 20:35:26 +0900 Subject: [PATCH 2251/2442] Add todo --- osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index ad0a928d1c..740f286246 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -132,6 +132,7 @@ namespace osu.Game.Screens.Play.HUD } } + // Todo: This class shouldn't exist, but requires breaking changes to allow DifficultyCalculator to receive an IBeatmap. private class GameplayWorkingBeatmap : WorkingBeatmap { private readonly IBeatmap gameplayBeatmap; From 1837e1bf3ccb4e471979e1034c3ee92362d49739 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 4 Oct 2021 20:35:53 +0900 Subject: [PATCH 2252/2442] Share rounding with PerformanceStatistic --- osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index 740f286246..265c1a9d01 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -79,7 +79,7 @@ namespace osu.Game.Screens.Play.HUD attribIndex = Math.Clamp(attribIndex, 0, timedAttributes.Length - 1); var ppProcessor = gameplayRuleset.CreatePerformanceCalculator(timedAttributes[attribIndex].Attributes, gameplayState.Score.ScoreInfo); - Current.Value = (int)(ppProcessor?.Calculate() ?? 0); + Current.Value = (int)Math.Round(ppProcessor?.Calculate() ?? 0, MidpointRounding.AwayFromZero); } protected override LocalisableString FormatCount(int count) => count.ToString(@"D"); From 0b0316e27ee68696d090cb4410f13e9aa0117045 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 4 Oct 2021 20:59:31 +0900 Subject: [PATCH 2253/2442] Fix missing CanBeNull --- osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index 265c1a9d01..154b6accb8 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -36,7 +36,7 @@ namespace osu.Game.Screens.Play.HUD [Resolved(CanBeNull = true)] private ScoreProcessor scoreProcessor { get; set; } - [Resolved] + [Resolved(CanBeNull = true)] [CanBeNull] private GameplayState gameplayState { get; set; } From d1e7191f94fa1de0d3e7911951a5cb9179125a17 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 4 Oct 2021 20:59:51 +0900 Subject: [PATCH 2254/2442] Pass score into GameplayState --- osu.Game/Screens/Play/Player.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 00907584e1..69093db883 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -162,6 +162,7 @@ namespace osu.Game.Screens.Play return; Score = CreateScore(); + GameplayState.Score = Score; // ensure the score is in a consistent state with the current player. Score.ScoreInfo.BeatmapInfo = Beatmap.Value.BeatmapInfo; From d120678e306e9dc85cfb0e8d2fd82efd96c3a7cb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 4 Oct 2021 21:13:14 +0900 Subject: [PATCH 2255/2442] Fix redundant default value --- osu.Game/Screens/Play/GameplayState.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/GameplayState.cs b/osu.Game/Screens/Play/GameplayState.cs index ef4967b34d..9c83eddb45 100644 --- a/osu.Game/Screens/Play/GameplayState.cs +++ b/osu.Game/Screens/Play/GameplayState.cs @@ -36,7 +36,7 @@ namespace osu.Game.Screens.Play /// /// The gameplay score. /// - public Score? Score { get; set; } = null; + public Score? Score { get; set; } /// /// A bindable tracking the last judgement result applied to any hit object. From 15ec315ec62a9b32e9d6a81fcd9a2ddffbcabf9c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Oct 2021 01:14:58 +0900 Subject: [PATCH 2256/2442] Fix test runs hanging due to missing `ConfigureAwait` --- osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index d1c23f1442..4cc71717ff 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -909,7 +909,7 @@ namespace osu.Game.Tests.Beatmaps.IO var manager = osu.Dependencies.Get(); - var importedSet = await manager.Import(new ImportTask(temp)); + var importedSet = await manager.Import(new ImportTask(temp)).ConfigureAwait(false); ensureLoaded(osu); @@ -924,7 +924,7 @@ namespace osu.Game.Tests.Beatmaps.IO var manager = osu.Dependencies.Get(); - var importedSet = await manager.Import(new ImportTask(temp)); + var importedSet = await manager.Import(new ImportTask(temp)).ConfigureAwait(false); ensureLoaded(osu); From 86df4919f7ab8207e62d9b49cdcd06bbb0c6e6c5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 5 Oct 2021 11:06:24 +0900 Subject: [PATCH 2257/2442] Fix skin fallbacks test --- osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs index 7d4673c901..7398527f57 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs @@ -42,6 +42,7 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestEmptyLegacyBeatmapSkinFallsBack() { CreateSkinTest(SkinInfo.Default, () => new LegacyBeatmapSkin(new BeatmapInfo(), null, null)); + AddUntilStep("wait for hud load", () => Player.ChildrenOfType().All(c => c.ComponentsLoaded)); AddAssert("hud from default skin", () => AssertComponentsFromExpectedSource(SkinnableTarget.MainHUDComponents, skinManager.CurrentSkin.Value)); } From 593da79bbc96bd18fe6bf0eb35c5758fb1c16153 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 5 Oct 2021 11:26:13 +0900 Subject: [PATCH 2258/2442] Further asyncify load process --- osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 9 +++++++++ .../Screens/Play/HUD/PerformancePointsCounter.cs | 15 ++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index c46ab93ece..e6c287112f 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -17,6 +17,7 @@ using osu.Framework.Utils; using osu.Game.Configuration; using osu.Game.Database; using osu.Game.Rulesets; +using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; @@ -147,6 +148,14 @@ namespace osu.Game.Beatmaps }, token, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler); } + public Task GetTimedDifficultyAttributesAsync(WorkingBeatmap beatmap, Ruleset ruleset, Mod[] mods, CancellationToken token = default) + { + return Task.Factory.StartNew(() => ruleset.CreateDifficultyCalculator(beatmap).CalculateTimed(mods).ToArray(), + token, + TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, + updateScheduler); + } + /// /// Retrieves the that describes a star rating. /// diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index 154b6accb8..b02ab440a0 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -3,8 +3,11 @@ using System; using System.Collections.Generic; +using System.Diagnostics; 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; @@ -40,7 +43,10 @@ namespace osu.Game.Screens.Play.HUD [CanBeNull] private GameplayState gameplayState { get; set; } + [CanBeNull] private TimedDifficultyAttributes[] timedAttributes; + + [CanBeNull] private Ruleset gameplayRuleset; public PerformancePointsCounter() @@ -49,14 +55,15 @@ namespace osu.Game.Screens.Play.HUD } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, BeatmapDifficultyCache difficultyCache, CancellationToken cancellationToken) { Colour = colours.BlueLighter; if (gameplayState != null) { gameplayRuleset = gameplayState.Ruleset; - timedAttributes = gameplayRuleset.CreateDifficultyCalculator(new GameplayWorkingBeatmap(gameplayState.Beatmap)).CalculateTimed(gameplayState.Mods.ToArray()).ToArray(); + difficultyCache.GetTimedDifficultyAttributesAsync(new GameplayWorkingBeatmap(gameplayState.Beatmap), gameplayRuleset, gameplayState.Mods.ToArray(), cancellationToken) + .ContinueWith(r => Schedule(() => timedAttributes = r.Result), TaskContinuationOptions.OnlyOnRanToCompletion); } } @@ -70,9 +77,11 @@ namespace osu.Game.Screens.Play.HUD private void onNewJudgement(JudgementResult judgement) { - if (gameplayState?.Score == null || timedAttributes.Length == 0) + if (gameplayState?.Score == null || timedAttributes == null || timedAttributes.Length == 0) return; + Debug.Assert(gameplayRuleset != null); + var attribIndex = Array.BinarySearch(timedAttributes, 0, timedAttributes.Length, new TimedDifficultyAttributes(judgement.HitObject.GetEndTime(), null)); if (attribIndex < 0) attribIndex = ~attribIndex - 1; From 5624dd9af67376b73038e4f75570927f161ff602 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 5 Oct 2021 12:07:41 +0900 Subject: [PATCH 2259/2442] Fix incorrect CancellationToken usage Apparently I wrote the BDL system and don't know how this works. I believe you need `CancellationToken?` or CanBeNull=true, however that doesn't actually play well when actually using the token in code... --- osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index b02ab440a0..13b94e6cd6 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -49,20 +49,22 @@ namespace osu.Game.Screens.Play.HUD [CanBeNull] private Ruleset gameplayRuleset; + private readonly CancellationTokenSource loadCancellationSource = new CancellationTokenSource(); + public PerformancePointsCounter() { Current.Value = DisplayedCount = 0; } [BackgroundDependencyLoader] - private void load(OsuColour colours, BeatmapDifficultyCache difficultyCache, CancellationToken cancellationToken) + private void load(OsuColour colours, BeatmapDifficultyCache difficultyCache) { Colour = colours.BlueLighter; if (gameplayState != null) { gameplayRuleset = gameplayState.Ruleset; - difficultyCache.GetTimedDifficultyAttributesAsync(new GameplayWorkingBeatmap(gameplayState.Beatmap), gameplayRuleset, gameplayState.Mods.ToArray(), cancellationToken) + difficultyCache.GetTimedDifficultyAttributesAsync(new GameplayWorkingBeatmap(gameplayState.Beatmap), gameplayRuleset, gameplayState.Mods.ToArray(), loadCancellationSource.Token) .ContinueWith(r => Schedule(() => timedAttributes = r.Result), TaskContinuationOptions.OnlyOnRanToCompletion); } } @@ -101,6 +103,8 @@ namespace osu.Game.Screens.Play.HUD if (scoreProcessor != null) scoreProcessor.NewJudgement -= onNewJudgement; + + loadCancellationSource?.Cancel(); } private class TextComponent : CompositeDrawable, IHasText From b41fa41c8539d6370fb4d3efd8539ec664176ff2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Oct 2021 14:28:56 +0900 Subject: [PATCH 2260/2442] Rename `APIRequest.Result` to `Response` --- .../Online/TestDummyAPIRequestHandling.cs | 2 +- .../TestSceneUpdateableBeatmapBackgroundSprite.cs | 4 ++-- osu.Game.Tournament/TournamentGameBase.cs | 4 ++-- osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs | 2 +- osu.Game/Database/UserLookupCache.cs | 2 +- osu.Game/Online/API/APIRequest.cs | 15 ++++++++++----- osu.Game/Overlays/RankingsOverlay.cs | 6 +++--- .../Screens/OnlinePlay/Components/RoomManager.cs | 2 +- 8 files changed, 21 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs b/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs index aa29d76843..91c6b6c008 100644 --- a/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs +++ b/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Online AddAssert("response event fired", () => response != null); - AddAssert("request has response", () => request.Result == response); + AddAssert("request has response", () => request.Response == response); } [Test] diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs index 198cc70e01..74cd675a05 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs @@ -72,7 +72,7 @@ namespace osu.Game.Tests.Visual.UserInterface var req = new GetBeatmapSetRequest(1); api.Queue(req); - AddUntilStep("wait for api response", () => req.Result != null); + AddUntilStep("wait for api response", () => req.Response != null); TestUpdateableBeatmapBackgroundSprite background = null; @@ -81,7 +81,7 @@ namespace osu.Game.Tests.Visual.UserInterface Child = background = new TestUpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both, - Beatmap = { Value = new BeatmapInfo { BeatmapSet = req.Result?.ToBeatmapSet(rulesets) } } + Beatmap = { Value = new BeatmapInfo { BeatmapSet = req.Response?.ToBeatmapSet(rulesets) } } }; }); diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 2e4ed9d5b1..bdf7269c83 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -182,7 +182,7 @@ namespace osu.Game.Tournament { var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID }); API.Perform(req); - b.BeatmapInfo = req.Result?.ToBeatmapInfo(RulesetStore); + b.BeatmapInfo = req.Response?.ToBeatmapInfo(RulesetStore); addedInfo = true; } @@ -203,7 +203,7 @@ namespace osu.Game.Tournament { var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID }); req.Perform(API); - b.BeatmapInfo = req.Result?.ToBeatmapInfo(RulesetStore); + b.BeatmapInfo = req.Response?.ToBeatmapInfo(RulesetStore); addedInfo = true; } diff --git a/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs b/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs index e1faf6005b..1fe120557d 100644 --- a/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs +++ b/osu.Game/Beatmaps/BeatmapOnlineLookupQueue.cs @@ -78,7 +78,7 @@ namespace osu.Game.Beatmaps // intentionally blocking to limit web request concurrency api.Perform(req); - var res = req.Result; + var res = req.Response; if (res != null) { diff --git a/osu.Game/Database/UserLookupCache.cs b/osu.Game/Database/UserLookupCache.cs index 13c37ddfe9..ff81637efb 100644 --- a/osu.Game/Database/UserLookupCache.cs +++ b/osu.Game/Database/UserLookupCache.cs @@ -115,7 +115,7 @@ namespace osu.Game.Database createNewTask(); } - List foundUsers = request.Result?.Users; + List foundUsers = request.Response?.Users; if (foundUsers != null) { diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index cf17ed4b5d..d60c9cfe65 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using JetBrains.Annotations; using Newtonsoft.Json; using osu.Framework.IO.Network; using osu.Framework.Logging; @@ -17,7 +18,11 @@ namespace osu.Game.Online.API { protected override WebRequest CreateWebRequest() => new OsuJsonWebRequest(Uri); - public T Result { get; private set; } + /// + /// The deserialised response object. May be null if the request or deserialisation failed. + /// + [CanBeNull] + public T Response { get; private set; } /// /// Invoked on successful completion of an API request. @@ -27,21 +32,21 @@ namespace osu.Game.Online.API protected APIRequest() { - base.Success += () => Success?.Invoke(Result); + base.Success += () => Success?.Invoke(Response); } protected override void PostProcess() { base.PostProcess(); - Result = ((OsuJsonWebRequest)WebRequest)?.ResponseObject; + Response = ((OsuJsonWebRequest)WebRequest)?.ResponseObject; } internal void TriggerSuccess(T result) { - if (Result != null) + if (Response != null) throw new InvalidOperationException("Attempted to trigger success more than once"); - Result = result; + Response = result; TriggerSuccess(); } diff --git a/osu.Game/Overlays/RankingsOverlay.cs b/osu.Game/Overlays/RankingsOverlay.cs index b8bdef925e..2263d54d7b 100644 --- a/osu.Game/Overlays/RankingsOverlay.cs +++ b/osu.Game/Overlays/RankingsOverlay.cs @@ -146,16 +146,16 @@ namespace osu.Game.Overlays switch (userRequest.Type) { case UserRankingsType.Performance: - return new PerformanceTable(1, userRequest.Result.Users); + return new PerformanceTable(1, userRequest.Response.Users); case UserRankingsType.Score: - return new ScoresTable(1, userRequest.Result.Users); + return new ScoresTable(1, userRequest.Response.Users); } return null; case GetCountryRankingsRequest countryRequest: - return new CountriesTable(1, countryRequest.Result.Countries); + return new CountriesTable(1, countryRequest.Response.Countries); } return null; diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs index a64d89b699..381849189d 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs @@ -66,7 +66,7 @@ namespace osu.Game.Screens.OnlinePlay.Components req.Failure += exception => { - onError?.Invoke(req.Result?.Error ?? exception.Message); + onError?.Invoke(req.Response?.Error ?? exception.Message); }; api.Queue(req); From d3b9660148d3ebc79660915c1db35b779b001402 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Oct 2021 14:41:14 +0900 Subject: [PATCH 2261/2442] Move common interface implementations to extension methods --- .../BeatmapMetadataRomanisationTest.cs | 4 +- osu.Game/Beatmaps/BeatmapInfo.cs | 7 +-- osu.Game/Beatmaps/BeatmapInfoExtensions.cs | 37 +++++++++++++++ osu.Game/Beatmaps/BeatmapMetadata.cs | 7 +-- .../Beatmaps/BeatmapMetadataInfoExtensions.cs | 46 +++++++++++++++++++ osu.Game/Beatmaps/IBeatmapInfo.cs | 30 ------------ osu.Game/Beatmaps/IBeatmapMetadataInfo.cs | 43 ----------------- osu.Game/Overlays/Music/PlaylistItem.cs | 2 +- .../OnlinePlay/DrawableRoomPlaylistItem.cs | 2 +- .../Lounge/Components/DrawableRoom.cs | 2 +- .../Select/Carousel/CarouselBeatmap.cs | 2 +- 11 files changed, 91 insertions(+), 91 deletions(-) create mode 100644 osu.Game/Beatmaps/BeatmapInfoExtensions.cs create mode 100644 osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs diff --git a/osu.Game.Tests/Localisation/BeatmapMetadataRomanisationTest.cs b/osu.Game.Tests/Localisation/BeatmapMetadataRomanisationTest.cs index dab4825919..9926acf772 100644 --- a/osu.Game.Tests/Localisation/BeatmapMetadataRomanisationTest.cs +++ b/osu.Game.Tests/Localisation/BeatmapMetadataRomanisationTest.cs @@ -19,7 +19,7 @@ namespace osu.Game.Tests.Localisation Title = "Romanised title", TitleUnicode = "Unicode Title" }; - var romanisableString = metadata.ToRomanisableString(); + var romanisableString = metadata.GetDisplayTitleRomanisable(); Assert.AreEqual(metadata.ToString(), romanisableString.Romanised); Assert.AreEqual($"{metadata.ArtistUnicode} - {metadata.TitleUnicode}", romanisableString.Original); @@ -33,7 +33,7 @@ namespace osu.Game.Tests.Localisation Artist = "Romanised Artist", Title = "Romanised title" }; - var romanisableString = metadata.ToRomanisableString(); + var romanisableString = metadata.GetDisplayTitleRomanisable(); Assert.AreEqual(romanisableString.Romanised, romanisableString.Original); } diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index cd5f8fb9a1..ac5b5d7a8a 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -7,7 +7,6 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using Newtonsoft.Json; -using osu.Framework.Localisation; using osu.Framework.Testing; using osu.Game.Database; using osu.Game.Rulesets; @@ -152,11 +151,7 @@ namespace osu.Game.Beatmaps [JsonIgnore] public DifficultyRating DifficultyRating => BeatmapDifficultyCache.GetDifficultyRating(StarDifficulty); - public IEnumerable SearchableTerms => ((IBeatmapInfo)this).SearchableTerms; - - public override string ToString() => ((IBeatmapInfo)this).DisplayTitle; - - public RomanisableString ToRomanisableString() => ((IBeatmapInfo)this).DisplayTitleRomanisable; + public override string ToString() => this.GetDisplayTitle(); public bool Equals(BeatmapInfo other) { diff --git a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs new file mode 100644 index 0000000000..deab8b915a --- /dev/null +++ b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs @@ -0,0 +1,37 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Localisation; + +namespace osu.Game.Beatmaps +{ + public static class BeatmapInfoExtensions + { + /// + /// A user-presentable display title representing this beatmap. + /// + public static string GetDisplayTitle(this IBeatmapInfo beatmapInfo) => $"{getClosestMetadata(beatmapInfo)} {getVersionString(beatmapInfo)}".Trim(); + + /// + /// A user-presentable display title representing this beatmap, with localisation handling for potentially romanisable fields. + /// + public static RomanisableString GetDisplayTitleRomanisable(this IBeatmapInfo beatmapInfo) + { + var metadata = getClosestMetadata(beatmapInfo).GetDisplayTitleRomanisable(); + var versionString = getVersionString(beatmapInfo); + + return new RomanisableString($"{metadata.GetPreferred(true)} {versionString}".Trim(), $"{metadata.GetPreferred(false)} {versionString}".Trim()); + } + + public static string[] GetSearchableTerms(this IBeatmapInfo beatmapInfo) => new[] + { + beatmapInfo.DifficultyName + }.Concat(getClosestMetadata(beatmapInfo).GetSearchableTerms()).Where(s => !string.IsNullOrEmpty(s)).ToArray(); + + private static string getVersionString(IBeatmapInfo beatmapInfo) => string.IsNullOrEmpty(beatmapInfo.DifficultyName) ? string.Empty : $"[{beatmapInfo.DifficultyName}]"; + + // temporary helper methods until we figure which metadata should be where. + private static IBeatmapMetadataInfo getClosestMetadata(IBeatmapInfo beatmapInfo) => (beatmapInfo.Metadata ?? beatmapInfo.BeatmapSet.Metadata)!; + } +} diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index 3da80580cb..711533e118 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using Newtonsoft.Json; -using osu.Framework.Localisation; using osu.Framework.Testing; using osu.Game.Database; using osu.Game.Users; @@ -87,11 +86,7 @@ namespace osu.Game.Beatmaps public bool Equals(BeatmapMetadata other) => ((IBeatmapMetadataInfo)this).Equals(other); - public override string ToString() => ((IBeatmapMetadataInfo)this).DisplayTitle; - - public RomanisableString ToRomanisableString() => ((IBeatmapMetadataInfo)this).DisplayTitleRomanisable; - - public IEnumerable SearchableTerms => ((IBeatmapMetadataInfo)this).SearchableTerms; + public override string ToString() => this.GetDisplayTitle(); string IBeatmapMetadataInfo.Author => AuthorString; } diff --git a/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs b/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs new file mode 100644 index 0000000000..ee946eeeec --- /dev/null +++ b/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs @@ -0,0 +1,46 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Localisation; + +namespace osu.Game.Beatmaps +{ + public static class BeatmapMetadataInfoExtensions + { + /// + /// An array of all searchable terms provided in contained metadata. + /// + public static string[] GetSearchableTerms(this IBeatmapMetadataInfo metadataInfo) => new[] + { + metadataInfo.Author, + metadataInfo.Artist, + metadataInfo.ArtistUnicode, + metadataInfo.Title, + metadataInfo.TitleUnicode, + metadataInfo.Source, + metadataInfo.Tags + }.Where(s => !string.IsNullOrEmpty(s)).ToArray(); + + /// + /// A user-presentable display title representing this metadata. + /// + public static string GetDisplayTitle(this IBeatmapMetadataInfo metadataInfo) + { + string author = string.IsNullOrEmpty(metadataInfo.Author) ? string.Empty : $"({metadataInfo.Author})"; + return $"{metadataInfo.Artist} - {metadataInfo.Title} {author}".Trim(); + } + + /// + /// A user-presentable display title representing this beatmap, with localisation handling for potentially romanisable fields. + /// + public static RomanisableString GetDisplayTitleRomanisable(this IBeatmapMetadataInfo metadataInfo) + { + string author = string.IsNullOrEmpty(metadataInfo.Author) ? string.Empty : $"({metadataInfo.Author})"; + var artistUnicode = string.IsNullOrEmpty(metadataInfo.ArtistUnicode) ? metadataInfo.Artist : metadataInfo.ArtistUnicode; + var titleUnicode = string.IsNullOrEmpty(metadataInfo.TitleUnicode) ? metadataInfo.Title : metadataInfo.TitleUnicode; + + return new RomanisableString($"{artistUnicode} - {titleUnicode} {author}".Trim(), $"{metadataInfo.Artist} - {metadataInfo.Title} {author}".Trim()); + } + } +} diff --git a/osu.Game/Beatmaps/IBeatmapInfo.cs b/osu.Game/Beatmaps/IBeatmapInfo.cs index fb30b0279c..6a3f1b43d8 100644 --- a/osu.Game/Beatmaps/IBeatmapInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapInfo.cs @@ -1,8 +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.Linq; -using osu.Framework.Localisation; using osu.Game.Database; using osu.Game.Rulesets; @@ -64,33 +62,5 @@ namespace osu.Game.Beatmaps /// The basic star rating for this beatmap (with no mods applied). /// double StarRating { get; } - - /// - /// A user-presentable display title representing this metadata. - /// - string DisplayTitle => $"{Metadata} {versionString}".Trim(); - - /// - /// A user-presentable display title representing this beatmap, with localisation handling for potentially romanisable fields. - /// - RomanisableString DisplayTitleRomanisable - { - get - { - var metadata = closestMetadata.DisplayTitleRomanisable; - - return new RomanisableString($"{metadata.GetPreferred(true)} {versionString}".Trim(), $"{metadata.GetPreferred(false)} {versionString}".Trim()); - } - } - - string[] SearchableTerms => new[] - { - DifficultyName - }.Concat(closestMetadata.SearchableTerms).Where(s => !string.IsNullOrEmpty(s)).ToArray(); - - private string versionString => string.IsNullOrEmpty(DifficultyName) ? string.Empty : $"[{DifficultyName}]"; - - // temporary helper methods until we figure which metadata should be where. - private IBeatmapMetadataInfo closestMetadata => (Metadata ?? BeatmapSet.Metadata)!; } } diff --git a/osu.Game/Beatmaps/IBeatmapMetadataInfo.cs b/osu.Game/Beatmaps/IBeatmapMetadataInfo.cs index d0dae296a0..55aee7d7bc 100644 --- a/osu.Game/Beatmaps/IBeatmapMetadataInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapMetadataInfo.cs @@ -2,8 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; -using osu.Framework.Localisation; #nullable enable @@ -65,47 +63,6 @@ namespace osu.Game.Beatmaps /// string BackgroundFile { get; } - /// - /// A user-presentable display title representing this metadata. - /// - string DisplayTitle - { - get - { - string author = string.IsNullOrEmpty(Author) ? string.Empty : $"({Author})"; - return $"{Artist} - {Title} {author}".Trim(); - } - } - - /// - /// A user-presentable display title representing this metadata, with localisation handling for potentially romanisable fields. - /// - RomanisableString DisplayTitleRomanisable - { - get - { - string author = string.IsNullOrEmpty(Author) ? string.Empty : $"({Author})"; - var artistUnicode = string.IsNullOrEmpty(ArtistUnicode) ? Artist : ArtistUnicode; - var titleUnicode = string.IsNullOrEmpty(TitleUnicode) ? Title : TitleUnicode; - - return new RomanisableString($"{artistUnicode} - {titleUnicode} {author}".Trim(), $"{Artist} - {Title} {author}".Trim()); - } - } - - /// - /// An array of all searchable terms provided in contained metadata. - /// - string[] SearchableTerms => new[] - { - Author, - Artist, - ArtistUnicode, - Title, - TitleUnicode, - Source, - Tags - }.Where(s => !string.IsNullOrEmpty(s)).ToArray(); - bool IEquatable.Equals(IBeatmapMetadataInfo? other) { if (other == null) diff --git a/osu.Game/Overlays/Music/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs index 571b14428e..ef25de77c6 100644 --- a/osu.Game/Overlays/Music/PlaylistItem.cs +++ b/osu.Game/Overlays/Music/PlaylistItem.cs @@ -38,7 +38,7 @@ namespace osu.Game.Overlays.Music { Padding = new MarginPadding { Left = 5 }; - FilterTerms = item.Metadata.SearchableTerms; + FilterTerms = item.Metadata.GetSearchableTerms(); } [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 69eb857661..585b024623 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -108,7 +108,7 @@ namespace osu.Game.Screens.OnlinePlay difficultyIconContainer.Child = new DifficultyIcon(beatmap.Value, ruleset.Value, requiredMods) { Size = new Vector2(32) }; beatmapText.Clear(); - beatmapText.AddLink(Item.Beatmap.Value.ToRomanisableString(), LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineBeatmapID.ToString(), null, text => + beatmapText.AddLink(Item.Beatmap.Value.GetDisplayTitleRomanisable(), LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineBeatmapID.ToString(), null, text => { text.Truncate = true; text.RelativeSizeAxes = Axes.X; diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 03d13c353a..acd87ed864 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -379,7 +379,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components if (item.NewValue?.Beatmap.Value != null) { statusText.Text = "Currently playing "; - beatmapText.AddLink(item.NewValue.Beatmap.Value.ToRomanisableString(), + beatmapText.AddLink(item.NewValue.Beatmap.Value.GetDisplayTitleRomanisable(), LinkAction.OpenBeatmap, item.NewValue.Beatmap.Value.OnlineBeatmapID.ToString(), creationParameters: s => diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index 3f729d9477..d8c5aa760e 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -57,7 +57,7 @@ namespace osu.Game.Screens.Select.Carousel if (match) { - var terms = BeatmapInfo.SearchableTerms; + var terms = BeatmapInfo.GetSearchableTerms(); foreach (var criteriaTerm in criteria.SearchTerms) match &= terms.Any(term => term.Contains(criteriaTerm, StringComparison.InvariantCultureIgnoreCase)); From a5aa32811aaf215f7d96d601a82d44362d140ad3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Oct 2021 14:49:59 +0900 Subject: [PATCH 2262/2442] Remove null check suppression and add non-null fallback --- osu.Game/Beatmaps/BeatmapInfoExtensions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs index deab8b915a..eba19ac1a1 100644 --- a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs +++ b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs @@ -32,6 +32,7 @@ namespace osu.Game.Beatmaps private static string getVersionString(IBeatmapInfo beatmapInfo) => string.IsNullOrEmpty(beatmapInfo.DifficultyName) ? string.Empty : $"[{beatmapInfo.DifficultyName}]"; // temporary helper methods until we figure which metadata should be where. - private static IBeatmapMetadataInfo getClosestMetadata(IBeatmapInfo beatmapInfo) => (beatmapInfo.Metadata ?? beatmapInfo.BeatmapSet.Metadata)!; + private static IBeatmapMetadataInfo getClosestMetadata(IBeatmapInfo beatmapInfo) => + beatmapInfo.Metadata ?? beatmapInfo.BeatmapSet?.Metadata ?? new BeatmapMetadata(); } } From e19be8ebe4eb49f456b61cd8f26c1143dad606ac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Oct 2021 14:48:10 +0900 Subject: [PATCH 2263/2442] Make `GameplayState.Score` immutable --- osu.Game/Screens/Play/GameplayState.cs | 8 +++++--- osu.Game/Screens/Play/Player.cs | 20 ++++++++++---------- osu.Game/Screens/Play/ReplayPlayer.cs | 2 +- osu.Game/Screens/Play/SpectatorPlayer.cs | 3 ++- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayState.cs b/osu.Game/Screens/Play/GameplayState.cs index 9c83eddb45..44f72022f7 100644 --- a/osu.Game/Screens/Play/GameplayState.cs +++ b/osu.Game/Screens/Play/GameplayState.cs @@ -1,6 +1,7 @@ // 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 osu.Framework.Bindables; using osu.Game.Beatmaps; @@ -36,7 +37,7 @@ namespace osu.Game.Screens.Play /// /// The gameplay score. /// - public Score? Score { get; set; } + public readonly Score Score; /// /// A bindable tracking the last judgement result applied to any hit object. @@ -45,11 +46,12 @@ namespace osu.Game.Screens.Play private readonly Bindable lastJudgementResult = new Bindable(); - public GameplayState(IBeatmap beatmap, Ruleset ruleset, IReadOnlyList mods) + public GameplayState(IBeatmap beatmap, Ruleset ruleset, IReadOnlyList? mods = null, Score? score = null) { Beatmap = beatmap; Ruleset = ruleset; - Mods = mods; + Score = score ?? new Score(); + Mods = mods ?? ArraySegment.Empty; } /// diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 69093db883..a688330f54 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -161,14 +161,6 @@ namespace osu.Game.Screens.Play if (!LoadedBeatmapSuccessfully) return; - Score = CreateScore(); - GameplayState.Score = Score; - - // ensure the score is in a consistent state with the current player. - Score.ScoreInfo.BeatmapInfo = Beatmap.Value.BeatmapInfo; - Score.ScoreInfo.Ruleset = ruleset.RulesetInfo; - Score.ScoreInfo.Mods = Mods.Value.ToArray(); - PrepareReplay(); ScoreProcessor.NewJudgement += result => ScoreProcessor.PopulateScore(Score.ScoreInfo); @@ -226,7 +218,14 @@ namespace osu.Game.Screens.Play InternalChild = GameplayClockContainer = CreateGameplayClockContainer(Beatmap.Value, DrawableRuleset.GameplayStartTime); - dependencies.CacheAs(GameplayState = new GameplayState(playableBeatmap, ruleset, Mods.Value)); + Score = CreateScore(playableBeatmap); + + // ensure the score is in a consistent state with the current player. + Score.ScoreInfo.BeatmapInfo = Beatmap.Value.BeatmapInfo; + Score.ScoreInfo.Ruleset = ruleset.RulesetInfo; + Score.ScoreInfo.Mods = Mods.Value.ToArray(); + + dependencies.CacheAs(GameplayState = new GameplayState(playableBeatmap, ruleset, Mods.Value, Score)); AddInternal(screenSuspension = new ScreenSuspensionHandler(GameplayClockContainer)); @@ -989,8 +988,9 @@ namespace osu.Game.Screens.Play /// /// Creates the player's . /// + /// /// The . - protected virtual Score CreateScore() => new Score + protected virtual Score CreateScore(IBeatmap beatmap) => new Score { ScoreInfo = new ScoreInfo { User = api.LocalUser.Value }, }; diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index eefea737cf..93054b7bb5 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -41,7 +41,7 @@ namespace osu.Game.Screens.Play DrawableRuleset?.SetReplayScore(Score); } - protected override Score CreateScore() => createScore(GameplayState.Beatmap, Mods.Value); + protected override Score CreateScore(IBeatmap beatmap) => createScore(beatmap, Mods.Value); // Don't re-import replay scores as they're already present in the database. protected override Task ImportScore(Score score) => Task.CompletedTask; diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index fbb4fb5699..f6a89e7fa9 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Screens; +using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.Spectator; @@ -79,7 +80,7 @@ namespace osu.Game.Screens.Play NonFrameStableSeek(score.Replay.Frames[0].Time); } - protected override Score CreateScore() => score; + protected override Score CreateScore(IBeatmap beatmap) => score; protected override ResultsScreen CreateResults(ScoreInfo score) => new SpectatorResultsScreen(score); From 7176dc95e5d9a100cd7486122031b8b7dcd2caea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Oct 2021 14:51:53 +0900 Subject: [PATCH 2264/2442] Revert `Player.Score` to `protected` --- osu.Game/Screens/Play/Player.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a688330f54..ecc65c6bb0 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -137,7 +137,7 @@ namespace osu.Game.Screens.Play public readonly PlayerConfiguration Configuration; - internal Score Score { get; private set; } + protected Score Score { get; private set; } /// /// Create a new player instance. From b6af93d43445b96db2adae8ebee954891b4b35d5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Oct 2021 15:10:56 +0900 Subject: [PATCH 2265/2442] Apply some code quality refactoring --- .../Difficulty/DifficultyCalculator.cs | 19 +++++++++---------- .../Play/HUD/PerformancePointsCounter.cs | 19 +++++++------------ 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 3d90cc59f4..49a4b2c265 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -234,33 +234,32 @@ namespace osu.Game.Rulesets.Difficulty this.baseBeatmap = baseBeatmap; } + public readonly List HitObjects = new List(); + + IReadOnlyList IBeatmap.HitObjects => HitObjects; + + #region Delegated IBeatmap implementation + public BeatmapInfo BeatmapInfo { get => baseBeatmap.BeatmapInfo; set => baseBeatmap.BeatmapInfo = value; } - public BeatmapMetadata Metadata => baseBeatmap.Metadata; - public ControlPointInfo ControlPointInfo { get => baseBeatmap.ControlPointInfo; set => baseBeatmap.ControlPointInfo = value; } + public BeatmapMetadata Metadata => baseBeatmap.Metadata; public List Breaks => baseBeatmap.Breaks; - public double TotalBreakTime => baseBeatmap.TotalBreakTime; - - public readonly List HitObjects = new List(); - - IReadOnlyList IBeatmap.HitObjects => HitObjects; - public IEnumerable GetStatistics() => baseBeatmap.GetStatistics(); - public double GetMostCommonBeatLength() => baseBeatmap.GetMostCommonBeatLength(); - public IBeatmap Clone() => new ProgressiveCalculationBeatmap(baseBeatmap.Clone()); + + #endregion } } } diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index 13b94e6cd6..7babc90427 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Threading; @@ -46,9 +45,6 @@ namespace osu.Game.Screens.Play.HUD [CanBeNull] private TimedDifficultyAttributes[] timedAttributes; - [CanBeNull] - private Ruleset gameplayRuleset; - private readonly CancellationTokenSource loadCancellationSource = new CancellationTokenSource(); public PerformancePointsCounter() @@ -63,8 +59,8 @@ namespace osu.Game.Screens.Play.HUD if (gameplayState != null) { - gameplayRuleset = gameplayState.Ruleset; - difficultyCache.GetTimedDifficultyAttributesAsync(new GameplayWorkingBeatmap(gameplayState.Beatmap), gameplayRuleset, gameplayState.Mods.ToArray(), loadCancellationSource.Token) + var gameplayWorkingBeatmap = new GameplayWorkingBeatmap(gameplayState.Beatmap); + difficultyCache.GetTimedDifficultyAttributesAsync(gameplayWorkingBeatmap, gameplayState.Ruleset, gameplayState.Mods.ToArray(), loadCancellationSource.Token) .ContinueWith(r => Schedule(() => timedAttributes = r.Result), TaskContinuationOptions.OnlyOnRanToCompletion); } } @@ -82,15 +78,14 @@ namespace osu.Game.Screens.Play.HUD if (gameplayState?.Score == null || timedAttributes == null || timedAttributes.Length == 0) return; - Debug.Assert(gameplayRuleset != null); - - var attribIndex = Array.BinarySearch(timedAttributes, 0, timedAttributes.Length, new TimedDifficultyAttributes(judgement.HitObject.GetEndTime(), null)); + int attribIndex = Array.BinarySearch(timedAttributes, 0, timedAttributes.Length, new TimedDifficultyAttributes(judgement.HitObject.GetEndTime(), null)); if (attribIndex < 0) attribIndex = ~attribIndex - 1; attribIndex = Math.Clamp(attribIndex, 0, timedAttributes.Length - 1); - var ppProcessor = gameplayRuleset.CreatePerformanceCalculator(timedAttributes[attribIndex].Attributes, gameplayState.Score.ScoreInfo); - Current.Value = (int)Math.Round(ppProcessor?.Calculate() ?? 0, MidpointRounding.AwayFromZero); + var calculator = gameplayState.Ruleset.CreatePerformanceCalculator(timedAttributes[attribIndex].Attributes, gameplayState.Score.ScoreInfo); + + Current.Value = (int)Math.Round(calculator?.Calculate() ?? 0, MidpointRounding.AwayFromZero); } protected override LocalisableString FormatCount(int count) => count.ToString(@"D"); @@ -145,7 +140,7 @@ namespace osu.Game.Screens.Play.HUD } } - // Todo: This class shouldn't exist, but requires breaking changes to allow DifficultyCalculator to receive an IBeatmap. + // TODO: This class shouldn't exist, but requires breaking changes to allow DifficultyCalculator to receive an IBeatmap. private class GameplayWorkingBeatmap : WorkingBeatmap { private readonly IBeatmap gameplayBeatmap; From 81a13566bc36574c943d9c4aa37fc7b47dcdd711 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Oct 2021 15:23:34 +0900 Subject: [PATCH 2266/2442] Adjust default location slightly, fix alignment of "pp" subtext --- osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs | 3 ++- osu.Game/Skinning/DefaultSkin.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index 7babc90427..2fc3f23190 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -133,7 +133,8 @@ namespace osu.Game.Screens.Play.HUD Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, Text = @"pp", - Font = OsuFont.Numeric.With(size: 8) + Font = OsuFont.Numeric.With(size: 8), + Padding = new MarginPadding { Bottom = 1.5f }, // align baseline better } } }; diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 8c1e5313d5..8e03bddb4d 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -84,7 +84,7 @@ namespace osu.Game.Skinning if (ppCounter != null) { - ppCounter.Y = score.Position.Y + score.ScreenSpaceDrawQuad.Size.Y; + ppCounter.Y = score.Position.Y + ppCounter.ScreenSpaceDeltaToParentSpace(score.ScreenSpaceDrawQuad.Size).Y - 4; ppCounter.Origin = Anchor.TopCentre; ppCounter.Anchor = Anchor.TopCentre; } From 676df55a0e40df9a48e3661cf4f6ed89a90828d1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Oct 2021 15:39:29 +0900 Subject: [PATCH 2267/2442] Fade display out during rewind (as the value displayed is no longer valid) --- .../Graphics/UserInterface/RollingCounter.cs | 12 +++++---- .../Rulesets/Scoring/JudgementProcessor.cs | 7 +++++ .../Play/HUD/PerformancePointsCounter.cs | 26 +++++++++++++++++++ 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/RollingCounter.cs b/osu.Game/Graphics/UserInterface/RollingCounter.cs index 67b0b6a06b..16555075d1 100644 --- a/osu.Game/Graphics/UserInterface/RollingCounter.cs +++ b/osu.Game/Graphics/UserInterface/RollingCounter.cs @@ -25,7 +25,9 @@ namespace osu.Game.Graphics.UserInterface set => current.Current = value; } - private IHasText displayedCountSpriteText; + private IHasText displayedCountText; + + public Drawable DrawableCount { get; private set; } /// /// If true, the roll-up duration will be proportional to change in value. @@ -72,16 +74,16 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load() { - displayedCountSpriteText = CreateText(); + displayedCountText = CreateText(); UpdateDisplay(); - Child = (Drawable)displayedCountSpriteText; + Child = DrawableCount = (Drawable)displayedCountText; } protected void UpdateDisplay() { - if (displayedCountSpriteText != null) - displayedCountSpriteText.Text = FormatCount(DisplayedCount); + if (displayedCountText != null) + displayedCountText.Text = FormatCount(DisplayedCount); } protected override void LoadComplete() diff --git a/osu.Game/Rulesets/Scoring/JudgementProcessor.cs b/osu.Game/Rulesets/Scoring/JudgementProcessor.cs index 201a05e569..ed4a16f0e8 100644 --- a/osu.Game/Rulesets/Scoring/JudgementProcessor.cs +++ b/osu.Game/Rulesets/Scoring/JudgementProcessor.cs @@ -18,6 +18,11 @@ namespace osu.Game.Rulesets.Scoring /// public event Action NewJudgement; + /// + /// Invoked when a judgement is reverted, usually due to rewinding gameplay. + /// + public event Action JudgementReverted; + /// /// The maximum number of hits that can be judged. /// @@ -71,6 +76,8 @@ namespace osu.Game.Rulesets.Scoring JudgedHits--; RevertResultInternal(result); + + JudgementReverted?.Invoke(result); } /// diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index 2fc3f23190..b82a85691a 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -42,6 +42,9 @@ namespace osu.Game.Screens.Play.HUD [CanBeNull] private GameplayState gameplayState { get; set; } + [Resolved] + private GameplayClock gameplayClock { get; set; } + [CanBeNull] private TimedDifficultyAttributes[] timedAttributes; @@ -70,7 +73,24 @@ namespace osu.Game.Screens.Play.HUD base.LoadComplete(); if (scoreProcessor != null) + { scoreProcessor.NewJudgement += onNewJudgement; + scoreProcessor.JudgementReverted += onJudgementReverted; + } + } + + private bool isValid; + + protected bool IsValid + { + set + { + if (value == isValid) + return; + + isValid = value; + DrawableCount.FadeTo(isValid ? 1 : 0.3f, 1000, Easing.OutQuint); + } } private void onNewJudgement(JudgementResult judgement) @@ -86,6 +106,12 @@ namespace osu.Game.Screens.Play.HUD var calculator = gameplayState.Ruleset.CreatePerformanceCalculator(timedAttributes[attribIndex].Attributes, gameplayState.Score.ScoreInfo); Current.Value = (int)Math.Round(calculator?.Calculate() ?? 0, MidpointRounding.AwayFromZero); + IsValid = true; + } + + private void onJudgementReverted(JudgementResult obj) + { + IsValid = false; } protected override LocalisableString FormatCount(int count) => count.ToString(@"D"); From 45b63cbad9dbb31a35cf2da3d3a88b2de8a5460a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 5 Oct 2021 16:03:25 +0900 Subject: [PATCH 2268/2442] Remove unnecessary dependency --- osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index b82a85691a..c554a038ed 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -42,9 +42,6 @@ namespace osu.Game.Screens.Play.HUD [CanBeNull] private GameplayState gameplayState { get; set; } - [Resolved] - private GameplayClock gameplayClock { get; set; } - [CanBeNull] private TimedDifficultyAttributes[] timedAttributes; From 565e888f587e6bed57318d17eff71167e6820325 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Oct 2021 16:40:07 +0900 Subject: [PATCH 2269/2442] Tidy up attribute retrieval code --- .../Play/HUD/PerformancePointsCounter.cs | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index c554a038ed..fe34dd1d22 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -92,25 +92,32 @@ namespace osu.Game.Screens.Play.HUD private void onNewJudgement(JudgementResult judgement) { - if (gameplayState?.Score == null || timedAttributes == null || timedAttributes.Length == 0) + var attrib = getAttributeAtTime(judgement); + + if (gameplayState == null || attrib == null) return; - int attribIndex = Array.BinarySearch(timedAttributes, 0, timedAttributes.Length, new TimedDifficultyAttributes(judgement.HitObject.GetEndTime(), null)); - if (attribIndex < 0) - attribIndex = ~attribIndex - 1; - attribIndex = Math.Clamp(attribIndex, 0, timedAttributes.Length - 1); - - var calculator = gameplayState.Ruleset.CreatePerformanceCalculator(timedAttributes[attribIndex].Attributes, gameplayState.Score.ScoreInfo); + var calculator = gameplayState.Ruleset.CreatePerformanceCalculator(attrib, gameplayState.Score.ScoreInfo); Current.Value = (int)Math.Round(calculator?.Calculate() ?? 0, MidpointRounding.AwayFromZero); IsValid = true; } - private void onJudgementReverted(JudgementResult obj) + [CanBeNull] + private DifficultyAttributes getAttributeAtTime(JudgementResult judgement) { - IsValid = false; + if (timedAttributes == null || timedAttributes.Length == 0) + return null; + + int attribIndex = Array.BinarySearch(timedAttributes, 0, timedAttributes.Length, new TimedDifficultyAttributes(judgement.HitObject.GetEndTime(), null)); + if (attribIndex < 0) + attribIndex = ~attribIndex - 1; + + return timedAttributes[Math.Clamp(attribIndex, 0, timedAttributes.Length - 1)].Attributes; } + private void onJudgementReverted(JudgementResult obj) => IsValid = false; + protected override LocalisableString FormatCount(int count) => count.ToString(@"D"); protected override IHasText CreateText() => new TextComponent(); From eeb5f3d5191f87f20e9582caabab5792ab69bf2e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Oct 2021 16:48:54 +0900 Subject: [PATCH 2270/2442] Add basic test scene --- .../TestScenePerformancePointsCounter.cs | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs new file mode 100644 index 0000000000..350d08f63d --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs @@ -0,0 +1,83 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Diagnostics; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Testing; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.Play; +using osu.Game.Screens.Play.HUD; +using osuTK; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestScenePerformancePointsCounter : OsuTestScene + { + [Cached] + private GameplayState gameplayState; + + [Cached] + private ScoreProcessor scoreProcessor; + + private int iteration; + + public TestScenePerformancePointsCounter() + { + var ruleset = CreateRuleset(); + + Debug.Assert(ruleset != null); + + var beatmap = CreateWorkingBeatmap(ruleset.RulesetInfo) + .GetPlayableBeatmap(ruleset.RulesetInfo); + + gameplayState = new GameplayState(beatmap, ruleset); + scoreProcessor = new ScoreProcessor(); + } + + protected override Ruleset CreateRuleset() => new OsuRuleset(); + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("Create counter", () => + { + Child = new PerformancePointsCounter + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(5), + }; + }); + + AddRepeatStep("Add judgement", () => + { + var scoreInfo = gameplayState.Score.ScoreInfo; + + scoreInfo.MaxCombo = iteration * 1000; + scoreInfo.Accuracy = 1; + scoreInfo.Statistics[HitResult.Great] = iteration * 1000; + + scoreProcessor.ApplyResult(new OsuJudgementResult(new HitObject + { + StartTime = iteration * 10000, + }, new OsuJudgement()) + { + Type = HitResult.Perfect, + }); + + iteration++; + }, 10); + + AddStep("Revert judgement", () => + { + scoreProcessor.RevertResult(new JudgementResult(new HitObject(), new OsuJudgement())); + }); + } + } +} From fa7f11d906f8214dc15b01b04b405f8f957909ca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Oct 2021 16:51:49 +0900 Subject: [PATCH 2271/2442] Add easing to rolling counter value --- osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index fe34dd1d22..ab10399903 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -34,6 +34,10 @@ namespace osu.Game.Screens.Play.HUD { public bool UsesFixedAnchor { get; set; } + protected override bool IsRollingProportional => true; + + protected override double RollingDuration => 1000; + [CanBeNull] [Resolved(CanBeNull = true)] private ScoreProcessor scoreProcessor { get; set; } From 599d82e383f167632719758554b024fb4ab58bf4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Oct 2021 16:59:54 +0900 Subject: [PATCH 2272/2442] Avoid returning a live `IEnumerable` --- osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 4 ++-- osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 10 +++++++--- osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs | 8 ++++---- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index e6c287112f..3777365088 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -148,9 +148,9 @@ namespace osu.Game.Beatmaps }, token, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler); } - public Task GetTimedDifficultyAttributesAsync(WorkingBeatmap beatmap, Ruleset ruleset, Mod[] mods, CancellationToken token = default) + public Task> GetTimedDifficultyAttributesAsync(WorkingBeatmap beatmap, Ruleset ruleset, Mod[] mods, CancellationToken token = default) { - return Task.Factory.StartNew(() => ruleset.CreateDifficultyCalculator(beatmap).CalculateTimed(mods).ToArray(), + return Task.Factory.StartNew(() => ruleset.CreateDifficultyCalculator(beatmap).CalculateTimed(mods), token, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler); diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 49a4b2c265..a7c4790366 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -58,12 +58,14 @@ namespace osu.Game.Rulesets.Difficulty return CreateDifficultyAttributes(Beatmap, playableMods, skills, clockRate); } - public IEnumerable CalculateTimed(params Mod[] mods) + public List CalculateTimed(params Mod[] mods) { preProcess(mods); + var attribs = new List(); + if (!Beatmap.HitObjects.Any()) - yield break; + return attribs; var skills = CreateSkills(Beatmap, playableMods, clockRate); var progressiveBeatmap = new ProgressiveCalculationBeatmap(Beatmap); @@ -75,8 +77,10 @@ namespace osu.Game.Rulesets.Difficulty foreach (var skill in skills) skill.ProcessInternal(hitObject); - yield return new TimedDifficultyAttributes(hitObject.EndTime, CreateDifficultyAttributes(progressiveBeatmap, playableMods, skills, clockRate)); + attribs.Add(new TimedDifficultyAttributes(hitObject.EndTime, CreateDifficultyAttributes(progressiveBeatmap, playableMods, skills, clockRate))); } + + return attribs; } /// diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index ab10399903..514369735e 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -47,7 +47,7 @@ namespace osu.Game.Screens.Play.HUD private GameplayState gameplayState { get; set; } [CanBeNull] - private TimedDifficultyAttributes[] timedAttributes; + private List timedAttributes; private readonly CancellationTokenSource loadCancellationSource = new CancellationTokenSource(); @@ -110,14 +110,14 @@ namespace osu.Game.Screens.Play.HUD [CanBeNull] private DifficultyAttributes getAttributeAtTime(JudgementResult judgement) { - if (timedAttributes == null || timedAttributes.Length == 0) + if (timedAttributes == null || timedAttributes.Count == 0) return null; - int attribIndex = Array.BinarySearch(timedAttributes, 0, timedAttributes.Length, new TimedDifficultyAttributes(judgement.HitObject.GetEndTime(), null)); + int attribIndex = timedAttributes.BinarySearch(new TimedDifficultyAttributes(judgement.HitObject.GetEndTime(), null)); if (attribIndex < 0) attribIndex = ~attribIndex - 1; - return timedAttributes[Math.Clamp(attribIndex, 0, timedAttributes.Length - 1)].Attributes; + return timedAttributes[Math.Clamp(attribIndex, 0, timedAttributes.Count - 1)].Attributes; } private void onJudgementReverted(JudgementResult obj) => IsValid = false; From 04538a69e403fcbd8bbcf3e8e2baea5afc00191e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Oct 2021 17:10:24 +0900 Subject: [PATCH 2273/2442] Add assert tests --- .../TestScenePerformancePointsCounter.cs | 54 +++++++++++++------ 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs index 350d08f63d..26f4bda171 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Diagnostics; +using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Testing; @@ -26,6 +27,7 @@ namespace osu.Game.Tests.Visual.Gameplay private ScoreProcessor scoreProcessor; private int iteration; + private PerformancePointsCounter counter; public TestScenePerformancePointsCounter() { @@ -47,37 +49,55 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("Create counter", () => { - Child = new PerformancePointsCounter + iteration = 0; + + Child = counter = new PerformancePointsCounter { Anchor = Anchor.Centre, Origin = Anchor.Centre, Scale = new Vector2(5), }; }); + } - AddRepeatStep("Add judgement", () => - { - var scoreInfo = gameplayState.Score.ScoreInfo; + [Test] + public void TestBasicCounting() + { + AddAssert("counter displaying zero", () => counter.Current.Value == 0); - scoreInfo.MaxCombo = iteration * 1000; - scoreInfo.Accuracy = 1; - scoreInfo.Statistics[HitResult.Great] = iteration * 1000; + AddRepeatStep("Add judgement", applyOneJudgement, 10); - scoreProcessor.ApplyResult(new OsuJudgementResult(new HitObject - { - StartTime = iteration * 10000, - }, new OsuJudgement()) - { - Type = HitResult.Perfect, - }); - - iteration++; - }, 10); + AddUntilStep("counter non-zero", () => counter.Current.Value > 0); AddStep("Revert judgement", () => { scoreProcessor.RevertResult(new JudgementResult(new HitObject(), new OsuJudgement())); }); + + AddUntilStep("counter faded", () => counter.Child.Alpha < 1); + + AddStep("Add judgement", applyOneJudgement); + + AddUntilStep("counter opaque", () => counter.Child.Alpha == 1); + } + + private void applyOneJudgement() + { + var scoreInfo = gameplayState.Score.ScoreInfo; + + scoreInfo.MaxCombo = iteration * 1000; + scoreInfo.Accuracy = 1; + scoreInfo.Statistics[HitResult.Great] = iteration * 1000; + + scoreProcessor.ApplyResult(new OsuJudgementResult(new HitObject + { + StartTime = iteration * 10000, + }, new OsuJudgement()) + { + Type = HitResult.Perfect, + }); + + iteration++; } } } From f64226ded6da72516c59a65047b19b1eaf4427b4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Oct 2021 17:10:32 +0900 Subject: [PATCH 2274/2442] Fix display not displaying correctly after initial load --- osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index 514369735e..da821d76ad 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -51,6 +51,8 @@ namespace osu.Game.Screens.Play.HUD private readonly CancellationTokenSource loadCancellationSource = new CancellationTokenSource(); + private JudgementResult lastJudgement; + public PerformancePointsCounter() { Current.Value = DisplayedCount = 0; @@ -65,7 +67,12 @@ namespace osu.Game.Screens.Play.HUD { var gameplayWorkingBeatmap = new GameplayWorkingBeatmap(gameplayState.Beatmap); difficultyCache.GetTimedDifficultyAttributesAsync(gameplayWorkingBeatmap, gameplayState.Ruleset, gameplayState.Mods.ToArray(), loadCancellationSource.Token) - .ContinueWith(r => Schedule(() => timedAttributes = r.Result), TaskContinuationOptions.OnlyOnRanToCompletion); + .ContinueWith(r => Schedule(() => + { + timedAttributes = r.Result; + if (lastJudgement != null) + onNewJudgement(lastJudgement); + }), TaskContinuationOptions.OnlyOnRanToCompletion); } } @@ -96,6 +103,8 @@ namespace osu.Game.Screens.Play.HUD private void onNewJudgement(JudgementResult judgement) { + lastJudgement = judgement; + var attrib = getAttributeAtTime(judgement); if (gameplayState == null || attrib == null) From 1e4da8112088b5a6a315fa4b962e3e7f5245c39a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Oct 2021 17:14:09 +0900 Subject: [PATCH 2275/2442] Fix import notifications not showing correct text --- osu.Game/Database/ArchiveModelManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 9ad2dec12e..ee1a7e2900 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -197,7 +197,7 @@ namespace osu.Game.Database else { notification.CompletionText = imported.Count == 1 - ? $"Imported {imported.First()}!" + ? $"Imported {imported.First().Value}!" : $"Imported {imported.Count} {HumanisedModelName}s!"; if (imported.Count > 0 && PostImport != null) From 0859c336de52dcb5d69492f02ef5de704d782653 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Oct 2021 17:24:36 +0900 Subject: [PATCH 2276/2442] Also dim counter during initial calculation phase --- .../Gameplay/TestScenePerformancePointsCounter.cs | 1 + osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs index 26f4bda171..c7d2204de2 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs @@ -68,6 +68,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddRepeatStep("Add judgement", applyOneJudgement, 10); AddUntilStep("counter non-zero", () => counter.Current.Value > 0); + AddUntilStep("counter opaque", () => counter.Child.Alpha == 1); AddStep("Revert judgement", () => { diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index da821d76ad..9eac62fe73 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -38,6 +38,8 @@ namespace osu.Game.Screens.Play.HUD protected override double RollingDuration => 1000; + private const float alpha_when_invalid = 0.3f; + [CanBeNull] [Resolved(CanBeNull = true)] private ScoreProcessor scoreProcessor { get; set; } @@ -70,6 +72,7 @@ namespace osu.Game.Screens.Play.HUD .ContinueWith(r => Schedule(() => { timedAttributes = r.Result; + IsValid = true; if (lastJudgement != null) onNewJudgement(lastJudgement); }), TaskContinuationOptions.OnlyOnRanToCompletion); @@ -97,7 +100,7 @@ namespace osu.Game.Screens.Play.HUD return; isValid = value; - DrawableCount.FadeTo(isValid ? 1 : 0.3f, 1000, Easing.OutQuint); + DrawableCount.FadeTo(isValid ? 1 : alpha_when_invalid, 1000, Easing.OutQuint); } } @@ -133,7 +136,10 @@ namespace osu.Game.Screens.Play.HUD protected override LocalisableString FormatCount(int count) => count.ToString(@"D"); - protected override IHasText CreateText() => new TextComponent(); + protected override IHasText CreateText() => new TextComponent + { + Alpha = alpha_when_invalid + }; protected override void Dispose(bool isDisposing) { From 2be44188efbd6befbd4a39242a9f823509d7e35c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Oct 2021 17:59:38 +0900 Subject: [PATCH 2277/2442] Add missing null checks --- osu.Game/Overlays/RankingsOverlay.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Overlays/RankingsOverlay.cs b/osu.Game/Overlays/RankingsOverlay.cs index 2263d54d7b..80ce2e038d 100644 --- a/osu.Game/Overlays/RankingsOverlay.cs +++ b/osu.Game/Overlays/RankingsOverlay.cs @@ -143,6 +143,9 @@ namespace osu.Game.Overlays switch (request) { case GetUserRankingsRequest userRequest: + if (userRequest.Response == null) + return null; + switch (userRequest.Type) { case UserRankingsType.Performance: @@ -155,7 +158,12 @@ namespace osu.Game.Overlays return null; case GetCountryRankingsRequest countryRequest: + { + if (countryRequest.Response == null) + return null; + return new CountriesTable(1, countryRequest.Response.Countries); + } } return null; From 94153e8bba3293d7141ab4091d0ab3203c20280f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Oct 2021 18:06:24 +0900 Subject: [PATCH 2278/2442] Fix `TestDifficultyIconSelectingForDifferentRuleset` potentially failing due to async load --- osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 70224ae9f2..067f1cabb4 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -714,10 +714,11 @@ namespace osu.Game.Tests.Visual.SongSelect }); FilterableDifficultyIcon difficultyIcon = null; - AddStep("Find an icon for different ruleset", () => + AddUntilStep("Find an icon for different ruleset", () => { difficultyIcon = set.ChildrenOfType() - .First(icon => icon.Item.BeatmapInfo.Ruleset.ID == 3); + .FirstOrDefault(icon => icon.Item.BeatmapInfo.Ruleset.ID == 3); + return difficultyIcon != null; }); AddAssert("Check ruleset is osu!", () => Ruleset.Value.ID == 0); From 5d708b612d3c4326c56a308b5e42723d00d91ed0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Oct 2021 18:17:20 +0900 Subject: [PATCH 2279/2442] Fix delete local score test not waiting for "fetch" to complete Even though this is a completely local operation in this case, there's still a level of asynchronous operation which was recent introduced with the score ordering: https://github.com/ppy/osu/blob/853cf6feaa165e833ecb7ca18c6cdffe8ca6e005/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs#L159 This means there is a brief period where the `Scores` property is null, after `Reset()` is called in the re-fetch procedure. --- .../Visual/UserInterface/TestSceneDeleteLocalScore.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index d0a76bac27..189b143a35 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -162,6 +162,8 @@ namespace osu.Game.Tests.Visual.UserInterface InputManager.Click(MouseButton.Left); }); + AddUntilStep("wait for fetch", () => leaderboard.Scores != null); + AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s.OnlineScoreID != scoreBeingDeleted.OnlineScoreID)); } From e6efdae7c95967543caedd282f91cf03b2ea953d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Oct 2021 18:53:20 +0900 Subject: [PATCH 2280/2442] Add various logging output in an atttempt to figure multiplayer test failure --- osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs | 3 +++ osu.Game/Tests/Visual/ScreenTestScene.cs | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs index a8fda19c60..c040ab27e4 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs @@ -6,6 +6,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Graphics; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Beatmaps; @@ -68,6 +69,8 @@ namespace osu.Game.Tests.Visual.Multiplayer LoadScreen(dependenciesScreen = new DependenciesScreen(client)); }); + AddUntilStep("wait for dependencies screen", () => Stack.CurrentScreen is DependenciesScreen); + AddUntilStep("wait for dependencies to start load", () => dependenciesScreen.LoadState > LoadState.Ready); AddUntilStep("wait for dependencies to load", () => dependenciesScreen.IsLoaded); AddStep("load multiplayer", () => LoadScreen(multiplayerScreen)); diff --git a/osu.Game/Tests/Visual/ScreenTestScene.cs b/osu.Game/Tests/Visual/ScreenTestScene.cs index b30be05ac4..966c513269 100644 --- a/osu.Game/Tests/Visual/ScreenTestScene.cs +++ b/osu.Game/Tests/Visual/ScreenTestScene.cs @@ -4,6 +4,8 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Logging; +using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Overlays; using osu.Game.Screens; @@ -32,6 +34,9 @@ namespace osu.Game.Tests.Visual content = new Container { RelativeSizeAxes = Axes.Both }, DialogOverlay = new DialogOverlay() }); + + Stack.ScreenPushed += (lastScreen, newScreen) => Logger.Log($"{nameof(ScreenTestScene)} screen changed → {newScreen}"); + Stack.ScreenExited += (lastScreen, newScreen) => Logger.Log($"{nameof(ScreenTestScene)} screen changed ← {newScreen}"); } protected void LoadScreen(OsuScreen screen) => Stack.Push(screen); From 98fef6ece2d39efb59ea89384a7799b5f7c0efba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Oct 2021 19:08:30 +0900 Subject: [PATCH 2281/2442] Handle judgement reverts with actual display updates --- .../Gameplay/TestScenePerformancePointsCounter.cs | 8 ++++++-- .../Screens/Play/HUD/PerformancePointsCounter.cs | 15 ++++++++------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs index c7d2204de2..4c48d52acd 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs @@ -63,6 +63,8 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestBasicCounting() { + int previousValue = 0; + AddAssert("counter displaying zero", () => counter.Current.Value == 0); AddRepeatStep("Add judgement", applyOneJudgement, 10); @@ -72,14 +74,16 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Revert judgement", () => { + previousValue = counter.Current.Value; + scoreProcessor.RevertResult(new JudgementResult(new HitObject(), new OsuJudgement())); }); - AddUntilStep("counter faded", () => counter.Child.Alpha < 1); + AddUntilStep("counter decreased", () => counter.Current.Value < previousValue); AddStep("Add judgement", applyOneJudgement); - AddUntilStep("counter opaque", () => counter.Child.Alpha == 1); + AddUntilStep("counter non-zero", () => counter.Current.Value > 0); } private void applyOneJudgement() diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index 9eac62fe73..2ae7b5660a 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -74,7 +74,7 @@ namespace osu.Game.Screens.Play.HUD timedAttributes = r.Result; IsValid = true; if (lastJudgement != null) - onNewJudgement(lastJudgement); + onJudgementChanged(lastJudgement); }), TaskContinuationOptions.OnlyOnRanToCompletion); } } @@ -85,8 +85,8 @@ namespace osu.Game.Screens.Play.HUD if (scoreProcessor != null) { - scoreProcessor.NewJudgement += onNewJudgement; - scoreProcessor.JudgementReverted += onJudgementReverted; + scoreProcessor.NewJudgement += onJudgementChanged; + scoreProcessor.JudgementReverted += onJudgementChanged; } } @@ -104,14 +104,17 @@ namespace osu.Game.Screens.Play.HUD } } - private void onNewJudgement(JudgementResult judgement) + private void onJudgementChanged(JudgementResult judgement) { lastJudgement = judgement; var attrib = getAttributeAtTime(judgement); if (gameplayState == null || attrib == null) + { + IsValid = false; return; + } var calculator = gameplayState.Ruleset.CreatePerformanceCalculator(attrib, gameplayState.Score.ScoreInfo); @@ -132,8 +135,6 @@ namespace osu.Game.Screens.Play.HUD return timedAttributes[Math.Clamp(attribIndex, 0, timedAttributes.Count - 1)].Attributes; } - private void onJudgementReverted(JudgementResult obj) => IsValid = false; - protected override LocalisableString FormatCount(int count) => count.ToString(@"D"); protected override IHasText CreateText() => new TextComponent @@ -146,7 +147,7 @@ namespace osu.Game.Screens.Play.HUD base.Dispose(isDisposing); if (scoreProcessor != null) - scoreProcessor.NewJudgement -= onNewJudgement; + scoreProcessor.NewJudgement -= onJudgementChanged; loadCancellationSource?.Cancel(); } From 12da27cde728b0d17517e23306b0e180be9e75d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 5 Oct 2021 20:52:40 +0200 Subject: [PATCH 2282/2442] Add test coverage for loading process on channel join --- .../Visual/Online/TestSceneChatOverlay.cs | 38 ++++++++++++++++++- .../Online/API/Requests/GetMessagesRequest.cs | 6 +-- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 609e637914..9562b41363 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -18,6 +19,7 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.Chat; using osu.Game.Overlays; +using osu.Game.Overlays.Chat; using osu.Game.Overlays.Chat.Selection; using osu.Game.Overlays.Chat.Tabs; using osu.Game.Users; @@ -41,6 +43,9 @@ namespace osu.Game.Tests.Visual.Online private Channel channel2 => channels[1]; private Channel channel3 => channels[2]; + [CanBeNull] + private Func> onGetMessages; + [Resolved] private GameHost host { get; set; } @@ -79,6 +84,8 @@ namespace osu.Game.Tests.Visual.Online { AddStep("register request handling", () => { + onGetMessages = null; + ((DummyAPIAccess)API).HandleRequest = req => { switch (req) @@ -102,6 +109,12 @@ namespace osu.Game.Tests.Visual.Online } return true; + + case GetMessagesRequest getMessages: + var messages = onGetMessages?.Invoke(getMessages.Channel); + if (messages != null) + getMessages.TriggerSuccess(messages); + return true; } return false; @@ -122,14 +135,37 @@ namespace osu.Game.Tests.Visual.Online } [Test] - public void TestSelectingChannelClosesSelector() + public void TestChannelSelection() { AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible); + AddStep("Setup get message response", () => onGetMessages = channel => + { + if (channel == channel1) + { + return new List + { + new Message(1) + { + ChannelId = channel1.Id, + Content = "hello from channel 1!", + Sender = new User + { + Id = 2, + Username = "test_user" + } + } + }; + } + + return null; + }); AddStep("Join channel 1", () => channelManager.JoinChannel(channel1)); AddStep("Switch to channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); AddAssert("Current channel is channel 1", () => currentChannel == channel1); + AddUntilStep("Loading spinner hidden", () => chatOverlay.ChildrenOfType().All(spinner => !spinner.IsPresent)); + AddAssert("Channel message shown", () => chatOverlay.ChildrenOfType().Count() == 1); AddAssert("Channel selector was closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden); } diff --git a/osu.Game/Online/API/Requests/GetMessagesRequest.cs b/osu.Game/Online/API/Requests/GetMessagesRequest.cs index 36e81a9348..651f8a06c5 100644 --- a/osu.Game/Online/API/Requests/GetMessagesRequest.cs +++ b/osu.Game/Online/API/Requests/GetMessagesRequest.cs @@ -8,13 +8,13 @@ namespace osu.Game.Online.API.Requests { public class GetMessagesRequest : APIRequest> { - private readonly Channel channel; + public readonly Channel Channel; public GetMessagesRequest(Channel channel) { - this.channel = channel; + Channel = channel; } - protected override string Target => $@"chat/channels/{channel.Id}/messages"; + protected override string Target => $@"chat/channels/{Channel.Id}/messages"; } } From a5b07ce4feaf88cf2f2f5eee6e43b166a8207737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 5 Oct 2021 20:53:06 +0200 Subject: [PATCH 2283/2442] Fix backwards containment check in chat channel load callback --- osu.Game/Overlays/ChatOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 20d637d957..4b27335c7c 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -285,7 +285,7 @@ namespace osu.Game.Overlays return; // check once more to ensure the channel hasn't since been removed from the loaded channels list (may have been left by some automated means). - if (loadedChannels.Contains(loaded)) + if (!loadedChannels.Contains(loaded)) return; loading.Hide(); From baa8baaa1efd762b650c4e2b9e409f700e601fe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 5 Oct 2021 21:12:35 +0200 Subject: [PATCH 2284/2442] Fix "most played beatmap" request breakage after property rename --- .../Online/API/Requests/Responses/APIUserMostPlayedBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIUserMostPlayedBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIUserMostPlayedBeatmap.cs index 15f67eda47..10f7ca6fe2 100644 --- a/osu.Game/Online/API/Requests/Responses/APIUserMostPlayedBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIUserMostPlayedBeatmap.cs @@ -15,7 +15,7 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty("count")] public int PlayCount { get; set; } - [JsonProperty] + [JsonProperty("beatmap")] private BeatmapInfo beatmapInfo { get; set; } [JsonProperty] From 3cb816b6cd2165788a288d51457ec737c52944e2 Mon Sep 17 00:00:00 2001 From: emu1337 Date: Tue, 5 Oct 2021 21:19:00 +0200 Subject: [PATCH 2285/2442] fixed some nerfs not carrying to the next iteration --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index 4048d84d35..c981348e44 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills rhythmComplexitySum += Math.Sqrt(effectiveRatio * startRatio) * currHistoricalDecay * Math.Sqrt(4 + islandSize) / 2 * Math.Sqrt(4 + previousIslandSize) / 2; - startRatio = windowPenalty * currRatio; + startRatio = effectiveRatio; previousIslandSize = islandSize; // log the last island size. From 777763a55095147c307b32e502aa5fd9220f5066 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Oct 2021 05:27:36 +0900 Subject: [PATCH 2286/2442] Add more comprehensive (and failing) test coverage of replay download button --- .../Gameplay/TestSceneReplayDownloadButton.cs | 84 +++++++++++++++++-- 1 file changed, 75 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs index 1809332bce..5dd4ecf30b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs @@ -8,34 +8,97 @@ using osu.Game.Online.API.Requests.Responses; using osu.Game.Scoring; using osu.Game.Users; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Game.Rulesets; using osu.Game.Screens.Ranking; +using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay { [TestFixture] - public class TestSceneReplayDownloadButton : OsuTestScene + public class TestSceneReplayDownloadButton : OsuManualInputManagerTestScene { [Resolved] private RulesetStore rulesets { get; set; } private TestReplayDownloadButton downloadButton; - public TestSceneReplayDownloadButton() + [Test] + public void TestDisplayStates() { - createButton(true); + AddStep(@"create button with replay", () => + { + Child = downloadButton = new TestReplayDownloadButton(getScoreInfo(true)) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + }); + + AddUntilStep("wait for load", () => downloadButton.IsLoaded); + + AddStep("click button", () => downloadButton.TriggerClick()); + AddStep(@"downloading state", () => downloadButton.SetDownloadState(DownloadState.Downloading)); AddStep(@"locally available state", () => downloadButton.SetDownloadState(DownloadState.LocallyAvailable)); AddStep(@"not downloaded state", () => downloadButton.SetDownloadState(DownloadState.NotDownloaded)); - createButton(false); - createButtonNoScore(); } - private void createButton(bool withReplay) + [Test] + public void TestButtonWithReplayStartsDownload() { - AddStep(withReplay ? @"create button with replay" : "create button without replay", () => + bool downloadStarted = false; + bool downloadFinished = false; + + AddStep(@"create button with replay", () => { - Child = downloadButton = new TestReplayDownloadButton(getScoreInfo(withReplay)) + downloadStarted = false; + downloadFinished = false; + + Child = downloadButton = new TestReplayDownloadButton(getScoreInfo(true)) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + + downloadButton.State.BindValueChanged(state => + { + switch (state.NewValue) + { + case DownloadState.Downloading: + downloadStarted = true; + break; + } + + switch (state.OldValue) + { + case DownloadState.Downloading: + downloadFinished = true; + break; + } + }); + }); + + AddUntilStep("wait for load", () => downloadButton.IsLoaded); + + AddAssert("state is available", () => downloadButton.State.Value == DownloadState.NotDownloaded); + + AddStep("click button", () => + { + InputManager.MoveMouseTo(downloadButton); + InputManager.Click(MouseButton.Left); + }); + + AddAssert("state entered downloading", () => downloadStarted); + AddUntilStep("state left downloading", () => downloadFinished); + } + + [Test] + public void TestButtonWithoutReplay() + { + AddStep("create button without replay", () => + { + Child = downloadButton = new TestReplayDownloadButton(getScoreInfo(false)) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -45,7 +108,8 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("wait for load", () => downloadButton.IsLoaded); } - private void createButtonNoScore() + [Test] + public void CreateButtonWithNoScore() { AddStep("create button with null score", () => { @@ -78,6 +142,8 @@ namespace osu.Game.Tests.Visual.Gameplay { public void SetDownloadState(DownloadState state) => State.Value = state; + public new Bindable State => base.State; + public TestReplayDownloadButton(ScoreInfo score) : base(score) { From 5a4474e1b2560896c3f09dc2d4fc94c437b70d88 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Oct 2021 05:28:11 +0900 Subject: [PATCH 2287/2442] Fix incorrect DI retrieval in `ReplayDownloadButton` --- osu.Game/Screens/Ranking/ReplayDownloadButton.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs index d96b6989b4..e644eb671a 100644 --- a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs +++ b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs @@ -40,7 +40,7 @@ namespace osu.Game.Screens.Ranking } [BackgroundDependencyLoader(true)] - private void load(OsuGame game, ScoreModelDownloader scores) + private void load(OsuGame game, ScoreManager scores) { InternalChild = shakeContainer = new ShakeContainer { @@ -60,7 +60,7 @@ namespace osu.Game.Screens.Ranking break; case DownloadState.NotDownloaded: - scores.Download(Model.Value); + scores.Download(Model.Value, false); break; case DownloadState.Importing: From 1f6a31355c514e809ca61a64f3a82e3a608e1777 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Oct 2021 05:30:49 +0900 Subject: [PATCH 2288/2442] Remove unused using statement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Tests/Visual/ScreenTestScene.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Tests/Visual/ScreenTestScene.cs b/osu.Game/Tests/Visual/ScreenTestScene.cs index 966c513269..aa46b516bf 100644 --- a/osu.Game/Tests/Visual/ScreenTestScene.cs +++ b/osu.Game/Tests/Visual/ScreenTestScene.cs @@ -5,7 +5,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Logging; -using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Overlays; using osu.Game.Screens; From 1a784b788dba00e79be4011b2af652d9250d3be4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Oct 2021 05:31:07 +0900 Subject: [PATCH 2289/2442] Fix incorrect load state check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs index c040ab27e4..80217a7726 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs @@ -70,7 +70,7 @@ namespace osu.Game.Tests.Visual.Multiplayer }); AddUntilStep("wait for dependencies screen", () => Stack.CurrentScreen is DependenciesScreen); - AddUntilStep("wait for dependencies to start load", () => dependenciesScreen.LoadState > LoadState.Ready); + AddUntilStep("wait for dependencies to start load", () => dependenciesScreen.LoadState > LoadState.NotLoaded); AddUntilStep("wait for dependencies to load", () => dependenciesScreen.IsLoaded); AddStep("load multiplayer", () => LoadScreen(multiplayerScreen)); From 31c0c7a8881e56a02b44794a3d2406548e79b69a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Oct 2021 05:49:04 +0900 Subject: [PATCH 2290/2442] Remove pointless (and incorrect) click step --- osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs index 5dd4ecf30b..8197e09594 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs @@ -37,8 +37,6 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("wait for load", () => downloadButton.IsLoaded); - AddStep("click button", () => downloadButton.TriggerClick()); - AddStep(@"downloading state", () => downloadButton.SetDownloadState(DownloadState.Downloading)); AddStep(@"locally available state", () => downloadButton.SetDownloadState(DownloadState.LocallyAvailable)); AddStep(@"not downloaded state", () => downloadButton.SetDownloadState(DownloadState.NotDownloaded)); From d6f25e07ccc453bc06de28fef5eefaa2540e1e4f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Oct 2021 05:49:18 +0900 Subject: [PATCH 2291/2442] Add assert coverage of non-downloadable states --- .../Visual/Gameplay/TestSceneReplayDownloadButton.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs index 8197e09594..5e2374cbcb 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Online; @@ -9,6 +10,8 @@ using osu.Game.Scoring; using osu.Game.Users; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Testing; +using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets; using osu.Game.Screens.Ranking; using osuTK.Input; @@ -104,6 +107,9 @@ namespace osu.Game.Tests.Visual.Gameplay }); AddUntilStep("wait for load", () => downloadButton.IsLoaded); + + AddAssert("state is not downloaded", () => downloadButton.State.Value == DownloadState.NotDownloaded); + AddAssert("button is not enabled", () => !downloadButton.ChildrenOfType().First().Enabled.Value); } [Test] @@ -119,6 +125,9 @@ namespace osu.Game.Tests.Visual.Gameplay }); AddUntilStep("wait for load", () => downloadButton.IsLoaded); + + AddAssert("state is not downloaded", () => downloadButton.State.Value == DownloadState.NotDownloaded); + AddAssert("button is not enabled", () => !downloadButton.ChildrenOfType().First().Enabled.Value); } private ScoreInfo getScoreInfo(bool replayAvailable) From 4d5696959b0435bf4bcb0fad96b7c233eaf2bdb6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Oct 2021 05:52:28 +0900 Subject: [PATCH 2292/2442] Remove unnecessary access modifier in interface Co-authored-by: Dan Balasescu --- osu.Game/Rulesets/IRulesetInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/IRulesetInfo.cs b/osu.Game/Rulesets/IRulesetInfo.cs index ded3ac4b58..779433dc81 100644 --- a/osu.Game/Rulesets/IRulesetInfo.cs +++ b/osu.Game/Rulesets/IRulesetInfo.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets /// string InstantiationInfo { get; } - public Ruleset? CreateInstance() + Ruleset? CreateInstance() { var type = Type.GetType(InstantiationInfo); From 4f59fc15a50910eb4716f3968779e64b28856acc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Oct 2021 05:54:37 +0900 Subject: [PATCH 2293/2442] Mark `BeatmapSet` as nullable for the time being --- osu.Game/Beatmaps/IBeatmapInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/IBeatmapInfo.cs b/osu.Game/Beatmaps/IBeatmapInfo.cs index 6a3f1b43d8..3d51c5d4b6 100644 --- a/osu.Game/Beatmaps/IBeatmapInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapInfo.cs @@ -31,7 +31,7 @@ namespace osu.Game.Beatmaps /// /// The beatmap set this beatmap is part of. /// - IBeatmapSetInfo BeatmapSet { get; } + IBeatmapSetInfo? BeatmapSet { get; } /// /// The playable length in milliseconds of this beatmap. From d17beb9bbe0ab1e0551ea210c01515757aa073b8 Mon Sep 17 00:00:00 2001 From: emu1337 Date: Wed, 6 Oct 2021 01:39:01 +0200 Subject: [PATCH 2294/2442] improved overall balance --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index c981348e44..cae6b8e01c 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills public class Speed : OsuStrainSkill { private const double single_spacing_threshold = 125; - private const double rhythm_multiplier = 0.675; + private const double rhythm_multiplier = 0.75; private const int history_time_max = 5000; // 5 seconds of calculatingRhythmBonus max. private const double min_speed_bonus = 75; // ~200BPM private const double speed_balancing_factor = 40; @@ -70,11 +70,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double currDelta = currObj.StrainTime; double prevDelta = prevObj.StrainTime; double lastDelta = lastObj.StrainTime; - double currRatio = 1.0 + Math.Min(4.5, 6 * Math.Pow(Math.Sin(Math.PI / (Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta))), 2)); // fancy function to calculate rhythmbonuses. + double currRatio = 1.0 + 6.0 * Math.Min(0.5, Math.Pow(Math.Sin(Math.PI / (Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta))), 2)); // fancy function to calculate rhythmbonuses. - double windowPenalty = Math.Min(1, Math.Max(0, Math.Max(prevDelta, currDelta) - Math.Min(prevDelta, currDelta) - greatWindow) / greatWindow); + double windowPenalty = Math.Min(1, Math.Max(0, Math.Abs(prevDelta - currDelta) - greatWindow * 0.6) / (greatWindow * 0.6)); - windowPenalty = Math.Min(1, windowPenalty * (previousIslandSize + islandSize)); + windowPenalty = Math.Min(1, windowPenalty); + + double effectiveRatio = windowPenalty * currRatio; if (firstDeltaSwitch) { @@ -85,8 +87,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } else { - double effectiveRatio = windowPenalty * currRatio; - if (Previous[i - 1].BaseObject is Slider) // bpm change is into slider, this is easy acc window effectiveRatio *= 0.125; @@ -118,7 +118,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { // Begin counting island until we change speed again. firstDeltaSwitch = true; - startRatio = windowPenalty * currRatio; + startRatio = effectiveRatio; islandSize = 1; } } From df182ba92ba94030c136a816ecc2c2cf8fb80e12 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Wed, 6 Oct 2021 11:27:13 +0900 Subject: [PATCH 2295/2442] Set fQ to recommended value from BASS developer to prevent filter calculations from overflowing when approaching nyquist --- osu.Game/Audio/Effects/Filter.cs | 43 ++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/osu.Game/Audio/Effects/Filter.cs b/osu.Game/Audio/Effects/Filter.cs index 142e6b8fff..00b617ffa1 100644 --- a/osu.Game/Audio/Effects/Filter.cs +++ b/osu.Game/Audio/Effects/Filter.cs @@ -10,7 +10,7 @@ namespace osu.Game.Audio.Effects { public class Filter : Component, ITransformableFilter { - public readonly int MaxCutoff; + public readonly int MaxCutoff = 22049; // nyquist - 1hz private readonly AudioMixer mixer; private readonly BQFParameters filter; private readonly BQFType type; @@ -18,36 +18,29 @@ namespace osu.Game.Audio.Effects public BindableNumber Cutoff { get; } /// - /// A BiQuad filter that performs a filter-sweep when toggled on or off. + /// A Component that implements a BASS FX BiQuad Filter Effect. /// - /// The mixer this effect should be attached to. + /// The mixer this effect should be applied to. /// The type of filter (e.g. LowPass, HighPass, etc) public Filter(AudioMixer mixer, BQFType type = BQFType.LowPass) { this.mixer = mixer; this.type = type; - var initialCutoff = 1; + int initialCutoff; - // These max cutoff values are a work-around for BASS' BiQuad filters behaving weirdly when approaching nyquist. - // Note that these values assume a sample rate of 44100 (as per BassAudioMixer in osu.Framework) - // See also https://www.un4seen.com/forum/?topic=19542.0 for more information. switch (type) { case BQFType.HighPass: - MaxCutoff = 21968; // beyond this value, the high-pass cuts out + initialCutoff = 1; break; case BQFType.LowPass: - MaxCutoff = initialCutoff = 14000; // beyond (roughly) this value, the low-pass filter audibly wraps/reflects - break; - - case BQFType.BandPass: - MaxCutoff = 16000; // beyond (roughly) this value, the band-pass filter audibly wraps/reflects + initialCutoff = MaxCutoff; break; default: - MaxCutoff = 22050; // default to nyquist for other filter types, TODO: handle quirks of other filter types + initialCutoff = 500; // A default that should ensure audio remains audible for other filters. break; } @@ -59,11 +52,12 @@ namespace osu.Game.Audio.Effects filter = new BQFParameters { lFilter = type, - fCenter = initialCutoff + fCenter = initialCutoff, + fBandwidth = 0, + fQ = 0.7f // This allows fCenter to go up to 22049hz (nyquist - 1hz) without overflowing and causing weird filter behaviour (see: https://www.un4seen.com/forum/?topic=19542.0) }; attachFilter(); - Cutoff.ValueChanged += updateFilter; Cutoff.Value = initialCutoff; } @@ -74,9 +68,7 @@ namespace osu.Game.Audio.Effects private void updateFilter(ValueChangedEvent cutoff) { - // This is another workaround for quirks in BASS' BiQuad filters. - // Because the cutoff can't be set above ~14khz (i.e. outside of human hearing range) without the aforementioned wrapping/reflecting quirk occuring, we instead - // remove the effect from the mixer when the cutoff is at maximum so that a LowPass filter isn't always attenuating high frequencies just by existing. + // Workaround for weird behaviour when rapidly setting fCenter of a low-pass filter to nyquist - 1hz. if (type == BQFType.LowPass) { if (cutoff.NewValue >= MaxCutoff) @@ -89,6 +81,19 @@ namespace osu.Game.Audio.Effects attachFilter(); } + // Workaround for weird behaviour when rapidly setting fCenter of a high-pass filter to 1hz. + if (type == BQFType.HighPass) + { + if (cutoff.NewValue <= 1) + { + detachFilter(); + return; + } + + if (cutoff.OldValue <= 1 && cutoff.NewValue > 1) + attachFilter(); + } + var filterIndex = mixer.Effects.IndexOf(filter); if (filterIndex < 0) return; From 266e62794e4c1f892f05e4ddbdd2b8a0b3eba289 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Wed, 6 Oct 2021 11:50:14 +0900 Subject: [PATCH 2296/2442] Tweak cutoff effect for new Q value --- osu.Game/Overlays/DialogOverlay.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index 6016d16d29..bd20b74970 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -82,13 +82,15 @@ namespace osu.Game.Overlays { base.PopIn(); this.FadeIn(PopupDialog.ENTER_DURATION, Easing.OutQuint); - lpFilter.CutoffTo(2000).Then().CutoffTo(150, 100, Easing.OutCubic); + lpFilter.CutoffTo(300, 100, Easing.OutCubic); } protected override void PopOut() { base.PopOut(); + lpFilter.CutoffTo(lpFilter.MaxCutoff, 100, Easing.InCubic); + if (CurrentDialog?.State.Value == Visibility.Visible) { CurrentDialog.Hide(); @@ -96,7 +98,6 @@ namespace osu.Game.Overlays } this.FadeOut(PopupDialog.EXIT_DURATION, Easing.InSine); - lpFilter.CutoffTo(2000, 100, Easing.InCubic).Then().CutoffTo(lpFilter.MaxCutoff); } public override bool OnPressed(KeyBindingPressEvent e) From ffbb7a9b1a45692305c984a1a7fc42d37db2993d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Oct 2021 12:22:32 +0900 Subject: [PATCH 2297/2442] Remove incorrect csproj change Co-authored-by: Dan Balasescu --- osu.Game/osu.Game.csproj | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 05c587bcc0..4877ddf725 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -43,7 +43,4 @@ - - - From 6e797ddcac59f939f7fed68a9f0a91c1bf9a5143 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Oct 2021 12:41:17 +0900 Subject: [PATCH 2298/2442] Add test coverage of creating, saving and loading a new beatmap --- .../Visual/Editing/TestSceneEditorSaving.cs | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs new file mode 100644 index 0000000000..2258a209e2 --- /dev/null +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs @@ -0,0 +1,62 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Input; +using osu.Framework.Testing; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Edit; +using osu.Game.Screens.Edit; +using osu.Game.Screens.Menu; +using osu.Game.Screens.Select; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Editing +{ + public class TestSceneEditorSaving : OsuGameTestScene + { + private Editor editor => Game.ChildrenOfType().FirstOrDefault(); + + private EditorBeatmap editorBeatmap => (EditorBeatmap)editor.Dependencies.Get(typeof(EditorBeatmap)); + + /// + /// Tests the general expected flow of creating a new beatmap, saving it, then loading it back from song select. + /// + [Test] + public void TestNewBeatmapSaveThenLoad() + { + AddStep("set default beatmap", () => Game.Beatmap.SetDefault()); + + PushAndConfirm(() => new EditorLoader()); + + AddUntilStep("wait for editor load", () => editor != null); + + AddStep("Add timing point", () => editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint())); + + AddStep("Enter compose mode", () => InputManager.Key(Key.F1)); + AddUntilStep("Wait for compose mode load", () => editor.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); + + AddStep("Change to placement mode", () => InputManager.Key(Key.Number2)); + AddStep("Move to playfield", () => InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.Centre)); + AddStep("Place single hitcircle", () => InputManager.Click(MouseButton.Left)); + + AddStep("Save and exit", () => + { + InputManager.Keys(PlatformAction.Save); + InputManager.Key(Key.Escape); + }); + + AddUntilStep("Wait for main menu", () => Game.ScreenStack.CurrentScreen is MainMenu); + + PushAndConfirm(() => new PlaySongSelect()); + + AddUntilStep("Wait for beatmap selected", () => !Game.Beatmap.IsDefault); + AddStep("Open options", () => InputManager.Key(Key.F3)); + AddStep("Enter editor", () => InputManager.Key(Key.Number5)); + + AddUntilStep("Wait for editor load", () => editor != null); + AddAssert("Beatmap contains single hitcircle", () => editorBeatmap.HitObjects.Count == 1); + } + } +} From 007b33cd88d465253613f6d74d05c93c8a6a0882 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Oct 2021 12:05:11 +0900 Subject: [PATCH 2299/2442] Add missing methods to interfaces --- osu.Game/Beatmaps/BeatmapModelManager.cs | 4 ++-- osu.Game/Beatmaps/IBeatmapModelManager.cs | 20 ++++++++++++++++++++ osu.Game/Beatmaps/IWorkingBeatmapCache.cs | 12 ++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 osu.Game/Beatmaps/IBeatmapModelManager.cs diff --git a/osu.Game/Beatmaps/BeatmapModelManager.cs b/osu.Game/Beatmaps/BeatmapModelManager.cs index 250d6653d5..787559899a 100644 --- a/osu.Game/Beatmaps/BeatmapModelManager.cs +++ b/osu.Game/Beatmaps/BeatmapModelManager.cs @@ -32,7 +32,7 @@ namespace osu.Game.Beatmaps /// Handles ef-core storage of beatmaps. /// [ExcludeFromDynamicCompile] - public class BeatmapModelManager : ArchiveModelManager + public class BeatmapModelManager : ArchiveModelManager, IBeatmapModelManager { /// /// Fired when a single difficulty has been hidden. @@ -54,7 +54,7 @@ namespace osu.Game.Beatmaps /// /// The game working beatmap cache, used to invalidate entries on changes. /// - public WorkingBeatmapCache WorkingBeatmapCache { private get; set; } + public IWorkingBeatmapCache WorkingBeatmapCache { private get; set; } private readonly Bindable> beatmapRestored = new Bindable>(); diff --git a/osu.Game/Beatmaps/IBeatmapModelManager.cs b/osu.Game/Beatmaps/IBeatmapModelManager.cs new file mode 100644 index 0000000000..8c243c2b77 --- /dev/null +++ b/osu.Game/Beatmaps/IBeatmapModelManager.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Database; + +namespace osu.Game.Beatmaps +{ + public interface IBeatmapModelManager : IModelManager + { + /// + /// Provide an online lookup queue component to handle populating online beatmap metadata. + /// + BeatmapOnlineLookupQueue OnlineLookupQueue { set; } + + /// + /// Provide a working beatmap cache, used to invalidate entries on changes. + /// + IWorkingBeatmapCache WorkingBeatmapCache { set; } + } +} diff --git a/osu.Game/Beatmaps/IWorkingBeatmapCache.cs b/osu.Game/Beatmaps/IWorkingBeatmapCache.cs index 881e734292..3eb33f10d6 100644 --- a/osu.Game/Beatmaps/IWorkingBeatmapCache.cs +++ b/osu.Game/Beatmaps/IWorkingBeatmapCache.cs @@ -11,5 +11,17 @@ namespace osu.Game.Beatmaps /// The beatmap to lookup. /// A instance correlating to the provided . WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo); + + /// + /// Invalidate a cache entry if it exists. + /// + /// The beatmap set info to invalidate any cached entries for. + void Invalidate(BeatmapSetInfo beatmapSetInfo); + + /// + /// Invalidate a cache entry if it exists. + /// + /// The beatmap info to invalidate any cached entries for. + void Invalidate(BeatmapInfo beatmapInfo); } } From 8ffaa491e7303289b35de86eb1fb85e3a9097e81 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Oct 2021 12:05:30 +0900 Subject: [PATCH 2300/2442] Fix `BeatmapModelManager` not receiving `WorkingBeatmapCache` --- osu.Game/Beatmaps/BeatmapManager.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 91d5b16204..240db22c00 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -45,6 +45,7 @@ namespace osu.Game.Beatmaps workingBeatmapCache = CreateWorkingBeatmapCache(audioManager, resources, new FileStore(contextFactory, storage).Store, defaultBeatmap, host); workingBeatmapCache.BeatmapManager = beatmapModelManager; + beatmapModelManager.WorkingBeatmapCache = workingBeatmapCache; if (performOnlineLookups) { @@ -305,6 +306,9 @@ namespace osu.Game.Beatmaps public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo importedBeatmap) => workingBeatmapCache.GetWorkingBeatmap(importedBeatmap); + void IWorkingBeatmapCache.Invalidate(BeatmapSetInfo beatmapSetInfo) => workingBeatmapCache.Invalidate(beatmapSetInfo); + void IWorkingBeatmapCache.Invalidate(BeatmapInfo beatmapInfo) => workingBeatmapCache.Invalidate(beatmapInfo); + #endregion #region Implementation of IModelFileManager From 90fdaf18c027ac08b3cd15e636105709dd6903e2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Oct 2021 12:40:25 +0900 Subject: [PATCH 2301/2442] Fix `PushAndConfirm` potentially failing if new screen quickly pushes a child screen --- osu.Game/Tests/Visual/OsuGameTestScene.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/osu.Game/Tests/Visual/OsuGameTestScene.cs b/osu.Game/Tests/Visual/OsuGameTestScene.cs index 881c4bab02..c025cf85c7 100644 --- a/osu.Game/Tests/Visual/OsuGameTestScene.cs +++ b/osu.Game/Tests/Visual/OsuGameTestScene.cs @@ -83,8 +83,17 @@ namespace osu.Game.Tests.Visual protected void PushAndConfirm(Func newScreen) { Screen screen = null; - AddStep("Push new screen", () => Game.ScreenStack.Push(screen = newScreen())); - AddUntilStep("Wait for new screen", () => Game.ScreenStack.CurrentScreen == screen && screen.IsLoaded); + IScreen previousScreen = null; + + AddStep("Push new screen", () => + { + previousScreen = Game.ScreenStack.CurrentScreen; + Game.ScreenStack.Push(screen = newScreen()); + }); + + AddUntilStep("Wait for new screen", () => screen.IsLoaded + && Game.ScreenStack.CurrentScreen != previousScreen + && previousScreen.GetChildScreen() == screen); } protected void ConfirmAtMainMenu() => AddUntilStep("Wait for main menu", () => Game.ScreenStack.CurrentScreen is MainMenu menu && menu.IsLoaded); From d9849bcf4995b69bf493d9dcaab88950c40f3a4f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Oct 2021 13:14:39 +0900 Subject: [PATCH 2302/2442] Fix dragging on an editor file selection text box causing repeated popover display Local fix and no tests as this is a pretty weird usage of `TextBox`. We'll probably want to change it to not use a textbox eventually. Closes #14969. --- osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs b/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs index fd43349793..f833bc49f7 100644 --- a/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs +++ b/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs @@ -89,6 +89,13 @@ namespace osu.Game.Screens.Edit.Setup { public Action OnFocused; + protected override bool OnDragStart(DragStartEvent e) + { + // This text box is intended to be "read only" without actually specifying that. + // As such we don't want to allow the user to select its content with a drag. + return false; + } + protected override void OnFocus(FocusEvent e) { OnFocused?.Invoke(); From b339c149d86b076a34a5af3701eb751a6689034b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 2 Oct 2021 12:34:29 +0900 Subject: [PATCH 2303/2442] Copy `BaseDifficulty` to `Beatmap` and move all write operations across --- .../CatchSelectionBlueprintTestScene.cs | 2 +- ...TestSceneBananaShowerPlacementBlueprint.cs | 3 +- .../TestSceneJuiceStreamPlacementBlueprint.cs | 11 +++-- .../TestSceneJuiceStreamSelectionBlueprint.cs | 2 +- .../Beatmaps/CatchBeatmapProcessor.cs | 2 +- .../Difficulty/CatchDifficultyCalculator.cs | 6 +-- .../Edit/DrawableCatchEditorRuleset.cs | 2 +- .../UI/DrawableCatchRuleset.cs | 4 +- .../Beatmaps/ManiaBeatmapConverter.cs | 6 +-- .../Legacy/DistanceObjectPatternGenerator.cs | 2 +- .../Patterns/Legacy/PatternGenerator.cs | 2 +- .../Difficulty/ManiaDifficultyCalculator.cs | 2 +- .../UI/DrawableManiaRuleset.cs | 2 +- .../Editor/TestSceneOsuDistanceSnapGrid.cs | 2 +- .../TestSceneGameplayCursor.cs | 4 +- .../TestSceneMissHitWindowJudgements.cs | 4 +- .../TestSceneObjectOrderedHitPolicy.cs | 2 +- .../Difficulty/OsuDifficultyCalculator.cs | 4 +- .../Edit/Checks/CheckTooShortSpinners.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 2 +- .../Replays/OsuAutoGenerator.cs | 2 +- .../Statistics/AccuracyHeatmap.cs | 2 +- .../UI/Cursor/OsuCursorContainer.cs | 2 +- .../Beatmaps/TaikoBeatmapConverter.cs | 14 +++--- .../Difficulty/TaikoDifficultyCalculator.cs | 2 +- .../Scoring/TaikoHealthProcessor.cs | 4 +- .../Formats/LegacyBeatmapDecoderTest.cs | 2 +- .../Beatmaps/Formats/OsuJsonDecoderTest.cs | 4 +- ...tSceneHitObjectComposerDistanceSnapping.cs | 12 ++--- .../TestSceneDrainingHealthProcessor.cs | 4 +- .../TestSceneDrawableScrollingRuleset.cs | 4 +- .../Gameplay/TestSceneGameplayRewinding.cs | 2 +- .../TestScenePlaylistsRoomSubScreen.cs | 4 +- osu.Game/Beatmaps/Beatmap.cs | 49 +++++++++++++++---- .../Beatmaps/Formats/JsonBeatmapDecoder.cs | 2 +- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 6 +-- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 16 +++--- osu.Game/Beatmaps/IBeatmap.cs | 5 ++ osu.Game/Beatmaps/WorkingBeatmap.cs | 6 +-- .../Difficulty/DifficultyCalculator.cs | 7 +++ osu.Game/Rulesets/Edit/HitObjectComposer.cs | 2 +- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 2 +- osu.Game/Rulesets/Mods/DifficultyBindable.cs | 2 +- .../Scoring/DrainingHealthProcessor.cs | 2 +- .../UI/Scrolling/DrawableScrollingRuleset.cs | 6 +-- osu.Game/Screens/Edit/EditorBeatmap.cs | 8 ++- .../Screens/Edit/Setup/DifficultySection.cs | 16 +++--- .../Tests/Beatmaps/BeatmapConversionTest.cs | 2 + .../Visual/PlacementBlueprintTestScene.cs | 16 +++--- 49 files changed, 163 insertions(+), 108 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs b/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs index a458771550..7d806b119e 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor protected CatchSelectionBlueprintTestScene() { EditorBeatmap = new EditorBeatmap(new CatchBeatmap()); - EditorBeatmap.BeatmapInfo.BaseDifficulty.CircleSize = 0; + EditorBeatmap.Difficulty.CircleSize = 0; EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 100 diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneBananaShowerPlacementBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneBananaShowerPlacementBlueprint.cs index e3811b7669..cca3701a60 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneBananaShowerPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneBananaShowerPlacementBlueprint.cs @@ -5,6 +5,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Testing; using osu.Framework.Utils; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Catch.Edit.Blueprints; using osu.Game.Rulesets.Catch.Edit.Blueprints.Components; @@ -26,7 +27,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor protected override void AddHitObject(DrawableHitObject hitObject) { // Create nested bananas (but positions are not randomized because beatmap processing is not done). - hitObject.HitObject.ApplyDefaults(new ControlPointInfo(), Beatmap.Value.BeatmapInfo.BaseDifficulty); + hitObject.HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); base.AddHitObject(hitObject); } diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs index cd1fa31b61..981efc9a13 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs @@ -4,9 +4,9 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Utils; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Edit.Blueprints; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawables; @@ -23,11 +23,12 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor private JuiceStream lastObject => LastObject?.HitObject as JuiceStream; - [BackgroundDependencyLoader] - private void load() + protected override IBeatmap GetPlayableBeatmap() { - Beatmap.Value.BeatmapInfo.BaseDifficulty.SliderTickRate = 5; - Beatmap.Value.BeatmapInfo.BaseDifficulty.SliderMultiplier = velocity * 10; + var playable = base.GetPlayableBeatmap(); + playable.Difficulty.SliderTickRate = 5; + playable.Difficulty.SliderMultiplier = velocity * 10; + return playable; } [Test] diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs index 5e73a89069..155d033dd0 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs @@ -223,7 +223,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor X = x, Path = sliderPath, }; - EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = velocity; + EditorBeatmap.Difficulty.SliderMultiplier = velocity; EditorBeatmap.Add(hitObject); EditorBeatmap.Update(hitObject); Assert.That(hitObject.Velocity, Is.EqualTo(velocity)); diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index a891ec6c0a..87cc2c45e8 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -211,7 +211,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps palpableObjects.Sort((h1, h2) => h1.StartTime.CompareTo(h2.StartTime)); - double halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.BeatmapInfo.BaseDifficulty) / 2; + double halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.Difficulty) / 2; // Todo: This is wrong. osu!stable calculated hyperdashes using the full catcher size, excluding the margins. // This should theoretically cause impossible scenarios, but practically, likely due to the size of the playfield, it doesn't seem possible. diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 5b1f613f8d..03a76f10ef 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty return new CatchDifficultyAttributes { Mods = mods, Skills = skills }; // this is the same as osu!, so there's potential to share the implementation... maybe - double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; + double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate; return new CatchDifficultyAttributes { @@ -69,10 +69,10 @@ namespace osu.Game.Rulesets.Catch.Difficulty protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) { - halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.BeatmapInfo.BaseDifficulty) * 0.5f; + halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.Difficulty) * 0.5f; // For circle sizes above 5.5, reduce the catcher width further to simulate imperfect gameplay. - halfCatcherWidth *= 1 - (Math.Max(0, beatmap.BeatmapInfo.BaseDifficulty.CircleSize - 5.5f) * 0.0625f); + halfCatcherWidth *= 1 - (Math.Max(0, beatmap.Difficulty.CircleSize - 5.5f) * 0.0625f); return new Skill[] { diff --git a/osu.Game.Rulesets.Catch/Edit/DrawableCatchEditorRuleset.cs b/osu.Game.Rulesets.Catch/Edit/DrawableCatchEditorRuleset.cs index 0344709d45..9a7528d90c 100644 --- a/osu.Game.Rulesets.Catch/Edit/DrawableCatchEditorRuleset.cs +++ b/osu.Game.Rulesets.Catch/Edit/DrawableCatchEditorRuleset.cs @@ -16,6 +16,6 @@ namespace osu.Game.Rulesets.Catch.Edit { } - protected override Playfield CreatePlayfield() => new CatchEditorPlayfield(Beatmap.BeatmapInfo.BaseDifficulty); + protected override Playfield CreatePlayfield() => new CatchEditorPlayfield(Beatmap.Difficulty); } } diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs index ba6e9224c9..a8ec9f1d2f 100644 --- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs @@ -27,14 +27,14 @@ namespace osu.Game.Rulesets.Catch.UI : base(ruleset, beatmap, mods) { Direction.Value = ScrollingDirection.Down; - TimeRange.Value = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450); + TimeRange.Value = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450); } protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay); protected override ReplayRecorder CreateReplayRecorder(Score score) => new CatchReplayRecorder(score, (CatchPlayfield)Playfield); - protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty); + protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.Difficulty); public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new CatchPlayfieldAdjustmentContainer(); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 01e44ef9e4..ebfbaccd31 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -42,8 +42,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps { IsForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo); - var roundedCircleSize = Math.Round(beatmap.BeatmapInfo.BaseDifficulty.CircleSize); - var roundedOverallDifficulty = Math.Round(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty); + var roundedCircleSize = Math.Round(beatmap.Difficulty.CircleSize); + var roundedOverallDifficulty = Math.Round(beatmap.Difficulty.OverallDifficulty); if (IsForCurrentRuleset) { @@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps protected override Beatmap ConvertBeatmap(IBeatmap original, CancellationToken cancellationToken) { - IBeatmapDifficultyInfo difficulty = original.BeatmapInfo.BaseDifficulty; + IBeatmapDifficultyInfo difficulty = original.Difficulty; int seed = (int)MathF.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)MathF.Round(difficulty.ApproachRate); Random = new FastRandom(seed); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 26e5d381e2..380efff69f 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy StartTime = (int)Math.Round(hitObject.StartTime); // This matches stable's calculation. - EndTime = (int)Math.Floor(StartTime + distanceData.Distance * beatLength * SpanCount * 0.01 / beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier); + EndTime = (int)Math.Floor(StartTime + distanceData.Distance * beatLength * SpanCount * 0.01 / beatmap.Difficulty.SliderMultiplier); SegmentDuration = (EndTime - StartTime) / SpanCount; } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs index d65e78bb49..eaf0ea0f2b 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs @@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy if (drainTime == 0) drainTime = 10000; - IBeatmapDifficultyInfo difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty; + IBeatmapDifficultyInfo difficulty = OriginalBeatmap.Difficulty; conversionDifficulty = ((difficulty.DrainRate + Math.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + (double)OriginalBeatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15; conversionDifficulty = Math.Min(conversionDifficulty.Value, 12); diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index fc29eadedc..9140e8afce 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty return new ManiaDifficultyAttributes { Mods = mods, Skills = skills }; HitWindows hitWindows = new ManiaHitWindows(); - hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty); + hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty); return new ManiaDifficultyAttributes { diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index 614a7b00c7..3b7da8d9ba 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Mania.UI { // Mania doesn't care about global velocity p.Velocity = 1; - p.BaseBeatLength *= Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier; + p.BaseBeatLength *= Beatmap.Difficulty.SliderMultiplier; // For non-mania beatmap, speed changes should only happen through timing points if (!isForCurrentRuleset) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs index 9af2a99470..851be2b2f2 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor [SetUp] public void Setup() => Schedule(() => { - editorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 1; + editorBeatmap.Difficulty.SliderMultiplier = 1; editorBeatmap.ControlPointInfo.Clear(); editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = beat_length }); diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs index 41d9bf7132..f09aad8b49 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Tests AddSliderStep("circle size", 0f, 10f, 0f, val => { config.SetValue(OsuSetting.AutoCursorSize, true); - gameplayState.Beatmap.BeatmapInfo.BaseDifficulty.CircleSize = val; + gameplayState.Beatmap.Difficulty.CircleSize = val; Scheduler.AddOnce(() => loadContent(false)); }); @@ -75,7 +75,7 @@ namespace osu.Game.Rulesets.Osu.Tests public void TestSizing(int circleSize, float userScale) { AddStep($"set user scale to {userScale}", () => config.SetValue(OsuSetting.GameplayCursorSize, userScale)); - AddStep($"adjust cs to {circleSize}", () => gameplayState.Beatmap.BeatmapInfo.BaseDifficulty.CircleSize = circleSize); + AddStep($"adjust cs to {circleSize}", () => gameplayState.Beatmap.Difficulty.CircleSize = circleSize); AddStep("turn on autosizing", () => config.SetValue(OsuSetting.AutoCursorSize, true)); AddStep("load content", () => loadContent()); diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneMissHitWindowJudgements.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneMissHitWindowJudgements.cs index af67ab5839..a5629119b6 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneMissHitWindowJudgements.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneMissHitWindowJudgements.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Tests }; var hitWindows = new OsuHitWindows(); - hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty); + hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty); CreateModTest(new ModTestData { @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Osu.Tests }; var hitWindows = new OsuHitWindows(); - hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty); + hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty); CreateModTest(new ModTestData { diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneObjectOrderedHitPolicy.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneObjectOrderedHitPolicy.cs index cfce80a2b2..ececfb0586 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneObjectOrderedHitPolicy.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneObjectOrderedHitPolicy.cs @@ -400,9 +400,9 @@ namespace osu.Game.Rulesets.Osu.Tests Beatmap.Value = CreateWorkingBeatmap(new Beatmap { HitObjects = hitObjects, + Difficulty = new BeatmapDifficulty { SliderTickRate = 3 }, BeatmapInfo = { - BaseDifficulty = new BeatmapDifficulty { SliderTickRate = 3 }, Ruleset = new OsuRuleset().RulesetInfo }, }); diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index a8f10f44dc..17a611817c 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty double starRating = basePerformance > 0.00001 ? Math.Cbrt(1.12) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0; - double preempt = (int)IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; + double preempt = (int)IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate; int maxCombo = beatmap.HitObjects.Count; // Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above) @@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) { HitWindows hitWindows = new OsuHitWindows(); - hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty); + hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty); // Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future hitWindowGreat = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate; diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckTooShortSpinners.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckTooShortSpinners.cs index 0d0c3d9e69..f0aade1b7f 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckTooShortSpinners.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckTooShortSpinners.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks public IEnumerable Run(BeatmapVerifierContext context) { - double od = context.Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty; + double od = context.Beatmap.Difficulty.OverallDifficulty; // These are meant to reflect the duration necessary for auto to score at least 1000 points on the spinner. // It's difficult to eliminate warnings here, as auto achieving 1000 points depends on the approach angle on some spinners. diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index b0c655b106..bf70a63ab5 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -175,7 +175,7 @@ namespace osu.Game.Rulesets.Osu.Mods .Select(beat => { var newCircle = new HitCircle(); - newCircle.ApplyDefaults(controlPointInfo, osuBeatmap.BeatmapInfo.BaseDifficulty); + newCircle.ApplyDefaults(controlPointInfo, osuBeatmap.Difficulty); newCircle.StartTime = beat; return (OsuHitObject)newCircle; }).ToList(); diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index b88bf9108b..e231550e3e 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Replays : base(beatmap, mods) { defaultHitWindows = new OsuHitWindows(); - defaultHitWindows.SetDifficulty(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty); + defaultHitWindows.SetDifficulty(Beatmap.Difficulty.OverallDifficulty); } #endregion diff --git a/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs b/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs index cb769c31b8..24a660e69e 100644 --- a/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs +++ b/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs @@ -179,7 +179,7 @@ namespace osu.Game.Rulesets.Osu.Statistics return; // Todo: This should probably not be done like this. - float radius = OsuHitObject.OBJECT_RADIUS * (1.0f - 0.7f * (playableBeatmap.BeatmapInfo.BaseDifficulty.CircleSize - 5) / 5) / 2; + float radius = OsuHitObject.OBJECT_RADIUS * (1.0f - 0.7f * (playableBeatmap.Difficulty.CircleSize - 5) / 5) / 2; foreach (var e in score.HitEvents.Where(e => e.HitObject is HitCircle && !(e.HitObject is SliderTailCircle))) { diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs index cfe83d0106..d1d9ee9f4d 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs @@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor if (autoCursorScale.Value && state != null) { // if we have a beatmap available, let's get its circle size to figure out an automatic cursor scale modifier. - scale *= GetScaleForCircleSize(state.Beatmap.BeatmapInfo.BaseDifficulty.CircleSize); + scale *= GetScaleForCircleSize(state.Beatmap.Difficulty.CircleSize); } cursorScale.Value = scale; diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 3b5b972c01..643a656199 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -46,11 +46,11 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps protected override Beatmap ConvertBeatmap(IBeatmap original, CancellationToken cancellationToken) { - if (!(original.BeatmapInfo.BaseDifficulty is TaikoMutliplierAppliedDifficulty)) + if (!(original.Difficulty is TaikoMutliplierAppliedDifficulty)) { // Rewrite the beatmap info to add the slider velocity multiplier original.BeatmapInfo = original.BeatmapInfo.Clone(); - original.BeatmapInfo.BaseDifficulty = new TaikoMutliplierAppliedDifficulty(original.BeatmapInfo.BaseDifficulty); + original.Difficulty = new TaikoMutliplierAppliedDifficulty(original.Difficulty); } Beatmap converted = base.ConvertBeatmap(original, cancellationToken); @@ -108,7 +108,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps StartTime = obj.StartTime, Samples = obj.Samples, Duration = taikoDuration, - TickRate = beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate == 3 ? 3 : 4 + TickRate = beatmap.Difficulty.SliderTickRate == 3 ? 3 : 4 }; } @@ -117,7 +117,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps case IHasDuration endTimeData: { - double hitMultiplier = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier; + double hitMultiplier = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier; yield return new Swell { @@ -164,10 +164,10 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps else beatLength = timingPoint.BeatLength / difficultyPoint.SpeedMultiplier; - double sliderScoringPointDistance = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate; + double sliderScoringPointDistance = osu_base_scoring_distance * beatmap.Difficulty.SliderMultiplier / beatmap.Difficulty.SliderTickRate; // The velocity and duration of the taiko hit object - calculated as the velocity of a drum roll. - double taikoVelocity = sliderScoringPointDistance * beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate; + double taikoVelocity = sliderScoringPointDistance * beatmap.Difficulty.SliderTickRate; taikoDuration = (int)(distance / taikoVelocity * beatLength); if (isForCurrentRuleset) @@ -183,7 +183,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps beatLength = timingPoint.BeatLength; // If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat - tickSpacing = Math.Min(beatLength / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate, (double)taikoDuration / spans); + tickSpacing = Math.Min(beatLength / beatmap.Difficulty.SliderTickRate, (double)taikoDuration / spans); return tickSpacing > 0 && distance / osuVelocity * 1000 < 2 * beatLength; diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 18d06c069f..e755bb2325 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -85,7 +85,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty starRating = rescale(starRating); HitWindows hitWindows = new TaikoHitWindows(); - hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty); + hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty); return new TaikoDifficultyAttributes { diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs index 94cd411d7b..0d6ce44255 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs @@ -40,8 +40,8 @@ namespace osu.Game.Rulesets.Taiko.Scoring { base.ApplyBeatmap(beatmap); - hpMultiplier = 1 / (object_count_factor * Math.Max(1, beatmap.HitObjects.OfType().Count()) * IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98)); - hpMissMultiplier = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120); + hpMultiplier = 1 / (object_count_factor * Math.Max(1, beatmap.HitObjects.OfType().Count()) * IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 0.5, 0.75, 0.98)); + hpMissMultiplier = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 0.0018, 0.0075, 0.0120); } protected override double GetHealthIncreaseFor(JudgementResult result) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index a4bf8c92e3..b5e1fa204f 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -129,7 +129,7 @@ namespace osu.Game.Tests.Beatmaps.Formats using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new LineBufferedReader(resStream)) { - var difficulty = decoder.Decode(stream).BeatmapInfo.BaseDifficulty; + var difficulty = decoder.Decode(stream).Difficulty; Assert.AreEqual(6.5f, difficulty.DrainRate); Assert.AreEqual(4, difficulty.CircleSize); diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs index 1fc3abef9a..9ec2f37569 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs @@ -84,7 +84,7 @@ namespace osu.Game.Tests.Beatmaps.Formats public void TestDecodeDifficulty() { var beatmap = decodeAsJson(normal); - var difficulty = beatmap.BeatmapInfo.BaseDifficulty; + var difficulty = beatmap.Difficulty; Assert.AreEqual(6.5f, difficulty.DrainRate); Assert.AreEqual(4, difficulty.CircleSize); Assert.AreEqual(8, difficulty.OverallDifficulty); @@ -102,7 +102,7 @@ namespace osu.Game.Tests.Beatmaps.Formats processor.PreProcess(); foreach (var o in converted.HitObjects) - o.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty); + o.ApplyDefaults(converted.ControlPointInfo, converted.Difficulty); processor.PostProcess(); var beatmap = converted.Serialize().Deserialize(); diff --git a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs index bd34eaff63..a40a6dac4c 100644 --- a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs +++ b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs @@ -53,7 +53,7 @@ namespace osu.Game.Tests.Editing BeatDivisor.Value = 1; - composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 1; + composer.EditorBeatmap.Difficulty.SliderMultiplier = 1; composer.EditorBeatmap.ControlPointInfo.Clear(); composer.EditorBeatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SpeedMultiplier = 1 }); @@ -64,7 +64,7 @@ namespace osu.Game.Tests.Editing [TestCase(2)] public void TestSliderMultiplier(float multiplier) { - AddStep($"set multiplier = {multiplier}", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = multiplier); + AddStep($"set multiplier = {multiplier}", () => composer.EditorBeatmap.Difficulty.SliderMultiplier = multiplier); assertSnapDistance(100 * multiplier); } @@ -97,7 +97,7 @@ namespace osu.Game.Tests.Editing assertDurationToDistance(500, 50); assertDurationToDistance(1000, 100); - AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2); + AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.Difficulty.SliderMultiplier = 2); assertDurationToDistance(500, 100); assertDurationToDistance(1000, 200); @@ -118,7 +118,7 @@ namespace osu.Game.Tests.Editing assertDistanceToDuration(50, 500); assertDistanceToDuration(100, 1000); - AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2); + AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.Difficulty.SliderMultiplier = 2); assertDistanceToDuration(100, 500); assertDistanceToDuration(200, 1000); @@ -143,7 +143,7 @@ namespace osu.Game.Tests.Editing assertSnappedDuration(200, 2000); assertSnappedDuration(250, 3000); - AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2); + AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.Difficulty.SliderMultiplier = 2); assertSnappedDuration(0, 0); assertSnappedDuration(50, 0); @@ -175,7 +175,7 @@ namespace osu.Game.Tests.Editing assertSnappedDistance(200, 200); assertSnappedDistance(250, 200); - AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2); + AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.Difficulty.SliderMultiplier = 2); assertSnappedDistance(50, 0); assertSnappedDistance(100, 0); diff --git a/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs b/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs index 7264083338..296c5cef76 100644 --- a/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs +++ b/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs @@ -165,7 +165,7 @@ namespace osu.Game.Tests.Gameplay { var beatmap = new Beatmap { - BeatmapInfo = { BaseDifficulty = { DrainRate = 10 } }, + Difficulty = { DrainRate = 10 } }; beatmap.HitObjects.Add(new JudgeableHitObject { StartTime = 0 }); @@ -200,7 +200,7 @@ namespace osu.Game.Tests.Gameplay { var beatmap = new Beatmap { - BeatmapInfo = { BaseDifficulty = { DrainRate = 10 } }, + Difficulty = { DrainRate = 10 } }; for (double time = startTime; time <= endTime; time += 100) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs index 75a5eec6f7..3f10d7892d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs @@ -182,7 +182,7 @@ namespace osu.Game.Tests.Visual.Gameplay { var beatmap = createBeatmap(); beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = time_range }); - beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2; + beatmap.Difficulty.SliderMultiplier = 2; createTest(beatmap, d => d.RelativeScaleBeatLengthsOverride = true); AddStep("adjust time range", () => drawableRuleset.TimeRange.Value = 5000); @@ -196,7 +196,7 @@ namespace osu.Game.Tests.Visual.Gameplay { var beatmap = createBeatmap(); beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = time_range }); - beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2; + beatmap.Difficulty.SliderMultiplier = 2; createTest(beatmap); AddStep("adjust time range", () => drawableRuleset.TimeRange.Value = 2000); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs index 73c6970482..814b41cdbc 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs @@ -55,7 +55,7 @@ namespace osu.Game.Tests.Visual.Gameplay { var beatmap = new Beatmap { - BeatmapInfo = { BaseDifficulty = { ApproachRate = 9 } }, + Difficulty = { ApproachRate = 9 }, }; for (int i = 0; i < 15; i++) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs index d8ec89a94e..deb9a22184 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs @@ -137,11 +137,11 @@ namespace osu.Game.Tests.Visual.Playlists InputManager.Click(MouseButton.Left); }); - AddAssert("match has altered beatmap", () => match.Beatmap.Value.Beatmap.BeatmapInfo.BaseDifficulty.CircleSize == 1); + AddAssert("match has altered beatmap", () => match.Beatmap.Value.Beatmap.Difficulty.CircleSize == 1); AddStep("re-import original beatmap", () => manager.Import(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait()); - AddAssert("match has original beatmap", () => match.Beatmap.Value.Beatmap.BeatmapInfo.BaseDifficulty.CircleSize != 1); + AddAssert("match has original beatmap", () => match.Beatmap.Value.Beatmap.Difficulty.CircleSize != 1); } private class TestPlaylistsRoomSubScreen : PlaylistsRoomSubScreen diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index e5b6a4bc44..f2a60c4711 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -18,17 +18,48 @@ namespace osu.Game.Beatmaps public class Beatmap : IBeatmap where T : HitObject { - public BeatmapInfo BeatmapInfo { get; set; } = new BeatmapInfo + private BeatmapDifficulty difficulty = new BeatmapDifficulty(); + + public BeatmapDifficulty Difficulty { - Metadata = new BeatmapMetadata + get => difficulty; + set { - Artist = @"Unknown", - Title = @"Unknown", - AuthorString = @"Unknown Creator", - }, - Version = @"Normal", - BaseDifficulty = new BeatmapDifficulty() - }; + difficulty = value; + + if (beatmapInfo != null) + beatmapInfo.BaseDifficulty = new BeatmapDifficulty(difficulty); + } + } + + private BeatmapInfo beatmapInfo; + + public BeatmapInfo BeatmapInfo + { + get => beatmapInfo; + set + { + beatmapInfo = value; + + if (beatmapInfo != null) + Difficulty = new BeatmapDifficulty(beatmapInfo.BaseDifficulty); + } + } + + public Beatmap() + { + beatmapInfo = new BeatmapInfo + { + Metadata = new BeatmapMetadata + { + Artist = @"Unknown", + Title = @"Unknown", + AuthorString = @"Unknown Creator", + }, + Version = @"Normal", + BaseDifficulty = Difficulty, + }; + } [JsonIgnore] public BeatmapMetadata Metadata => BeatmapInfo?.Metadata ?? BeatmapInfo?.BeatmapSet?.Metadata; diff --git a/osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs index 988968fa42..0d5c48f64d 100644 --- a/osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/JsonBeatmapDecoder.cs @@ -18,7 +18,7 @@ namespace osu.Game.Beatmaps.Formats stream.ReadToEnd().DeserializeInto(output); foreach (var hitObject in output.HitObjects) - hitObject.ApplyDefaults(output.ControlPointInfo, output.BeatmapInfo.BaseDifficulty); + hitObject.ApplyDefaults(output.ControlPointInfo, output.Difficulty); } } } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 4b5eaafa4a..f71b148008 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -67,7 +67,7 @@ namespace osu.Game.Beatmaps.Formats this.beatmap.HitObjects = this.beatmap.HitObjects.OrderBy(h => h.StartTime).ToList(); foreach (var hitObject in this.beatmap.HitObjects) - hitObject.ApplyDefaults(this.beatmap.ControlPointInfo, this.beatmap.BeatmapInfo.BaseDifficulty); + hitObject.ApplyDefaults(this.beatmap.ControlPointInfo, this.beatmap.Difficulty); } protected override bool ShouldSkipLine(string line) => base.ShouldSkipLine(line) || line.StartsWith(' ') || line.StartsWith('_'); @@ -276,7 +276,7 @@ namespace osu.Game.Beatmaps.Formats { var pair = SplitKeyVal(line); - var difficulty = beatmap.BeatmapInfo.BaseDifficulty; + var difficulty = beatmap.Difficulty; switch (pair.Key) { @@ -444,7 +444,7 @@ namespace osu.Game.Beatmaps.Formats if (obj != null) { - obj.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty); + obj.ApplyDefaults(beatmap.ControlPointInfo, beatmap.Difficulty); beatmap.HitObjects.Add(obj); } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index aef13b8872..74b3c178cd 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -141,17 +141,17 @@ namespace osu.Game.Beatmaps.Formats { writer.WriteLine("[Difficulty]"); - writer.WriteLine(FormattableString.Invariant($"HPDrainRate: {beatmap.BeatmapInfo.BaseDifficulty.DrainRate}")); - writer.WriteLine(FormattableString.Invariant($"CircleSize: {beatmap.BeatmapInfo.BaseDifficulty.CircleSize}")); - writer.WriteLine(FormattableString.Invariant($"OverallDifficulty: {beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty}")); - writer.WriteLine(FormattableString.Invariant($"ApproachRate: {beatmap.BeatmapInfo.BaseDifficulty.ApproachRate}")); + writer.WriteLine(FormattableString.Invariant($"HPDrainRate: {beatmap.Difficulty.DrainRate}")); + writer.WriteLine(FormattableString.Invariant($"CircleSize: {beatmap.Difficulty.CircleSize}")); + writer.WriteLine(FormattableString.Invariant($"OverallDifficulty: {beatmap.Difficulty.OverallDifficulty}")); + writer.WriteLine(FormattableString.Invariant($"ApproachRate: {beatmap.Difficulty.ApproachRate}")); // Taiko adjusts the slider multiplier (see: LEGACY_TAIKO_VELOCITY_MULTIPLIER) writer.WriteLine(beatmap.BeatmapInfo.RulesetID == 1 - ? FormattableString.Invariant($"SliderMultiplier: {beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier / LEGACY_TAIKO_VELOCITY_MULTIPLIER}") - : FormattableString.Invariant($"SliderMultiplier: {beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier}")); + ? FormattableString.Invariant($"SliderMultiplier: {beatmap.Difficulty.SliderMultiplier / LEGACY_TAIKO_VELOCITY_MULTIPLIER}") + : FormattableString.Invariant($"SliderMultiplier: {beatmap.Difficulty.SliderMultiplier}")); - writer.WriteLine(FormattableString.Invariant($"SliderTickRate: {beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate}")); + writer.WriteLine(FormattableString.Invariant($"SliderTickRate: {beatmap.Difficulty.SliderTickRate}")); } private void handleEvents(TextWriter writer) @@ -285,7 +285,7 @@ namespace osu.Game.Beatmaps.Formats break; case 3: - int totalColumns = (int)Math.Max(1, beatmap.BeatmapInfo.BaseDifficulty.CircleSize); + int totalColumns = (int)Math.Max(1, beatmap.Difficulty.CircleSize); position.X = (int)Math.Ceiling(((IHasXPosition)hitObject).X * (512f / totalColumns)); break; } diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs index f61dd269e1..3f598cd1e5 100644 --- a/osu.Game/Beatmaps/IBeatmap.cs +++ b/osu.Game/Beatmaps/IBeatmap.cs @@ -20,6 +20,11 @@ namespace osu.Game.Beatmaps /// BeatmapMetadata Metadata { get; } + /// + /// This beatmap's difficulty settings. + /// + public BeatmapDifficulty Difficulty { get; set; } + /// /// The control points in this beatmap. /// diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index c4c5c89f28..c7ccfe976a 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -120,14 +120,14 @@ namespace osu.Game.Beatmaps if (mods.Any(m => m is IApplicableToDifficulty)) { converted.BeatmapInfo = converted.BeatmapInfo.Clone(); - converted.BeatmapInfo.BaseDifficulty = converted.BeatmapInfo.BaseDifficulty.Clone(); + converted.Difficulty = converted.Difficulty.Clone(); foreach (var mod in mods.OfType()) { if (cancellationSource.IsCancellationRequested) throw new BeatmapLoadTimeoutException(BeatmapInfo); - mod.ApplyToDifficulty(converted.BeatmapInfo.BaseDifficulty); + mod.ApplyToDifficulty(converted.Difficulty); } } @@ -146,7 +146,7 @@ namespace osu.Game.Beatmaps if (cancellationSource.IsCancellationRequested) throw new BeatmapLoadTimeoutException(BeatmapInfo); - obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty, cancellationSource.Token); + obj.ApplyDefaults(converted.ControlPointInfo, converted.Difficulty, cancellationSource.Token); } } catch (OperationCanceledException) diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index a7c4790366..7e3e5d5417 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -257,6 +257,13 @@ namespace osu.Game.Rulesets.Difficulty } public BeatmapMetadata Metadata => baseBeatmap.Metadata; + + public BeatmapDifficulty Difficulty + { + get => baseBeatmap.Difficulty; + set => baseBeatmap.Difficulty = value; + } + public List Breaks => baseBeatmap.Breaks; public double TotalBreakTime => baseBeatmap.TotalBreakTime; public IEnumerable GetStatistics() => baseBeatmap.GetStatistics(); diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 8090fcbd32..b41e0442bc 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -392,7 +392,7 @@ namespace osu.Game.Rulesets.Edit public override float GetBeatSnapDistanceAt(double referenceTime) { DifficultyControlPoint difficultyPoint = EditorBeatmap.ControlPointInfo.DifficultyPointAt(referenceTime); - return (float)(100 * EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / BeatSnapProvider.BeatDivisor); + return (float)(100 * EditorBeatmap.Difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / BeatSnapProvider.BeatDivisor); } public override float DurationToDistance(double referenceTime, double duration) diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index b7529f39ca..0c0c5990d1 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Edit /// Invokes , /// refreshing and parameters for the . /// - protected void ApplyDefaultsToHitObject() => HitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty); + protected void ApplyDefaultsToHitObject() => HitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.Difficulty); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parent?.ReceivePositionalInputAt(screenSpacePos) ?? false; diff --git a/osu.Game/Rulesets/Mods/DifficultyBindable.cs b/osu.Game/Rulesets/Mods/DifficultyBindable.cs index e4304795f2..6cfae0b085 100644 --- a/osu.Game/Rulesets/Mods/DifficultyBindable.cs +++ b/osu.Game/Rulesets/Mods/DifficultyBindable.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mods /// /// A function that can extract the current value of this setting from a beatmap difficulty for display purposes. /// - public Func ReadCurrentFromDifficulty; + public Func ReadCurrentFromDifficulty; public float Precision { diff --git a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs index 85693abb93..dfeb6b4788 100644 --- a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs @@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Scoring .First() ))); - targetMinimumHealth = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, min_health_target, mid_health_target, max_health_target); + targetMinimumHealth = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, min_health_target, mid_health_target, max_health_target); // Add back a portion of the amount of HP to be drained, depending on the lenience requested. targetMinimumHealth += drainLenience * (1 - targetMinimumHealth); diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs index 7b30bb9574..041c5ebef5 100644 --- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs +++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs @@ -133,7 +133,7 @@ namespace osu.Game.Rulesets.UI.Scrolling maxDuration = duration; // The slider multiplier is post-multiplied to determine the final velocity, but for relative scale beat lengths // the multiplier should not affect the effective timing point (the longest in the beatmap), so it is factored out here - baseBeatLength = timingPoints[i].BeatLength / Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier; + baseBeatLength = timingPoints[i].BeatLength / Beatmap.Difficulty.SliderMultiplier; } } } @@ -155,7 +155,7 @@ namespace osu.Game.Rulesets.UI.Scrolling return new MultiplierControlPoint(c.Time) { - Velocity = Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier, + Velocity = Beatmap.Difficulty.SliderMultiplier, BaseBeatLength = baseBeatLength, TimingPoint = lastTimingPoint, DifficultyPoint = lastDifficultyPoint @@ -172,7 +172,7 @@ namespace osu.Game.Rulesets.UI.Scrolling ControlPoints.AddRange(timingChanges); if (ControlPoints.Count == 0) - ControlPoints.Add(new MultiplierControlPoint { Velocity = Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier }); + ControlPoints.Add(new MultiplierControlPoint { Velocity = Beatmap.Difficulty.SliderMultiplier }); } protected override void LoadComplete() diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index 3402bf653a..64eb6225fa 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -86,6 +86,12 @@ namespace osu.Game.Screens.Edit public BeatmapMetadata Metadata => PlayableBeatmap.Metadata; + public BeatmapDifficulty Difficulty + { + get => PlayableBeatmap.Difficulty; + set => PlayableBeatmap.Difficulty = value; + } + public ControlPointInfo ControlPointInfo { get => PlayableBeatmap.ControlPointInfo; @@ -286,7 +292,7 @@ namespace osu.Game.Screens.Edit /// public void Clear() => RemoveRange(HitObjects.ToArray()); - private void processHitObject(HitObject hitObject) => hitObject.ApplyDefaults(ControlPointInfo, BeatmapInfo.BaseDifficulty); + private void processHitObject(HitObject hitObject) => hitObject.ApplyDefaults(ControlPointInfo, PlayableBeatmap.Difficulty); private void trackStartTime(HitObject hitObject) { diff --git a/osu.Game/Screens/Edit/Setup/DifficultySection.cs b/osu.Game/Screens/Edit/Setup/DifficultySection.cs index a8800d524f..75c6a89a66 100644 --- a/osu.Game/Screens/Edit/Setup/DifficultySection.cs +++ b/osu.Game/Screens/Edit/Setup/DifficultySection.cs @@ -30,7 +30,7 @@ namespace osu.Game.Screens.Edit.Setup Label = "Object Size", FixedLabelWidth = LABEL_WIDTH, Description = "The size of all hit objects", - Current = new BindableFloat(Beatmap.BeatmapInfo.BaseDifficulty.CircleSize) + Current = new BindableFloat(Beatmap.Difficulty.CircleSize) { Default = BeatmapDifficulty.DEFAULT_DIFFICULTY, MinValue = 0, @@ -43,7 +43,7 @@ namespace osu.Game.Screens.Edit.Setup Label = "Health Drain", FixedLabelWidth = LABEL_WIDTH, Description = "The rate of passive health drain throughout playable time", - Current = new BindableFloat(Beatmap.BeatmapInfo.BaseDifficulty.DrainRate) + Current = new BindableFloat(Beatmap.Difficulty.DrainRate) { Default = BeatmapDifficulty.DEFAULT_DIFFICULTY, MinValue = 0, @@ -56,7 +56,7 @@ namespace osu.Game.Screens.Edit.Setup Label = "Approach Rate", FixedLabelWidth = LABEL_WIDTH, Description = "The speed at which objects are presented to the player", - Current = new BindableFloat(Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate) + Current = new BindableFloat(Beatmap.Difficulty.ApproachRate) { Default = BeatmapDifficulty.DEFAULT_DIFFICULTY, MinValue = 0, @@ -69,7 +69,7 @@ namespace osu.Game.Screens.Edit.Setup Label = "Overall Difficulty", FixedLabelWidth = LABEL_WIDTH, Description = "The harshness of hit windows and difficulty of special objects (ie. spinners)", - Current = new BindableFloat(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty) + Current = new BindableFloat(Beatmap.Difficulty.OverallDifficulty) { Default = BeatmapDifficulty.DEFAULT_DIFFICULTY, MinValue = 0, @@ -87,10 +87,10 @@ namespace osu.Game.Screens.Edit.Setup { // for now, update these on commit rather than making BeatmapMetadata bindables. // after switching database engines we can reconsider if switching to bindables is a good direction. - Beatmap.BeatmapInfo.BaseDifficulty.CircleSize = circleSizeSlider.Current.Value; - Beatmap.BeatmapInfo.BaseDifficulty.DrainRate = healthDrainSlider.Current.Value; - Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate = approachRateSlider.Current.Value; - Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty = overallDifficultySlider.Current.Value; + Beatmap.Difficulty.CircleSize = circleSizeSlider.Current.Value; + Beatmap.Difficulty.DrainRate = healthDrainSlider.Current.Value; + Beatmap.Difficulty.ApproachRate = approachRateSlider.Current.Value; + Beatmap.Difficulty.OverallDifficulty = overallDifficultySlider.Current.Value; Beatmap.UpdateAllHitObjects(); } diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index 64f1ee4a7a..54fb287661 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -111,6 +111,8 @@ namespace osu.Game.Tests.Beatmaps var converterResult = new Dictionary>(); + beatmap.BeatmapInfo.BaseDifficulty = beatmap.Difficulty; + var working = new ConversionWorkingBeatmap(beatmap) { ConversionGenerated = (o, r, c) => diff --git a/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs b/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs index 42cf826bd4..b2f5b1754f 100644 --- a/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs +++ b/osu.Game/Tests/Visual/PlacementBlueprintTestScene.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Timing; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; @@ -24,24 +25,25 @@ namespace osu.Game.Tests.Visual base.Content.Add(HitObjectContainer = CreateHitObjectContainer().With(c => c.Clock = new FramedClock(new StopwatchClock()))); } - [BackgroundDependencyLoader] - private void load() - { - Beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize = 2; - } - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); dependencies.CacheAs(new EditorClock()); - var playable = Beatmap.Value.GetPlayableBeatmap(Beatmap.Value.BeatmapInfo.Ruleset); + var playable = GetPlayableBeatmap(); dependencies.CacheAs(new EditorBeatmap(playable)); return dependencies; } + protected virtual IBeatmap GetPlayableBeatmap() + { + var playable = Beatmap.Value.GetPlayableBeatmap(Beatmap.Value.BeatmapInfo.Ruleset); + playable.Difficulty.CircleSize = 2; + return playable; + } + protected override void LoadComplete() { base.LoadComplete(); From 0ab8dcc2a0cc227c22f3a2d6eb9fa8756a2d542c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Oct 2021 15:10:42 +0900 Subject: [PATCH 2304/2442] Fix taiko weird difficulty multiplier failing on double convert --- .../Beatmaps/TaikoBeatmapConverter.cs | 27 ++++++++++++++-- osu.Game/Beatmaps/Beatmap.cs | 6 ++-- osu.Game/Beatmaps/BeatmapDifficulty.cs | 31 ++++++++++--------- 3 files changed, 43 insertions(+), 21 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 643a656199..ef8cf5cb53 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Utils; using System.Threading; +using JetBrains.Annotations; using osu.Game.Audio; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Formats; @@ -49,7 +50,6 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps if (!(original.Difficulty is TaikoMutliplierAppliedDifficulty)) { // Rewrite the beatmap info to add the slider velocity multiplier - original.BeatmapInfo = original.BeatmapInfo.Clone(); original.Difficulty = new TaikoMutliplierAppliedDifficulty(original.Difficulty); } @@ -196,9 +196,30 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps public TaikoMutliplierAppliedDifficulty(IBeatmapDifficultyInfo difficulty) { CopyFrom(difficulty); - - SliderMultiplier *= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; } + + [UsedImplicitly] + public TaikoMutliplierAppliedDifficulty() + { + } + + #region Overrides of BeatmapDifficulty + + public override void CopyTo(BeatmapDifficulty other) + { + base.CopyTo(other); + if (!(other is TaikoMutliplierAppliedDifficulty)) + SliderMultiplier /= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; + } + + public override void CopyFrom(IBeatmapDifficultyInfo other) + { + base.CopyFrom(other); + if (!(other is TaikoMutliplierAppliedDifficulty)) + SliderMultiplier *= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; + } + + #endregion } } } diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index f2a60c4711..b2211e26cf 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -28,7 +28,7 @@ namespace osu.Game.Beatmaps difficulty = value; if (beatmapInfo != null) - beatmapInfo.BaseDifficulty = new BeatmapDifficulty(difficulty); + beatmapInfo.BaseDifficulty = difficulty.Clone(); } } @@ -41,8 +41,8 @@ namespace osu.Game.Beatmaps { beatmapInfo = value; - if (beatmapInfo != null) - Difficulty = new BeatmapDifficulty(beatmapInfo.BaseDifficulty); + if (beatmapInfo?.BaseDifficulty != null) + Difficulty = beatmapInfo.BaseDifficulty.Clone(); } } diff --git a/osu.Game/Beatmaps/BeatmapDifficulty.cs b/osu.Game/Beatmaps/BeatmapDifficulty.cs index 2bb0787b4c..dfd21469fa 100644 --- a/osu.Game/Beatmaps/BeatmapDifficulty.cs +++ b/osu.Game/Beatmaps/BeatmapDifficulty.cs @@ -1,6 +1,7 @@ // 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 osu.Game.Database; namespace osu.Game.Beatmaps @@ -43,31 +44,31 @@ namespace osu.Game.Beatmaps /// public BeatmapDifficulty Clone() { - var diff = new BeatmapDifficulty(); + var diff = (BeatmapDifficulty)Activator.CreateInstance(GetType()); CopyTo(diff); return diff; } - public void CopyFrom(IBeatmapDifficultyInfo difficulty) + public virtual void CopyFrom(IBeatmapDifficultyInfo other) { - ApproachRate = difficulty.ApproachRate; - DrainRate = difficulty.DrainRate; - CircleSize = difficulty.CircleSize; - OverallDifficulty = difficulty.OverallDifficulty; + ApproachRate = other.ApproachRate; + DrainRate = other.DrainRate; + CircleSize = other.CircleSize; + OverallDifficulty = other.OverallDifficulty; - SliderMultiplier = difficulty.SliderMultiplier; - SliderTickRate = difficulty.SliderTickRate; + SliderMultiplier = other.SliderMultiplier; + SliderTickRate = other.SliderTickRate; } - public void CopyTo(BeatmapDifficulty difficulty) + public virtual void CopyTo(BeatmapDifficulty other) { - difficulty.ApproachRate = ApproachRate; - difficulty.DrainRate = DrainRate; - difficulty.CircleSize = CircleSize; - difficulty.OverallDifficulty = OverallDifficulty; + other.ApproachRate = ApproachRate; + other.DrainRate = DrainRate; + other.CircleSize = CircleSize; + other.OverallDifficulty = OverallDifficulty; - difficulty.SliderMultiplier = SliderMultiplier; - difficulty.SliderTickRate = SliderTickRate; + other.SliderMultiplier = SliderMultiplier; + other.SliderTickRate = SliderTickRate; } } } From 222997f2b9bce92d37364d188d115f8a0995cfaf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Oct 2021 15:18:21 +0900 Subject: [PATCH 2305/2442] Copy difficulty settings back out to `BeatmapInfo` on editor changes --- osu.Game/Screens/Edit/Editor.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 2ff0101dc0..b8a2d3612e 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -332,6 +332,8 @@ namespace osu.Game.Screens.Edit // no longer new after first user-triggered save. isNewBeatmap = false; + playableBeatmap.BeatmapInfo.BaseDifficulty.CopyFrom(playableBeatmap.Difficulty); + // apply any set-level metadata changes. beatmapManager.Update(playableBeatmap.BeatmapInfo.BeatmapSet); From 3803f2f4624293b7c948a4ee9b6eb3211572a853 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Oct 2021 16:07:27 +0900 Subject: [PATCH 2306/2442] Fix leaderboard potentially displaying the wrong scores Closes #14762. This class is ugly. I think the whole process should be clened up once we have correctly-scheduled `SynchronizationContext`s. There's not much saving it as long as all these interdispersed `Schedule`s around required. --- osu.Game/Online/Leaderboards/Leaderboard.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index 4f8b27602b..e3ac9f603d 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -255,6 +255,7 @@ namespace osu.Game.Online.Leaderboards } private APIRequest getScoresRequest; + private ScheduledDelegate getScoresRequestCallback; protected abstract bool IsOnlineScope { get; } @@ -282,13 +283,16 @@ namespace osu.Game.Online.Leaderboards getScoresRequest?.Cancel(); getScoresRequest = null; + getScoresRequestCallback?.Cancel(); + getScoresRequestCallback = null; + pendingUpdateScores?.Cancel(); pendingUpdateScores = Schedule(() => { PlaceholderState = PlaceholderState.Retrieving; loading.Show(); - getScoresRequest = FetchScores(scores => Schedule(() => + getScoresRequest = FetchScores(scores => getScoresRequestCallback = Schedule(() => { Scores = scores.ToArray(); PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores; @@ -297,7 +301,7 @@ namespace osu.Game.Online.Leaderboards if (getScoresRequest == null) return; - getScoresRequest.Failure += e => Schedule(() => + getScoresRequest.Failure += e => getScoresRequestCallback = Schedule(() => { if (e is OperationCanceledException) return; From 456cfd62bf50fb58da56594555fa338be0380f14 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 6 Oct 2021 16:46:24 +0900 Subject: [PATCH 2307/2442] Fix intermittent score panel test failure --- osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs index 6f3b3028be..b7b7407428 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs @@ -221,6 +221,8 @@ namespace osu.Game.Tests.Visual.Ranking list.SelectedScore.Value = middleScore; }); + AddUntilStep("wait for all scores to be visible", () => list.ChildrenOfType().All(t => t.IsPresent)); + assertScoreState(highestScore, false); assertScoreState(middleScore, true); assertScoreState(lowestScore, false); From fc0c3e8758a5e3817881069da5a26e27b80c557e Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Wed, 6 Oct 2021 17:32:28 +0900 Subject: [PATCH 2308/2442] Attach filters at load time --- osu.Game.Tests/Visual/Audio/TestSceneFilter.cs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/Audio/TestSceneFilter.cs b/osu.Game.Tests/Visual/Audio/TestSceneFilter.cs index 79d8f7da8c..c639da63d3 100644 --- a/osu.Game.Tests/Visual/Audio/TestSceneFilter.cs +++ b/osu.Game.Tests/Visual/Audio/TestSceneFilter.cs @@ -18,7 +18,6 @@ namespace osu.Game.Tests.Visual.Audio public class TestSceneFilter : OsuTestScene { private WorkingBeatmap testBeatmap; - private OsuSpriteText lowpassText; private OsuSpriteText highpassText; private Filter lowpassFilter; @@ -28,12 +27,12 @@ namespace osu.Game.Tests.Visual.Audio private void load(AudioManager audio) { testBeatmap = new WaveformTestBeatmap(audio); - lowpassFilter = new Filter(audio.TrackMixer); - highpassFilter = new Filter(audio.TrackMixer, BQFType.HighPass); Add(new FillFlowContainer { Children = new Drawable[] { + lowpassFilter = new Filter(audio.TrackMixer), + highpassFilter = new Filter(audio.TrackMixer, BQFType.HighPass), lowpassText = new OsuSpriteText { Padding = new MarginPadding(20), @@ -74,15 +73,8 @@ namespace osu.Game.Tests.Visual.Audio private void testFilter(Filter filter, int cutoffFrom, int cutoffTo) { - Add(filter); - AddStep("Prepare Track", () => - { - testBeatmap.LoadTrack(); - }); - AddStep("Play Track", () => - { - testBeatmap.Track.Start(); - }); + AddStep("Load Track", () => testBeatmap.LoadTrack()); + AddStep("Play Track", () => testBeatmap.Track.Start()); AddWaitStep("Let track play", 10); AddStep("Filter Sweep", () => { From bd0c98614435d91526409b78f1de14f18a00a3da Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Wed, 6 Oct 2021 17:34:24 +0900 Subject: [PATCH 2309/2442] Add asserts to ensure correct state when attaching/detaching the filter --- osu.Game/Audio/Effects/Filter.cs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/osu.Game/Audio/Effects/Filter.cs b/osu.Game/Audio/Effects/Filter.cs index 00b617ffa1..2036851cad 100644 --- a/osu.Game/Audio/Effects/Filter.cs +++ b/osu.Game/Audio/Effects/Filter.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Diagnostics; using ManagedBass.Fx; using osu.Framework.Audio.Mixing; using osu.Framework.Bindables; @@ -44,7 +45,7 @@ namespace osu.Game.Audio.Effects break; } - Cutoff = new BindableNumber + Cutoff = new BindableNumber(initialCutoff) { MinValue = 1, MaxValue = MaxCutoff @@ -57,14 +58,24 @@ namespace osu.Game.Audio.Effects fQ = 0.7f // This allows fCenter to go up to 22049hz (nyquist - 1hz) without overflowing and causing weird filter behaviour (see: https://www.un4seen.com/forum/?topic=19542.0) }; - attachFilter(); + // Don't start attached if this is low-pass or high-pass filter (as they have special auto-attach/detach logic) + if (type != BQFType.LowPass && type != BQFType.HighPass) + attachFilter(); + Cutoff.ValueChanged += updateFilter; - Cutoff.Value = initialCutoff; } - private void attachFilter() => mixer.Effects.Add(filter); + private void attachFilter() + { + Debug.Assert(!mixer.Effects.Contains(filter)); + mixer.Effects.Add(filter); + } - private void detachFilter() => mixer.Effects.Remove(filter); + private void detachFilter() + { + Debug.Assert(mixer.Effects.Contains(filter)); + mixer.Effects.Remove(filter); + } private void updateFilter(ValueChangedEvent cutoff) { From 99fb86878e42a75d162a2742b45f3c6f9779f79b Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Wed, 6 Oct 2021 18:31:56 +0900 Subject: [PATCH 2310/2442] Only detach filter on disposal if attached --- osu.Game/Audio/Effects/Filter.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Audio/Effects/Filter.cs b/osu.Game/Audio/Effects/Filter.cs index 2036851cad..428a69bb19 100644 --- a/osu.Game/Audio/Effects/Filter.cs +++ b/osu.Game/Audio/Effects/Filter.cs @@ -118,7 +118,9 @@ namespace osu.Game.Audio.Effects protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - detachFilter(); + + if (mixer.Effects.Contains(filter)) + detachFilter(); } } } From 34269d48e5e41a26d2e94e6b367ee6a583cf649e Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Wed, 6 Oct 2021 12:25:19 +0100 Subject: [PATCH 2311/2442] Use global multipliers instead of multiplying skill values --- .../Difficulty/OsuPerformanceCalculator.cs | 26 +++++-------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 62e0dea4fa..9d5887c26e 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -40,11 +40,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty countMeh = Score.Statistics.GetValueOrDefault(HitResult.Meh); countMiss = Score.Statistics.GetValueOrDefault(HitResult.Miss); - if (mods.Any(h => h is OsuModRelax)) - { - countMiss += countOk + countMeh; - } - // Custom multipliers for NoFail and SpunOut. double multiplier = 1.12; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things. @@ -54,6 +49,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(m => m is OsuModSpunOut)) multiplier *= 1.0 - Math.Pow((double)Attributes.SpinnerCount / totalHits, 0.85); + if (mods.Any(h => h is OsuModRelax)) + { + countMiss += countOk + countMeh; + multiplier *= 0.6; + } + double aimValue = computeAimValue(); double speedValue = computeSpeedValue(); double accuracyValue = computeAccuracyValue(); @@ -95,11 +96,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty aimValue *= lengthBonus; - if (mods.Any(h => h is OsuModRelax)) - { - aimValue *= 0.6; - } - // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. if (countMiss > 0) aimValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), countMiss); @@ -136,11 +132,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty { double speedValue = Math.Pow(5.0 * Math.Max(1.0, Attributes.SpeedStrain / 0.0675) - 4.0, 3.0) / 100000.0; - if (mods.Any(h => h is OsuModRelax)) - { - speedValue *= 0.6; - } - // Longer maps are worth more. double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) + (totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0); @@ -222,11 +213,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(h => h is OsuModHidden)) flashlightValue *= 1.3; - if (mods.Any(h => h is OsuModRelax)) - { - flashlightValue *= 0.6; - } - // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. if (countMiss > 0) flashlightValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), Math.Pow(countMiss, .875)); From 433e7cd4030ff1e2bd428b3112c23934369afccf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Oct 2021 21:26:30 +0900 Subject: [PATCH 2312/2442] Fix rate mods not working if pp counter is displayed --- osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 5 +++-- .../Screens/Play/HUD/PerformancePointsCounter.cs | 12 ++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index a7c4790366..1143549f7f 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -110,10 +110,11 @@ namespace osu.Game.Rulesets.Difficulty private void preProcess(Mod[] mods) { playableMods = mods.Select(m => m.DeepClone()).ToArray(); - Beatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods); + + Beatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, playableMods); var track = new TrackVirtual(10000); - mods.OfType().ForEach(m => m.ApplyToTrack(track)); + playableMods.OfType().ForEach(m => m.ApplyToTrack(track)); clockRate = track.Rate; } diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index 2ae7b5660a..15d9f9517b 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -60,6 +60,8 @@ namespace osu.Game.Screens.Play.HUD Current.Value = DisplayedCount = 0; } + private Mod[] clonedMods; + [BackgroundDependencyLoader] private void load(OsuColour colours, BeatmapDifficultyCache difficultyCache) { @@ -67,8 +69,10 @@ namespace osu.Game.Screens.Play.HUD if (gameplayState != null) { + clonedMods = gameplayState.Mods.Select(m => m.DeepClone()).ToArray(); + var gameplayWorkingBeatmap = new GameplayWorkingBeatmap(gameplayState.Beatmap); - difficultyCache.GetTimedDifficultyAttributesAsync(gameplayWorkingBeatmap, gameplayState.Ruleset, gameplayState.Mods.ToArray(), loadCancellationSource.Token) + difficultyCache.GetTimedDifficultyAttributesAsync(gameplayWorkingBeatmap, gameplayState.Ruleset, gameplayState.Mods.Select(m => m.DeepClone()).ToArray(), loadCancellationSource.Token) .ContinueWith(r => Schedule(() => { timedAttributes = r.Result; @@ -116,7 +120,11 @@ namespace osu.Game.Screens.Play.HUD return; } - var calculator = gameplayState.Ruleset.CreatePerformanceCalculator(attrib, gameplayState.Score.ScoreInfo); + // awkward but we need to make sure the true mods are not passed to PerformanceCalculator as it makes a mess of track applications. + var scoreInfo = gameplayState.Score.ScoreInfo.DeepClone(); + scoreInfo.Mods = clonedMods; + + var calculator = gameplayState.Ruleset.CreatePerformanceCalculator(attrib, scoreInfo); Current.Value = (int)Math.Round(calculator?.Calculate() ?? 0, MidpointRounding.AwayFromZero); IsValid = true; From 9705c7b546ff0ebe61e7bae5bd5ea8e3fdbcadd4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Oct 2021 21:30:30 +0900 Subject: [PATCH 2313/2442] Use cloned mods in one more place --- osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index 15d9f9517b..ef289c2a20 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -72,7 +72,7 @@ namespace osu.Game.Screens.Play.HUD clonedMods = gameplayState.Mods.Select(m => m.DeepClone()).ToArray(); var gameplayWorkingBeatmap = new GameplayWorkingBeatmap(gameplayState.Beatmap); - difficultyCache.GetTimedDifficultyAttributesAsync(gameplayWorkingBeatmap, gameplayState.Ruleset, gameplayState.Mods.Select(m => m.DeepClone()).ToArray(), loadCancellationSource.Token) + difficultyCache.GetTimedDifficultyAttributesAsync(gameplayWorkingBeatmap, gameplayState.Ruleset, clonedMods, loadCancellationSource.Token) .ContinueWith(r => Schedule(() => { timedAttributes = r.Result; From d6eab02d9273ff87cd648db7d2b4d2b278813ef9 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Wed, 6 Oct 2021 16:53:33 +0100 Subject: [PATCH 2314/2442] Return 0 for speed with relax --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 3 +++ osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 4c8d0b2ce6..651fd056f0 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -37,6 +37,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; double flashlightRating = Math.Sqrt(skills[2].DifficultyValue()) * difficulty_multiplier; + if (mods.Any(h => h is OsuModRelax)) + speedRating = 0.0; + double baseAimPerformance = Math.Pow(5 * Math.Max(1, aimRating / 0.0675) - 4, 3) / 100000; double baseSpeedPerformance = Math.Pow(5 * Math.Max(1, speedRating / 0.0675) - 4, 3) / 100000; double baseFlashlightPerformance = 0.0; diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index e8169ba660..b4421d8750 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -81,9 +81,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } } - if (Mods.Any(m => m is OsuModRelax)) - speedBonus = 0.0; - return (1 + (speedBonus - 1) * 0.75) * angleBonus * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) From 57c069e0e1a2ec625c449c427b49b9051c21f1f6 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Wed, 6 Oct 2021 23:50:23 +0100 Subject: [PATCH 2315/2442] Remove unused strings --- osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index b4421d8750..9364b11048 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -7,8 +7,6 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Objects; using osu.Framework.Utils; -using System.Linq; -using osu.Game.Rulesets.Osu.Mods; namespace osu.Game.Rulesets.Osu.Difficulty.Skills { From 5f129ae33c515adc30874c1df6cd475769e5d1a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Oct 2021 14:53:36 +0900 Subject: [PATCH 2316/2442] Remove local overridden storage of `Mods` in `Player` Not required and only causing headaches. Accessing mods should now be done via `GameplayState`. Closes #14912. --- osu.Game/Screens/Play/Player.cs | 39 ++++++++++++++------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index ecc65c6bb0..e2ed71e28a 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -125,15 +124,11 @@ namespace osu.Game.Screens.Play public DimmableStoryboard DimmableStoryboard { get; private set; } - [Cached] - [Cached(Type = typeof(IBindable>))] - protected new readonly Bindable> Mods = new Bindable>(Array.Empty()); - /// /// Whether failing should be allowed. /// By default, this checks whether all selected mods allow failing. /// - protected virtual bool CheckModsAllowFailure() => Mods.Value.OfType().All(m => m.PerformFail()); + protected virtual bool CheckModsAllowFailure() => GameplayState.Mods.OfType().All(m => m.PerformFail()); public readonly PlayerConfiguration Configuration; @@ -179,12 +174,12 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader(true)] private void load(AudioManager audio, OsuConfigManager config, OsuGameBase game) { - Mods.Value = base.Mods.Value.Select(m => m.DeepClone()).ToArray(); + var gameplayMods = Mods.Value.Select(m => m.DeepClone()).ToArray(); if (Beatmap.Value is DummyWorkingBeatmap) return; - IBeatmap playableBeatmap = loadPlayableBeatmap(); + IBeatmap playableBeatmap = loadPlayableBeatmap(gameplayMods); if (playableBeatmap == null) return; @@ -199,12 +194,12 @@ namespace osu.Game.Screens.Play if (game is OsuGame osuGame) LocalUserPlaying.BindTo(osuGame.LocalUserPlaying); - DrawableRuleset = ruleset.CreateDrawableRulesetWith(playableBeatmap, Mods.Value); + DrawableRuleset = ruleset.CreateDrawableRulesetWith(playableBeatmap, gameplayMods); dependencies.CacheAs(DrawableRuleset); ScoreProcessor = ruleset.CreateScoreProcessor(); ScoreProcessor.ApplyBeatmap(playableBeatmap); - ScoreProcessor.Mods.BindTo(Mods); + ScoreProcessor.Mods.Value = gameplayMods; dependencies.CacheAs(ScoreProcessor); @@ -223,9 +218,9 @@ namespace osu.Game.Screens.Play // ensure the score is in a consistent state with the current player. Score.ScoreInfo.BeatmapInfo = Beatmap.Value.BeatmapInfo; Score.ScoreInfo.Ruleset = ruleset.RulesetInfo; - Score.ScoreInfo.Mods = Mods.Value.ToArray(); + Score.ScoreInfo.Mods = gameplayMods; - dependencies.CacheAs(GameplayState = new GameplayState(playableBeatmap, ruleset, Mods.Value, Score)); + dependencies.CacheAs(GameplayState = new GameplayState(playableBeatmap, ruleset, gameplayMods, Score)); AddInternal(screenSuspension = new ScreenSuspensionHandler(GameplayClockContainer)); @@ -302,13 +297,13 @@ namespace osu.Game.Screens.Play // this is required for mods that apply transforms to these processors. ScoreProcessor.OnLoadComplete += _ => { - foreach (var mod in Mods.Value.OfType()) + foreach (var mod in gameplayMods.OfType()) mod.ApplyToScoreProcessor(ScoreProcessor); }; HealthProcessor.OnLoadComplete += _ => { - foreach (var mod in Mods.Value.OfType()) + foreach (var mod in gameplayMods.OfType()) mod.ApplyToHealthProcessor(HealthProcessor); }; @@ -356,7 +351,7 @@ namespace osu.Game.Screens.Play // display the cursor above some HUD elements. DrawableRuleset.Cursor?.CreateProxy() ?? new Container(), DrawableRuleset.ResumeOverlay?.CreateProxy() ?? new Container(), - HUDOverlay = new HUDOverlay(DrawableRuleset, Mods.Value) + HUDOverlay = new HUDOverlay(DrawableRuleset, GameplayState.Mods) { HoldToQuit = { @@ -467,7 +462,7 @@ namespace osu.Game.Screens.Play } } - private IBeatmap loadPlayableBeatmap() + private IBeatmap loadPlayableBeatmap(Mod[] gameplayMods) { IBeatmap playable; @@ -481,7 +476,7 @@ namespace osu.Game.Screens.Play try { - playable = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo, Mods.Value); + playable = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo, gameplayMods); } catch (BeatmapInvalidForRulesetException) { @@ -489,7 +484,7 @@ namespace osu.Game.Screens.Play rulesetInfo = Beatmap.Value.BeatmapInfo.Ruleset; ruleset = rulesetInfo.CreateInstance(); - playable = Beatmap.Value.GetPlayableBeatmap(rulesetInfo, Mods.Value); + playable = Beatmap.Value.GetPlayableBeatmap(rulesetInfo, gameplayMods); } if (playable.HitObjects.Count == 0) @@ -789,7 +784,7 @@ namespace osu.Game.Screens.Play failAnimation.Start(); - if (Mods.Value.OfType().Any(m => m.RestartOnFail)) + if (GameplayState.Mods.OfType().Any(m => m.RestartOnFail)) Restart(); return true; @@ -919,17 +914,17 @@ namespace osu.Game.Screens.Play storyboardReplacesBackground.Value = Beatmap.Value.Storyboard.ReplacesBackground && Beatmap.Value.Storyboard.HasDrawable; - foreach (var mod in Mods.Value.OfType()) + foreach (var mod in GameplayState.Mods.OfType()) mod.ApplyToPlayer(this); - foreach (var mod in Mods.Value.OfType()) + foreach (var mod in GameplayState.Mods.OfType()) mod.ApplyToHUD(HUDOverlay); // Our mods are local copies of the global mods so they need to be re-applied to the track. // This is done through the music controller (for now), because resetting speed adjustments on the beatmap track also removes adjustments provided by DrawableTrack. // Todo: In the future, player will receive in a track and will probably not have to worry about this... musicController.ResetTrackAdjustments(); - foreach (var mod in Mods.Value.OfType()) + foreach (var mod in GameplayState.Mods.OfType()) mod.ApplyToTrack(musicController.CurrentTrack); updateGameplayState(); From 697f53c4450882f3c3e70262a4c167422e9921a0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Oct 2021 15:00:37 +0900 Subject: [PATCH 2317/2442] Fix test failure due to reference of `Player.Mods` --- osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 8160a62991..aee15a145c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -205,7 +205,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("wait for loader to become current", () => loader.IsCurrentScreen()); AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre)); AddUntilStep("wait for player to be current", () => player.IsCurrentScreen()); - AddStep("retrieve mods", () => playerMod1 = (TestMod)player.Mods.Value.Single()); + AddStep("retrieve mods", () => playerMod1 = (TestMod)player.GameplayState.Mods.Single()); AddAssert("game mods not applied", () => gameMod.Applied == false); AddAssert("player mods applied", () => playerMod1.Applied); @@ -217,7 +217,7 @@ namespace osu.Game.Tests.Visual.Gameplay }); AddUntilStep("wait for player to be current", () => player.IsCurrentScreen()); - AddStep("retrieve mods", () => playerMod2 = (TestMod)player.Mods.Value.Single()); + AddStep("retrieve mods", () => playerMod2 = (TestMod)player.GameplayState.Mods.Single()); AddAssert("game mods not applied", () => gameMod.Applied == false); AddAssert("player has different mods", () => playerMod1 != playerMod2); AddAssert("player mods applied", () => playerMod2.Applied); From a57b080f10050c8c9630def0fdcbf99f1718e97a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Oct 2021 15:27:57 +0900 Subject: [PATCH 2318/2442] Avoid showing the disclaimer in game tests No real performance gain, but this is handy to bypass when actually using one of these tests to test something. --- osu.Game/Tests/Visual/OsuGameTestScene.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Tests/Visual/OsuGameTestScene.cs b/osu.Game/Tests/Visual/OsuGameTestScene.cs index c025cf85c7..dba73b0024 100644 --- a/osu.Game/Tests/Visual/OsuGameTestScene.cs +++ b/osu.Game/Tests/Visual/OsuGameTestScene.cs @@ -126,6 +126,8 @@ namespace osu.Game.Tests.Visual public new Bindable> SelectedMods => base.SelectedMods; + public override Version AssemblyVersion => new Version(0, 0); + // if we don't do this, when running under nUnit the version that gets populated is that of nUnit. public override string Version => "test game"; From 290c9755e261d2732b85f305feb9ba6b0a5535ac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Oct 2021 15:46:50 +0900 Subject: [PATCH 2319/2442] Always use circles intro for `OsuGame` tests The triangles intro tracks video time, which is not adjusted based on the game's playback rate (ie. it runs in realtime even for headless tests). Maybe we want to make the triangles video adjust its rate along with tests? --- osu.Game/Tests/Visual/OsuGameTestScene.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Tests/Visual/OsuGameTestScene.cs b/osu.Game/Tests/Visual/OsuGameTestScene.cs index dba73b0024..94715dfc1a 100644 --- a/osu.Game/Tests/Visual/OsuGameTestScene.cs +++ b/osu.Game/Tests/Visual/OsuGameTestScene.cs @@ -22,6 +22,7 @@ using osu.Game.Scoring; using osu.Game.Screens; using osu.Game.Screens.Menu; using osuTK.Graphics; +using IntroSequence = osu.Game.Configuration.IntroSequence; namespace osu.Game.Tests.Visual { @@ -144,6 +145,9 @@ namespace osu.Game.Tests.Visual protected override void LoadComplete() { base.LoadComplete(); + + LocalConfig.SetValue(OsuSetting.IntroSequence, IntroSequence.Circles); + API.Login("Rhythm Champion", "osu!"); Dependencies.Get().SetValue(Static.MutedAudioNotificationShownOnce, true); From 0bd5136a293c99e31a3ead1af16f989027666b82 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Oct 2021 15:47:59 +0900 Subject: [PATCH 2320/2442] Fix `TestOverlayClosing` occasionally failing due to running too fast --- .../Visual/Navigation/TestSceneScreenNavigation.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index aeb800f58a..ce437e7299 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -350,13 +350,13 @@ namespace osu.Game.Tests.Visual.Navigation // since most overlays use a scroll container that absorbs on mouse down NowPlayingOverlay nowPlayingOverlay = null; - AddStep("enter menu", () => InputManager.Key(Key.Enter)); + AddUntilStep("Wait for now playing load", () => (nowPlayingOverlay = Game.ChildrenOfType().FirstOrDefault()) != null); - AddStep("get and press now playing hotkey", () => - { - nowPlayingOverlay = Game.ChildrenOfType().Single(); - InputManager.Key(Key.F6); - }); + AddStep("enter menu", () => InputManager.Key(Key.Enter)); + AddUntilStep("toolbar displayed", () => Game.Toolbar.State.Value == Visibility.Visible); + + AddStep("open now playing", () => InputManager.Key(Key.F6)); + AddUntilStep("now playing is visible", () => nowPlayingOverlay.State.Value == Visibility.Visible); // drag tests @@ -417,7 +417,7 @@ namespace osu.Game.Tests.Visual.Navigation pushEscape(); // returns to osu! logo AddStep("Hold escape", () => InputManager.PressKey(Key.Escape)); - AddUntilStep("Wait for intro", () => Game.ScreenStack.CurrentScreen is IntroTriangles); + AddUntilStep("Wait for intro", () => Game.ScreenStack.CurrentScreen is IntroScreen); AddStep("Release escape", () => InputManager.ReleaseKey(Key.Escape)); AddUntilStep("Wait for game exit", () => Game.ScreenStack.CurrentScreen == null); AddStep("test dispose doesn't crash", () => Game.Dispose()); From 7e0379441c692c5eed9f16ef8b6e36bed6bf6549 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Oct 2021 16:18:47 +0900 Subject: [PATCH 2321/2442] Change `TestSceneOsuGame` to use `OsuGameTestScene` to avoid async disposal deadlock The original implementation was done so in a way that the nested `OsuGame` would be disposed via the async queue, causing a deadlock for 10-20s during test runs. `OsuGameTestScene` was already fixed to avoid this, so consuming it here seems like the easy fix. --- .../Visual/Navigation/TestSceneOsuGame.cs | 30 +++---------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs index 7327d4053a..353ead131e 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs @@ -34,7 +34,7 @@ using osuTK.Graphics; namespace osu.Game.Tests.Visual.Navigation { [TestFixture] - public class TestSceneOsuGame : OsuTestScene + public class TestSceneOsuGame : OsuGameTestScene { private IReadOnlyList requiredGameDependencies => new[] { @@ -84,34 +84,12 @@ namespace osu.Game.Tests.Visual.Navigation typeof(PreviewTrackManager), }; - private OsuGame game; - [Resolved] private OsuGameBase gameBase { get; set; } [Resolved] private GameHost host { get; set; } - [SetUpSteps] - public void SetUpSteps() - { - AddStep("create game", () => - { - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - }, - }; - - AddGame(game = new OsuGame()); - }); - - AddUntilStep("wait for load", () => game.IsLoaded); - } - [Test] public void TestNullRulesetHandled() { @@ -127,8 +105,8 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestSwitchThreadExecutionMode() { - AddStep("Change thread mode to multi threaded", () => { game.Dependencies.Get().SetValue(FrameworkSetting.ExecutionMode, ExecutionMode.MultiThreaded); }); - AddStep("Change thread mode to single thread", () => { game.Dependencies.Get().SetValue(FrameworkSetting.ExecutionMode, ExecutionMode.SingleThread); }); + AddStep("Change thread mode to multi threaded", () => { Game.Dependencies.Get().SetValue(FrameworkSetting.ExecutionMode, ExecutionMode.MultiThreaded); }); + AddStep("Change thread mode to single thread", () => { Game.Dependencies.Get().SetValue(FrameworkSetting.ExecutionMode, ExecutionMode.SingleThread); }); } [Test] @@ -154,7 +132,7 @@ namespace osu.Game.Tests.Visual.Navigation { foreach (var type in requiredGameDependencies) { - if (game.Dependencies.Get(type) == null) + if (Game.Dependencies.Get(type) == null) throw new InvalidOperationException($"{type} has not been cached"); } From c41271ea78871e4b39af13521ff93ed853552e80 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Oct 2021 16:26:24 +0900 Subject: [PATCH 2322/2442] Fix hidden test failures --- osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs index af64be78f8..ed9da36b05 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs @@ -120,7 +120,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods private bool checkSomeHit() => Player.ScoreProcessor.JudgedHits >= 4; private bool objectWithIncreasedVisibilityHasIndex(int index) - => Player.Mods.Value.OfType().Single().FirstObject == Player.GameplayState.Beatmap.HitObjects[index]; + => Player.GameplayState.Mods.OfType().Single().FirstObject == Player.GameplayState.Beatmap.HitObjects[index]; private class TestOsuModHidden : OsuModHidden { From f88d8989605d2cd54e4af6ed26f5b096db88c386 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Oct 2021 16:38:22 +0900 Subject: [PATCH 2323/2442] Allow intro screens to be created without loading a `MainMenu` --- osu.Game/Screens/Loader.cs | 8 +++++--- osu.Game/Screens/Menu/IntroCircles.cs | 7 +++++++ osu.Game/Screens/Menu/IntroScreen.cs | 25 +++++++++++++++++++++---- osu.Game/Screens/Menu/IntroTriangles.cs | 6 ++++++ osu.Game/Screens/Menu/IntroWelcome.cs | 7 +++++++ 5 files changed, 46 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs index 0bfabdaa15..41097a4c74 100644 --- a/osu.Game/Screens/Loader.cs +++ b/osu.Game/Screens/Loader.cs @@ -49,14 +49,16 @@ namespace osu.Game.Screens switch (introSequence) { case IntroSequence.Circles: - return new IntroCircles(); + return new IntroCircles(createMainMenu); case IntroSequence.Welcome: - return new IntroWelcome(); + return new IntroWelcome(createMainMenu); default: - return new IntroTriangles(); + return new IntroTriangles(createMainMenu); } + + MainMenu createMainMenu() => new MainMenu(); } protected virtual ShaderPrecompiler CreateShaderPrecompiler() => new ShaderPrecompiler(); diff --git a/osu.Game/Screens/Menu/IntroCircles.cs b/osu.Game/Screens/Menu/IntroCircles.cs index a1b8c3a203..2792d05f75 100644 --- a/osu.Game/Screens/Menu/IntroCircles.cs +++ b/osu.Game/Screens/Menu/IntroCircles.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; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -20,6 +22,11 @@ namespace osu.Game.Screens.Menu private Sample welcome; + public IntroCircles([CanBeNull] Func createNextScreen = null) + : base(createNextScreen) + { + } + [BackgroundDependencyLoader] private void load(AudioManager audio) { diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index fbd33cad67..32fb9f1d6d 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -1,15 +1,17 @@ // 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.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Utils; using osu.Framework.Screens; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.IO.Archives; @@ -55,7 +57,7 @@ namespace osu.Game.Screens.Menu private LeasedBindable beatmap; - private MainMenu mainMenu; + private OsuScreen nextScreen; [Resolved] private AudioManager audio { get; set; } @@ -63,12 +65,20 @@ namespace osu.Game.Screens.Menu [Resolved] private MusicController musicController { get; set; } + [CanBeNull] + private readonly Func createNextScreen; + /// /// Whether the is provided by osu! resources, rather than a user beatmap. /// Only valid during or after . /// protected bool UsingThemedIntro { get; private set; } + protected IntroScreen([CanBeNull] Func createNextScreen = null) + { + this.createNextScreen = createNextScreen; + } + [BackgroundDependencyLoader] private void load(OsuConfigManager config, SkinManager skinManager, BeatmapManager beatmaps, Framework.Game game) { @@ -214,14 +224,21 @@ namespace osu.Game.Screens.Menu } } - protected void PrepareMenuLoad() => LoadComponentAsync(mainMenu = new MainMenu()); + protected void PrepareMenuLoad() + { + nextScreen = createNextScreen?.Invoke(); + + if (nextScreen != null) + LoadComponentAsync(nextScreen); + } protected void LoadMenu() { beatmap.Return(); DidLoadMenu = true; - this.Push(mainMenu); + if (nextScreen != null) + this.Push(nextScreen); } } } diff --git a/osu.Game/Screens/Menu/IntroTriangles.cs b/osu.Game/Screens/Menu/IntroTriangles.cs index a8ca17cec1..48ced63182 100644 --- a/osu.Game/Screens/Menu/IntroTriangles.cs +++ b/osu.Game/Screens/Menu/IntroTriangles.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -44,6 +45,11 @@ namespace osu.Game.Screens.Menu private DecoupleableInterpolatingFramedClock decoupledClock; private TrianglesIntroSequence intro; + public IntroTriangles([CanBeNull] Func createNextScreen = null) + : base(createNextScreen) + { + } + [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game/Screens/Menu/IntroWelcome.cs b/osu.Game/Screens/Menu/IntroWelcome.cs index f74043b045..639591cfef 100644 --- a/osu.Game/Screens/Menu/IntroWelcome.cs +++ b/osu.Game/Screens/Menu/IntroWelcome.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; +using JetBrains.Annotations; using osuTK; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -32,6 +34,11 @@ namespace osu.Game.Screens.Menu private BackgroundScreenDefault background; + public IntroWelcome([CanBeNull] Func createNextScreen = null) + : base(createNextScreen) + { + } + [BackgroundDependencyLoader] private void load(AudioManager audio) { From d0001f760d82bd1953da17b39898d5ac2a4d96a5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Oct 2021 16:50:05 +0900 Subject: [PATCH 2324/2442] Group applicable comment above new addition --- osu.Game/Tests/Visual/OsuGameTestScene.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Tests/Visual/OsuGameTestScene.cs b/osu.Game/Tests/Visual/OsuGameTestScene.cs index 94715dfc1a..77db697cb6 100644 --- a/osu.Game/Tests/Visual/OsuGameTestScene.cs +++ b/osu.Game/Tests/Visual/OsuGameTestScene.cs @@ -127,9 +127,8 @@ namespace osu.Game.Tests.Visual public new Bindable> SelectedMods => base.SelectedMods; + // if we don't apply these changes, when running under nUnit the version that gets populated is that of nUnit. public override Version AssemblyVersion => new Version(0, 0); - - // if we don't do this, when running under nUnit the version that gets populated is that of nUnit. public override string Version => "test game"; protected override Loader CreateLoader() => new TestLoader(); From ca9c79b758f531d617c450cb9c685e604f3876e5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Oct 2021 16:51:42 +0900 Subject: [PATCH 2325/2442] Use object initialiser (fixes CI inspection) --- .../Editor/CatchSelectionBlueprintTestScene.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs b/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs index 7d806b119e..d4c2c0f0af 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/CatchSelectionBlueprintTestScene.cs @@ -29,8 +29,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor protected CatchSelectionBlueprintTestScene() { - EditorBeatmap = new EditorBeatmap(new CatchBeatmap()); - EditorBeatmap.Difficulty.CircleSize = 0; + EditorBeatmap = new EditorBeatmap(new CatchBeatmap()) { Difficulty = { CircleSize = 0 } }; EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 100 From 0df409c0503654f987b29e44031f9af6a1ea27c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Oct 2021 17:16:44 +0900 Subject: [PATCH 2326/2442] Move difficulty copy to `BeatmapModelManager.Save` --- osu.Game/Beatmaps/BeatmapModelManager.cs | 2 ++ osu.Game/Screens/Edit/Editor.cs | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapModelManager.cs b/osu.Game/Beatmaps/BeatmapModelManager.cs index 787559899a..4d698d2c92 100644 --- a/osu.Game/Beatmaps/BeatmapModelManager.cs +++ b/osu.Game/Beatmaps/BeatmapModelManager.cs @@ -192,6 +192,8 @@ namespace osu.Game.Beatmaps { var setInfo = beatmapInfo.BeatmapSet; + beatmapInfo.BaseDifficulty.CopyFrom(beatmapContent.Difficulty); + using (var stream = new MemoryStream()) { using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index b8a2d3612e..2ff0101dc0 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -332,8 +332,6 @@ namespace osu.Game.Screens.Edit // no longer new after first user-triggered save. isNewBeatmap = false; - playableBeatmap.BeatmapInfo.BaseDifficulty.CopyFrom(playableBeatmap.Difficulty); - // apply any set-level metadata changes. beatmapManager.Update(playableBeatmap.BeatmapInfo.BeatmapSet); From 7dd7d35fc84550971b3271baafc390d319e63f3c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Oct 2021 17:19:34 +0900 Subject: [PATCH 2327/2442] Remove unnecessary difficulty copy operation --- osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index 54fb287661..64f1ee4a7a 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -111,8 +111,6 @@ namespace osu.Game.Tests.Beatmaps var converterResult = new Dictionary>(); - beatmap.BeatmapInfo.BaseDifficulty = beatmap.Difficulty; - var working = new ConversionWorkingBeatmap(beatmap) { ConversionGenerated = (o, r, c) => From d3efec3c045f99b3a749a966c708796fcbc02058 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Oct 2021 17:22:10 +0900 Subject: [PATCH 2328/2442] Remove unnecessary clone operations --- osu.Game/Beatmaps/WorkingBeatmap.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index c7ccfe976a..18adecb7aa 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -119,9 +119,6 @@ namespace osu.Game.Beatmaps // Apply difficulty mods if (mods.Any(m => m is IApplicableToDifficulty)) { - converted.BeatmapInfo = converted.BeatmapInfo.Clone(); - converted.Difficulty = converted.Difficulty.Clone(); - foreach (var mod in mods.OfType()) { if (cancellationSource.IsCancellationRequested) From 185bb9c122c9a15a85c03bec91ad7f0c03e6de8b Mon Sep 17 00:00:00 2001 From: Joseph Ireland Date: Thu, 7 Oct 2021 09:30:18 +0100 Subject: [PATCH 2329/2442] change initial strain from 1 to 0 to allow simpler implementations --- .../CatchDifficultyCalculatorTest.cs | 4 ++-- .../OsuDifficultyCalculatorTest.cs | 6 +++--- .../TaikoDifficultyCalculatorTest.cs | 8 ++++---- osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs | 2 +- osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs index 5580358f89..2fab47f857 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs @@ -14,11 +14,11 @@ namespace osu.Game.Rulesets.Catch.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Catch"; - [TestCase(4.050601681491468d, "diffcalc-test")] + [TestCase(4.0505463516206195d, "diffcalc-test")] public void Test(double expected, string name) => base.Test(expected, name); - [TestCase(5.169743871843191d, "diffcalc-test")] + [TestCase(5.1696411260785498d, "diffcalc-test")] public void TestClockRateAdjusted(double expected, string name) => Test(expected, name, new CatchModDoubleTime()); diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index 19881b5c33..b0d46f40fc 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -16,12 +16,12 @@ namespace osu.Game.Rulesets.Osu.Tests protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; [TestCase(6.6634445062299665d, "diffcalc-test")] - [TestCase(1.0414203870195022d, "zero-length-sliders")] + [TestCase(1.0404303969295756d, "zero-length-sliders")] public void Test(double expected, string name) => base.Test(expected, name); - [TestCase(8.3858089051603368d, "diffcalc-test")] - [TestCase(1.2723279173428435d, "zero-length-sliders")] + [TestCase(8.3857915525197733d, "diffcalc-test")] + [TestCase(1.2705229071231638d, "zero-length-sliders")] public void TestClockRateAdjusted(double expected, string name) => Test(expected, name, new OsuModDoubleTime()); diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs index dd3c6b317a..0ca5e1bb08 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs @@ -14,13 +14,13 @@ namespace osu.Game.Rulesets.Taiko.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko"; - [TestCase(2.2867022617692685d, "diffcalc-test")] - [TestCase(2.2867022617692685d, "diffcalc-test-strong")] + [TestCase(2.2593624565103561d, "diffcalc-test")] + [TestCase(2.2593624565103561d, "diffcalc-test-strong")] public void Test(double expected, string name) => base.Test(expected, name); - [TestCase(3.1704781712282624d, "diffcalc-test")] - [TestCase(3.1704781712282624d, "diffcalc-test-strong")] + [TestCase(3.1518486708786382d, "diffcalc-test")] + [TestCase(3.1518486708786382d, "diffcalc-test-strong")] public void TestClockRateAdjusted(double expected, string name) => Test(expected, name, new TaikoModDoubleTime()); diff --git a/osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs b/osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs index 73bab31e82..d8babf2f32 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Difficulty.Skills /// /// The current strain level. /// - protected double CurrentStrain { get; private set; } = 1; + protected double CurrentStrain { get; private set; } protected StrainDecaySkill(Mod[] mods) : base(mods) diff --git a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs index 0880f1b08e..bbd2f079aa 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Difficulty.Skills /// protected virtual int SectionLength => 400; - private double currentSectionPeak = 1; // We also keep track of the peak strain level in the current section. + private double currentSectionPeak; // We also keep track of the peak strain level in the current section. private double currentSectionEnd; From e586fee09133d56d9df22cf32f598f1ab6a5e765 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Oct 2021 17:46:30 +0900 Subject: [PATCH 2330/2442] Remove unused usings --- osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs index 353ead131e..2706ff5ceb 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs @@ -7,11 +7,8 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; using osu.Framework.Platform; -using osu.Framework.Testing; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Configuration; @@ -29,7 +26,6 @@ using osu.Game.Scoring; using osu.Game.Screens.Menu; using osu.Game.Skinning; using osu.Game.Utils; -using osuTK.Graphics; namespace osu.Game.Tests.Visual.Navigation { From ee66414e4f0119c7b36f72fb745fd833a64c9645 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Oct 2021 17:49:13 +0900 Subject: [PATCH 2331/2442] Move difficulty copy inside context retrieval --- osu.Game/Beatmaps/BeatmapModelManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapModelManager.cs b/osu.Game/Beatmaps/BeatmapModelManager.cs index 4d698d2c92..9c0fc5ef8a 100644 --- a/osu.Game/Beatmaps/BeatmapModelManager.cs +++ b/osu.Game/Beatmaps/BeatmapModelManager.cs @@ -192,8 +192,6 @@ namespace osu.Game.Beatmaps { var setInfo = beatmapInfo.BeatmapSet; - beatmapInfo.BaseDifficulty.CopyFrom(beatmapContent.Difficulty); - using (var stream = new MemoryStream()) { using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) @@ -204,6 +202,8 @@ namespace osu.Game.Beatmaps using (ContextFactory.GetForWrite()) { beatmapInfo = setInfo.Beatmaps.Single(b => b.ID == beatmapInfo.ID); + beatmapInfo.BaseDifficulty.CopyFrom(beatmapContent.Difficulty); + var metadata = beatmapInfo.Metadata ?? setInfo.Metadata; // grab the original file (or create a new one if not found). From 5c48340520cb5b8df5ae8852f01c0b1f0239aa43 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 7 Oct 2021 14:15:16 +0900 Subject: [PATCH 2332/2442] Add filter effect to beatmap loading --- osu.Game/Screens/Play/PlayerLoader.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 969527a758..4168dda867 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -15,6 +15,7 @@ using osu.Framework.Graphics.Transforms; using osu.Framework.Input; using osu.Framework.Screens; using osu.Framework.Threading; +using osu.Game.Audio.Effects; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -63,6 +64,8 @@ namespace osu.Game.Screens.Play private readonly BindableDouble volumeAdjustment = new BindableDouble(1); + private Filter lpFilter; + protected bool BackgroundBrightnessReduction { set @@ -127,7 +130,7 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader] - private void load(SessionStatics sessionStatics) + private void load(SessionStatics sessionStatics, AudioManager audio) { muteWarningShownOnce = sessionStatics.GetBindable(Static.MutedAudioNotificationShownOnce); batteryWarningShownOnce = sessionStatics.GetBindable(Static.LowBatteryNotificationShownOnce); @@ -159,7 +162,8 @@ namespace osu.Game.Screens.Play new InputSettings() } }, - idleTracker = new IdleTracker(750) + idleTracker = new IdleTracker(750), + lpFilter = new Filter(audio.TrackMixer) }); if (Beatmap.Value.BeatmapInfo.EpilepsyWarning) @@ -191,6 +195,7 @@ namespace osu.Game.Screens.Play epilepsyWarning.DimmableBackground = b; }); + lpFilter.CutoffTo(500, 100, Easing.OutCubic); Beatmap.Value.Track.AddAdjustment(AdjustableProperty.Volume, volumeAdjustment); content.ScaleTo(0.7f); @@ -229,6 +234,7 @@ namespace osu.Game.Screens.Play // stop the track before removing adjustment to avoid a volume spike. Beatmap.Value.Track.Stop(); Beatmap.Value.Track.RemoveAdjustment(AdjustableProperty.Volume, volumeAdjustment); + lpFilter.CutoffTo(lpFilter.MaxCutoff); } public override bool OnExiting(IScreen next) @@ -242,6 +248,7 @@ namespace osu.Game.Screens.Play BackgroundBrightnessReduction = false; Beatmap.Value.Track.RemoveAdjustment(AdjustableProperty.Volume, volumeAdjustment); + lpFilter.CutoffTo(lpFilter.MaxCutoff, 100, Easing.InCubic); return base.OnExiting(next); } From cc209f0f2d71a0a27ab52cd3e1d4a1642997e5b4 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 7 Oct 2021 14:15:47 +0900 Subject: [PATCH 2333/2442] Add filter effect to fail sequence --- osu.Game/Screens/Play/Player.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 9927467bd6..c3b733ae58 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -16,6 +16,7 @@ using osu.Framework.Input.Events; using osu.Framework.Logging; using osu.Framework.Screens; using osu.Framework.Threading; +using osu.Game.Audio.Effects; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Graphics.Containers; @@ -62,6 +63,8 @@ namespace osu.Game.Screens.Play private readonly Bindable samplePlaybackDisabled = new Bindable(); + private Filter lpFilter; + /// /// Whether gameplay should pause when the game window focus is lost. /// @@ -227,6 +230,7 @@ namespace osu.Game.Screens.Play AddInternal(GameplayBeatmap = new GameplayBeatmap(playableBeatmap)); AddInternal(screenSuspension = new ScreenSuspensionHandler(GameplayClockContainer)); + AddInternal(lpFilter = new Filter(audio.TrackMixer)); dependencies.CacheAs(GameplayBeatmap); @@ -788,6 +792,7 @@ namespace osu.Game.Screens.Play if (PauseOverlay.State.Value == Visibility.Visible) PauseOverlay.Hide(); + lpFilter.CutoffTo(300, 2500, Easing.OutCubic); failAnimation.Start(); if (Mods.Value.OfType().Any(m => m.RestartOnFail)) @@ -800,6 +805,7 @@ namespace osu.Game.Screens.Play private void onFailComplete() { GameplayClockContainer.Stop(); + lpFilter.CutoffTo(lpFilter.MaxCutoff); FailOverlay.Retries = RestartCount; FailOverlay.Show(); From f6458aa26d9b7cba848faea8ab1a1e75ad8e88ec Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Thu, 7 Oct 2021 17:56:11 +0900 Subject: [PATCH 2334/2442] Add filter effect to collection management dialog --- osu.Game/Collections/ManageCollectionsDialog.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Collections/ManageCollectionsDialog.cs b/osu.Game/Collections/ManageCollectionsDialog.cs index 680fec904f..c239396a41 100644 --- a/osu.Game/Collections/ManageCollectionsDialog.cs +++ b/osu.Game/Collections/ManageCollectionsDialog.cs @@ -2,11 +2,13 @@ // 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.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Game.Audio.Effects; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -19,6 +21,7 @@ namespace osu.Game.Collections { private const double enter_duration = 500; private const double exit_duration = 200; + private Filter lpFilter; [Resolved(CanBeNull = true)] private CollectionManager collectionManager { get; set; } @@ -36,7 +39,7 @@ namespace osu.Game.Collections } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, AudioManager audio) { Children = new Drawable[] { @@ -108,7 +111,8 @@ namespace osu.Game.Collections }, } } - } + }, + lpFilter = new Filter(audio.TrackMixer) }; } @@ -116,6 +120,7 @@ namespace osu.Game.Collections { base.PopIn(); + lpFilter.CutoffTo(300, 100, Easing.OutCubic); this.FadeIn(enter_duration, Easing.OutQuint); this.ScaleTo(0.9f).Then().ScaleTo(1f, enter_duration, Easing.OutQuint); } @@ -124,6 +129,7 @@ namespace osu.Game.Collections { base.PopOut(); + lpFilter.CutoffTo(lpFilter.MaxCutoff, 100, Easing.InCubic); this.FadeOut(exit_duration, Easing.OutQuint); this.ScaleTo(0.9f, exit_duration); From 189358240d4e11857062381d1c6b7e1f5873a9e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Oct 2021 18:39:48 +0900 Subject: [PATCH 2335/2442] Fix intro tests and move steps out of constructor --- osu.Game.Tests/Visual/Menus/IntroTestScene.cs | 13 +++++++++---- .../Visual/Menus/TestSceneIntroCircles.cs | 3 +-- .../Visual/Menus/TestSceneIntroTriangles.cs | 3 +-- .../Visual/Menus/TestSceneIntroWelcome.cs | 3 +-- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs index f71d13ed35..6e1f789bfe 100644 --- a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs +++ b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs @@ -5,7 +5,6 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; -using osu.Framework.Screens; using osu.Game.Screens; using osu.Game.Screens.Menu; using osuTK; @@ -21,6 +20,8 @@ namespace osu.Game.Tests.Visual.Menus protected OsuScreenStack IntroStack; + private IntroScreen intro; + protected IntroTestScene() { Children = new Drawable[] @@ -39,7 +40,11 @@ namespace osu.Game.Tests.Visual.Menus Position = new Vector2(0.5f), } }; + } + [Test] + public void TestPlayIntro() + { AddStep("restart sequence", () => { logo.FinishTransforms(); @@ -52,12 +57,12 @@ namespace osu.Game.Tests.Visual.Menus RelativeSizeAxes = Axes.Both, }); - IntroStack.Push(CreateScreen()); + IntroStack.Push(intro = CreateScreen()); }); - AddUntilStep("wait for menu", () => IntroStack.CurrentScreen is MainMenu); + AddUntilStep("wait for menu", () => intro.DidLoadMenu); } - protected abstract IScreen CreateScreen(); + protected abstract IntroScreen CreateScreen(); } } diff --git a/osu.Game.Tests/Visual/Menus/TestSceneIntroCircles.cs b/osu.Game.Tests/Visual/Menus/TestSceneIntroCircles.cs index 107734cc8d..ffc99185fb 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneIntroCircles.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneIntroCircles.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; -using osu.Framework.Screens; using osu.Game.Screens.Menu; namespace osu.Game.Tests.Visual.Menus @@ -10,6 +9,6 @@ namespace osu.Game.Tests.Visual.Menus [TestFixture] public class TestSceneIntroCircles : IntroTestScene { - protected override IScreen CreateScreen() => new IntroCircles(); + protected override IntroScreen CreateScreen() => new IntroCircles(); } } diff --git a/osu.Game.Tests/Visual/Menus/TestSceneIntroTriangles.cs b/osu.Game.Tests/Visual/Menus/TestSceneIntroTriangles.cs index df79584167..8f01e0321b 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneIntroTriangles.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneIntroTriangles.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; -using osu.Framework.Screens; using osu.Game.Screens.Menu; namespace osu.Game.Tests.Visual.Menus @@ -10,6 +9,6 @@ namespace osu.Game.Tests.Visual.Menus [TestFixture] public class TestSceneIntroTriangles : IntroTestScene { - protected override IScreen CreateScreen() => new IntroTriangles(); + protected override IntroScreen CreateScreen() => new IntroTriangles(); } } diff --git a/osu.Game.Tests/Visual/Menus/TestSceneIntroWelcome.cs b/osu.Game.Tests/Visual/Menus/TestSceneIntroWelcome.cs index 5f135febf4..71681dd03e 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneIntroWelcome.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneIntroWelcome.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; -using osu.Framework.Screens; using osu.Framework.Utils; using osu.Game.Screens.Menu; @@ -11,7 +10,7 @@ namespace osu.Game.Tests.Visual.Menus [TestFixture] public class TestSceneIntroWelcome : IntroTestScene { - protected override IScreen CreateScreen() => new IntroWelcome(); + protected override IntroScreen CreateScreen() => new IntroWelcome(); public TestSceneIntroWelcome() { From 0348c6c7e596a54977085ebd5d603bad47507ea7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Oct 2021 18:47:52 +0900 Subject: [PATCH 2336/2442] Apply some renaming and code quality fixes --- .../Visual/Audio/TestSceneFilter.cs | 4 +-- .../Effects/{Filter.cs => AudioFilter.cs} | 29 +++++++++++++------ osu.Game/Overlays/DialogOverlay.cs | 2 +- 3 files changed, 23 insertions(+), 12 deletions(-) rename osu.Game/Audio/Effects/{Filter.cs => AudioFilter.cs} (80%) diff --git a/osu.Game.Tests/Visual/Audio/TestSceneFilter.cs b/osu.Game.Tests/Visual/Audio/TestSceneFilter.cs index c639da63d3..6e5c8e8850 100644 --- a/osu.Game.Tests/Visual/Audio/TestSceneFilter.cs +++ b/osu.Game.Tests/Visual/Audio/TestSceneFilter.cs @@ -66,10 +66,10 @@ namespace osu.Game.Tests.Visual.Audio } [Test] - public void TestLowPass() => testFilter(lowpassFilter, lowpassFilter.MaxCutoff, 0); + public void TestLowPass() => testFilter(lowpassFilter, AudioFilter.MAX_LOWPASS_CUTOFF, 0); [Test] - public void TestHighPass() => testFilter(highpassFilter, 0, highpassFilter.MaxCutoff); + public void TestHighPass() => testFilter(highpassFilter, 0, AudioFilter.MAX_LOWPASS_CUTOFF); private void testFilter(Filter filter, int cutoffFrom, int cutoffTo) { diff --git a/osu.Game/Audio/Effects/Filter.cs b/osu.Game/Audio/Effects/AudioFilter.cs similarity index 80% rename from osu.Game/Audio/Effects/Filter.cs rename to osu.Game/Audio/Effects/AudioFilter.cs index 428a69bb19..611ece5bdb 100644 --- a/osu.Game/Audio/Effects/Filter.cs +++ b/osu.Game/Audio/Effects/AudioFilter.cs @@ -11,11 +11,19 @@ namespace osu.Game.Audio.Effects { public class Filter : Component, ITransformableFilter { - public readonly int MaxCutoff = 22049; // nyquist - 1hz + /// + /// The maximum cutoff frequency that can be used with a low-pass filter. + /// This is equal to nyquist - 1hz. + /// + public const int MAX_LOWPASS_CUTOFF = 22049; // nyquist - 1hz + private readonly AudioMixer mixer; private readonly BQFParameters filter; private readonly BQFType type; + /// + /// The current cutoff of this filter. + /// public BindableNumber Cutoff { get; } /// @@ -37,7 +45,7 @@ namespace osu.Game.Audio.Effects break; case BQFType.LowPass: - initialCutoff = MaxCutoff; + initialCutoff = MAX_LOWPASS_CUTOFF; break; default: @@ -48,8 +56,9 @@ namespace osu.Game.Audio.Effects Cutoff = new BindableNumber(initialCutoff) { MinValue = 1, - MaxValue = MaxCutoff + MaxValue = MAX_LOWPASS_CUTOFF }; + filter = new BQFParameters { lFilter = type, @@ -82,13 +91,13 @@ namespace osu.Game.Audio.Effects // Workaround for weird behaviour when rapidly setting fCenter of a low-pass filter to nyquist - 1hz. if (type == BQFType.LowPass) { - if (cutoff.NewValue >= MaxCutoff) + if (cutoff.NewValue >= MAX_LOWPASS_CUTOFF) { detachFilter(); return; } - if (cutoff.OldValue >= MaxCutoff && cutoff.NewValue < MaxCutoff) + if (cutoff.OldValue >= MAX_LOWPASS_CUTOFF && cutoff.NewValue < MAX_LOWPASS_CUTOFF) attachFilter(); } @@ -108,11 +117,13 @@ namespace osu.Game.Audio.Effects var filterIndex = mixer.Effects.IndexOf(filter); if (filterIndex < 0) return; - var existingFilter = mixer.Effects[filterIndex] as BQFParameters; - if (existingFilter == null) return; + if (mixer.Effects[filterIndex] is BQFParameters existingFilter) + { + existingFilter.fCenter = cutoff.NewValue; - existingFilter.fCenter = cutoff.NewValue; - mixer.Effects[filterIndex] = existingFilter; + // required to update effect with new parameters. + mixer.Effects[filterIndex] = existingFilter; + } } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index bd20b74970..5f4ab35a03 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -89,7 +89,7 @@ namespace osu.Game.Overlays { base.PopOut(); - lpFilter.CutoffTo(lpFilter.MaxCutoff, 100, Easing.InCubic); + lpFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF, 100, Easing.InCubic); if (CurrentDialog?.State.Value == Visibility.Visible) { From e578046b206dcf0d2a87da7c075fde8f90b29023 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Oct 2021 18:49:30 +0900 Subject: [PATCH 2337/2442] Rename `Filter` -> `AudioFilter` --- ...{TestSceneFilter.cs => TestSceneAudioFilter.cs} | 14 ++++++++------ osu.Game/Audio/Effects/AudioFilter.cs | 4 ++-- osu.Game/Overlays/DialogOverlay.cs | 8 ++++---- 3 files changed, 14 insertions(+), 12 deletions(-) rename osu.Game.Tests/Visual/Audio/{TestSceneFilter.cs => TestSceneAudioFilter.cs} (89%) diff --git a/osu.Game.Tests/Visual/Audio/TestSceneFilter.cs b/osu.Game.Tests/Visual/Audio/TestSceneAudioFilter.cs similarity index 89% rename from osu.Game.Tests/Visual/Audio/TestSceneFilter.cs rename to osu.Game.Tests/Visual/Audio/TestSceneAudioFilter.cs index 6e5c8e8850..cf93273ae2 100644 --- a/osu.Game.Tests/Visual/Audio/TestSceneFilter.cs +++ b/osu.Game.Tests/Visual/Audio/TestSceneAudioFilter.cs @@ -15,13 +15,15 @@ using osu.Game.Graphics.UserInterface; namespace osu.Game.Tests.Visual.Audio { - public class TestSceneFilter : OsuTestScene + public class TestSceneAudioFilter : OsuTestScene { private WorkingBeatmap testBeatmap; + private OsuSpriteText lowpassText; + private AudioFilter highpassFilter; + + private AudioFilter lowpassFilter; private OsuSpriteText highpassText; - private Filter lowpassFilter; - private Filter highpassFilter; [BackgroundDependencyLoader] private void load(AudioManager audio) @@ -31,8 +33,8 @@ namespace osu.Game.Tests.Visual.Audio { Children = new Drawable[] { - lowpassFilter = new Filter(audio.TrackMixer), - highpassFilter = new Filter(audio.TrackMixer, BQFType.HighPass), + lowpassFilter = new AudioFilter(audio.TrackMixer), + highpassFilter = new AudioFilter(audio.TrackMixer, BQFType.HighPass), lowpassText = new OsuSpriteText { Padding = new MarginPadding(20), @@ -71,7 +73,7 @@ namespace osu.Game.Tests.Visual.Audio [Test] public void TestHighPass() => testFilter(highpassFilter, 0, AudioFilter.MAX_LOWPASS_CUTOFF); - private void testFilter(Filter filter, int cutoffFrom, int cutoffTo) + private void testFilter(AudioFilter filter, int cutoffFrom, int cutoffTo) { AddStep("Load Track", () => testBeatmap.LoadTrack()); AddStep("Play Track", () => testBeatmap.Track.Start()); diff --git a/osu.Game/Audio/Effects/AudioFilter.cs b/osu.Game/Audio/Effects/AudioFilter.cs index 611ece5bdb..ee48bdd7d9 100644 --- a/osu.Game/Audio/Effects/AudioFilter.cs +++ b/osu.Game/Audio/Effects/AudioFilter.cs @@ -9,7 +9,7 @@ using osu.Framework.Graphics; namespace osu.Game.Audio.Effects { - public class Filter : Component, ITransformableFilter + public class AudioFilter : Component, ITransformableFilter { /// /// The maximum cutoff frequency that can be used with a low-pass filter. @@ -31,7 +31,7 @@ namespace osu.Game.Audio.Effects /// /// The mixer this effect should be applied to. /// The type of filter (e.g. LowPass, HighPass, etc) - public Filter(AudioMixer mixer, BQFType type = BQFType.LowPass) + public AudioFilter(AudioMixer mixer, BQFType type = BQFType.LowPass) { this.mixer = mixer; this.type = type; diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index 5f4ab35a03..9db0f34d1b 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays protected override string PopInSampleName => "UI/dialog-pop-in"; protected override string PopOutSampleName => "UI/dialog-pop-out"; - private Filter lpFilter; + private AudioFilter lowPassFilter; public PopupDialog CurrentDialog { get; private set; } @@ -42,7 +42,7 @@ namespace osu.Game.Overlays [BackgroundDependencyLoader] private void load(AudioManager audio) { - AddInternal(lpFilter = new Filter(audio.TrackMixer)); + AddInternal(lowPassFilter = new AudioFilter(audio.TrackMixer)); } public void Push(PopupDialog dialog) @@ -82,14 +82,14 @@ namespace osu.Game.Overlays { base.PopIn(); this.FadeIn(PopupDialog.ENTER_DURATION, Easing.OutQuint); - lpFilter.CutoffTo(300, 100, Easing.OutCubic); + lowPassFilter.CutoffTo(300, 100, Easing.OutCubic); } protected override void PopOut() { base.PopOut(); - lpFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF, 100, Easing.InCubic); + lowPassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF, 100, Easing.InCubic); if (CurrentDialog?.State.Value == Visibility.Visible) { From b88d4f19137012aafe01370b1ef6a2ba529dee02 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Oct 2021 19:58:34 +0900 Subject: [PATCH 2338/2442] Fix weird edge case in `TestSceneIntroWelcome` --- osu.Game.Tests/Visual/Menus/IntroTestScene.cs | 2 +- osu.Game.Tests/Visual/Menus/TestSceneIntroWelcome.cs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs index 6e1f789bfe..bfea97410a 100644 --- a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs +++ b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.Menus } [Test] - public void TestPlayIntro() + public virtual void TestPlayIntro() { AddStep("restart sequence", () => { diff --git a/osu.Game.Tests/Visual/Menus/TestSceneIntroWelcome.cs b/osu.Game.Tests/Visual/Menus/TestSceneIntroWelcome.cs index 71681dd03e..9081be3dd6 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneIntroWelcome.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneIntroWelcome.cs @@ -12,8 +12,10 @@ namespace osu.Game.Tests.Visual.Menus { protected override IntroScreen CreateScreen() => new IntroWelcome(); - public TestSceneIntroWelcome() + public override void TestPlayIntro() { + base.TestPlayIntro(); + AddUntilStep("wait for load", () => MusicController.TrackLoaded); AddAssert("correct track", () => Precision.AlmostEquals(MusicController.CurrentTrack.Length, 48000, 1)); AddAssert("check if menu music loops", () => MusicController.CurrentTrack.Looping); From ba84da6ef89892658677a7f67b50b8e1e43df0b6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Oct 2021 20:08:02 +0900 Subject: [PATCH 2339/2442] Tidy up test scene --- .../Visual/Audio/TestSceneAudioFilter.cs | 77 +++++++++++++------ 1 file changed, 54 insertions(+), 23 deletions(-) diff --git a/osu.Game.Tests/Visual/Audio/TestSceneAudioFilter.cs b/osu.Game.Tests/Visual/Audio/TestSceneAudioFilter.cs index cf93273ae2..211543a881 100644 --- a/osu.Game.Tests/Visual/Audio/TestSceneAudioFilter.cs +++ b/osu.Game.Tests/Visual/Audio/TestSceneAudioFilter.cs @@ -5,11 +5,12 @@ using ManagedBass.Fx; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Audio.Track; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Testing; using osu.Game.Audio.Effects; -using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -17,18 +18,22 @@ namespace osu.Game.Tests.Visual.Audio { public class TestSceneAudioFilter : OsuTestScene { - private WorkingBeatmap testBeatmap; - private OsuSpriteText lowpassText; + private AudioFilter lowpassFilter; + + private OsuSpriteText highpassText; private AudioFilter highpassFilter; - private AudioFilter lowpassFilter; - private OsuSpriteText highpassText; + private Track track; + + private WaveformTestBeatmap beatmap; [BackgroundDependencyLoader] private void load(AudioManager audio) { - testBeatmap = new WaveformTestBeatmap(audio); + beatmap = new WaveformTestBeatmap(audio); + track = beatmap.LoadTrack(); + Add(new FillFlowContainer { Children = new Drawable[] @@ -67,30 +72,56 @@ namespace osu.Game.Tests.Visual.Audio highpassFilter.Cutoff.ValueChanged += e => highpassText.Text = $"High Pass: {e.NewValue}hz"; } - [Test] - public void TestLowPass() => testFilter(lowpassFilter, AudioFilter.MAX_LOWPASS_CUTOFF, 0); - - [Test] - public void TestHighPass() => testFilter(highpassFilter, 0, AudioFilter.MAX_LOWPASS_CUTOFF); - - private void testFilter(AudioFilter filter, int cutoffFrom, int cutoffTo) + [SetUpSteps] + public void SetUpSteps() + { + AddStep("Play Track", () => track.Start()); + waitTrackPlay(); + } + + [Test] + public void TestLowPass() { - AddStep("Load Track", () => testBeatmap.LoadTrack()); - AddStep("Play Track", () => testBeatmap.Track.Start()); - AddWaitStep("Let track play", 10); AddStep("Filter Sweep", () => { - filter.CutoffTo(cutoffFrom).Then() - .CutoffTo(cutoffTo, 2000, cutoffFrom > cutoffTo ? Easing.OutCubic : Easing.InCubic); + lowpassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF).Then() + .CutoffTo(0, 2000, Easing.OutCubic); }); - AddWaitStep("Let track play", 10); + + waitTrackPlay(); + AddStep("Filter Sweep (reverse)", () => { - filter.CutoffTo(cutoffTo).Then() - .CutoffTo(cutoffFrom, 2000, cutoffTo > cutoffFrom ? Easing.OutCubic : Easing.InCubic); + lowpassFilter.CutoffTo(0).Then() + .CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF, 2000, Easing.InCubic); }); - AddWaitStep("Let track play", 10); - AddStep("Stop track", () => testBeatmap.Track.Stop()); + + waitTrackPlay(); + AddStep("Stop track", () => track.Stop()); } + + [Test] + public void TestHighPass() + { + AddStep("Filter Sweep", () => + { + highpassFilter.CutoffTo(0).Then() + .CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF, 2000, Easing.InCubic); + }); + + waitTrackPlay(); + + AddStep("Filter Sweep (reverse)", () => + { + highpassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF).Then() + .CutoffTo(0, 2000, Easing.OutCubic); + }); + + waitTrackPlay(); + + AddStep("Stop track", () => track.Stop()); + } + + private void waitTrackPlay() => AddWaitStep("Let track play", 10); } } From 9bf29503fb94a0726c65d07b0f649e2c72aa9a97 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Oct 2021 20:57:14 +0900 Subject: [PATCH 2340/2442] Rename remaining usage --- osu.Game/Collections/ManageCollectionsDialog.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Collections/ManageCollectionsDialog.cs b/osu.Game/Collections/ManageCollectionsDialog.cs index c239396a41..95fbfa0f86 100644 --- a/osu.Game/Collections/ManageCollectionsDialog.cs +++ b/osu.Game/Collections/ManageCollectionsDialog.cs @@ -21,7 +21,8 @@ namespace osu.Game.Collections { private const double enter_duration = 500; private const double exit_duration = 200; - private Filter lpFilter; + + private AudioFilter lowPassFilter; [Resolved(CanBeNull = true)] private CollectionManager collectionManager { get; set; } @@ -112,7 +113,7 @@ namespace osu.Game.Collections } } }, - lpFilter = new Filter(audio.TrackMixer) + lowPassFilter = new AudioFilter(audio.TrackMixer) }; } @@ -120,7 +121,7 @@ namespace osu.Game.Collections { base.PopIn(); - lpFilter.CutoffTo(300, 100, Easing.OutCubic); + lowPassFilter.CutoffTo(300, 100, Easing.OutCubic); this.FadeIn(enter_duration, Easing.OutQuint); this.ScaleTo(0.9f).Then().ScaleTo(1f, enter_duration, Easing.OutQuint); } @@ -129,7 +130,8 @@ namespace osu.Game.Collections { base.PopOut(); - lpFilter.CutoffTo(lpFilter.MaxCutoff, 100, Easing.InCubic); + lowPassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF, 100, Easing.InCubic); + this.FadeOut(exit_duration, Easing.OutQuint); this.ScaleTo(0.9f, exit_duration); From da96cc73d8b3005e28b444b0afc69eac652da446 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Oct 2021 23:40:47 +0900 Subject: [PATCH 2341/2442] Fix dual specification of `SuspensionHandler` and move fields around slightly --- osu.Game/Screens/Play/Player.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 620b026060..30ba2bfe6d 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -62,8 +62,6 @@ namespace osu.Game.Screens.Play private readonly Bindable samplePlaybackDisabled = new Bindable(); - private AudioFilter lowPassFilter; - /// /// Whether gameplay should pause when the game window focus is lost. /// @@ -217,7 +215,7 @@ namespace osu.Game.Screens.Play InternalChild = GameplayClockContainer = CreateGameplayClockContainer(Beatmap.Value, DrawableRuleset.GameplayStartTime); AddInternal(screenSuspension = new ScreenSuspensionHandler(GameplayClockContainer)); - AddInternal(lowPassFilter = new AudioFilter(audio.TrackMixer)); + AddInternal(failLowPassFilter = new AudioFilter(audio.TrackMixer)); Score = CreateScore(playableBeatmap); @@ -228,8 +226,6 @@ namespace osu.Game.Screens.Play dependencies.CacheAs(GameplayState = new GameplayState(playableBeatmap, ruleset, gameplayMods, Score)); - AddInternal(screenSuspension = new ScreenSuspensionHandler(GameplayClockContainer)); - var rulesetSkinProvider = new RulesetSkinProvidingContainer(ruleset, playableBeatmap, Beatmap.Value.Skin); // load the skinning hierarchy first. @@ -774,6 +770,8 @@ namespace osu.Game.Screens.Play private FailAnimation failAnimation; + private AudioFilter failLowPassFilter; + private bool onFail() { if (!CheckModsAllowFailure()) @@ -788,7 +786,7 @@ namespace osu.Game.Screens.Play if (PauseOverlay.State.Value == Visibility.Visible) PauseOverlay.Hide(); - lowPassFilter.CutoffTo(300, 2500, Easing.OutCubic); + failLowPassFilter.CutoffTo(300, 2500, Easing.OutCubic); failAnimation.Start(); if (GameplayState.Mods.OfType().Any(m => m.RestartOnFail)) @@ -801,7 +799,7 @@ namespace osu.Game.Screens.Play private void onFailComplete() { GameplayClockContainer.Stop(); - lowPassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF); + failLowPassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF); FailOverlay.Retries = RestartCount; FailOverlay.Show(); From 436ead421a71e0ca941d7e0f20aefd4f25fc83b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Oct 2021 12:27:04 +0900 Subject: [PATCH 2342/2442] Move low pass fail effect to `FailAnimation` --- osu.Game/Screens/Play/FailAnimation.cs | 10 +++++++++- osu.Game/Screens/Play/Player.cs | 6 ------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/FailAnimation.cs b/osu.Game/Screens/Play/FailAnimation.cs index 71bea2a145..e250791b72 100644 --- a/osu.Game/Screens/Play/FailAnimation.cs +++ b/osu.Game/Screens/Play/FailAnimation.cs @@ -10,7 +10,9 @@ using osu.Framework.Allocation; using osu.Framework.Audio.Sample; using osu.Framework.Audio.Track; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Utils; +using osu.Game.Audio.Effects; using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects.Drawables; using osuTK; @@ -22,7 +24,7 @@ namespace osu.Game.Screens.Play /// Manage the animation to be applied when a player fails. /// Single file; automatically disposed after use. /// - public class FailAnimation : Component + public class FailAnimation : CompositeDrawable { public Action OnComplete; @@ -32,6 +34,8 @@ namespace osu.Game.Screens.Play private Track track; + private AudioFilter failLowPassFilter; + private const float duration = 2500; private Sample failSample; @@ -46,6 +50,8 @@ namespace osu.Game.Screens.Play { track = beatmap.Value.Track; failSample = audio.Samples.Get(@"Gameplay/failsound"); + + AddInternal(failLowPassFilter = new AudioFilter(audio.TrackMixer)); } private bool started; @@ -68,6 +74,8 @@ namespace osu.Game.Screens.Play Expire(); }); + failLowPassFilter.CutoffTo(300, duration, Easing.OutCubic); + track.AddAdjustment(AdjustableProperty.Frequency, trackFreq); applyToPlayfield(drawableRuleset.Playfield); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 30ba2bfe6d..090210e611 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -15,7 +15,6 @@ using osu.Framework.Input.Events; using osu.Framework.Logging; using osu.Framework.Screens; using osu.Framework.Threading; -using osu.Game.Audio.Effects; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Graphics.Containers; @@ -215,7 +214,6 @@ namespace osu.Game.Screens.Play InternalChild = GameplayClockContainer = CreateGameplayClockContainer(Beatmap.Value, DrawableRuleset.GameplayStartTime); AddInternal(screenSuspension = new ScreenSuspensionHandler(GameplayClockContainer)); - AddInternal(failLowPassFilter = new AudioFilter(audio.TrackMixer)); Score = CreateScore(playableBeatmap); @@ -770,8 +768,6 @@ namespace osu.Game.Screens.Play private FailAnimation failAnimation; - private AudioFilter failLowPassFilter; - private bool onFail() { if (!CheckModsAllowFailure()) @@ -786,7 +782,6 @@ namespace osu.Game.Screens.Play if (PauseOverlay.State.Value == Visibility.Visible) PauseOverlay.Hide(); - failLowPassFilter.CutoffTo(300, 2500, Easing.OutCubic); failAnimation.Start(); if (GameplayState.Mods.OfType().Any(m => m.RestartOnFail)) @@ -799,7 +794,6 @@ namespace osu.Game.Screens.Play private void onFailComplete() { GameplayClockContainer.Stop(); - failLowPassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF); FailOverlay.Retries = RestartCount; FailOverlay.Show(); From 2856aef4eb17ee91f8d8f5c0a7852fae2e517870 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Oct 2021 13:51:12 +0900 Subject: [PATCH 2343/2442] Add exception to catch any incorrect defaults of `Bindable` --- osu.Game/Overlays/Settings/SettingsTextBox.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/osu.Game/Overlays/Settings/SettingsTextBox.cs b/osu.Game/Overlays/Settings/SettingsTextBox.cs index d28dbf1068..68562802cf 100644 --- a/osu.Game/Overlays/Settings/SettingsTextBox.cs +++ b/osu.Game/Overlays/Settings/SettingsTextBox.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; +using osu.Framework.Bindables; using osu.Framework.Graphics; namespace osu.Game.Overlays.Settings @@ -13,5 +15,17 @@ namespace osu.Game.Overlays.Settings RelativeSizeAxes = Axes.X, CommitOnFocusLost = true }; + + public override Bindable Current + { + get => base.Current; + set + { + if (value.Default == null) + throw new InvalidOperationException($"Bindable settings of type {nameof(Bindable)} should have a non-null default value."); + + base.Current = value; + } + } } } From 672664dce7c88beb56328e0af53377465d04ea2a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Oct 2021 13:55:20 +0900 Subject: [PATCH 2344/2442] Fix all remaining cases of incorrect `Bindable` defaults --- osu.Game.Tournament/Models/SeedingResult.cs | 2 +- osu.Game.Tournament/Models/TournamentRound.cs | 4 ++-- osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs | 2 +- osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tournament/Models/SeedingResult.cs b/osu.Game.Tournament/Models/SeedingResult.cs index 87aaf8bf36..d37c967762 100644 --- a/osu.Game.Tournament/Models/SeedingResult.cs +++ b/osu.Game.Tournament/Models/SeedingResult.cs @@ -10,7 +10,7 @@ namespace osu.Game.Tournament.Models { public List Beatmaps = new List(); - public Bindable Mod = new Bindable(); + public Bindable Mod = new Bindable(string.Empty); public Bindable Seed = new BindableInt { diff --git a/osu.Game.Tournament/Models/TournamentRound.cs b/osu.Game.Tournament/Models/TournamentRound.cs index 08b3143be1..ab39605d07 100644 --- a/osu.Game.Tournament/Models/TournamentRound.cs +++ b/osu.Game.Tournament/Models/TournamentRound.cs @@ -14,8 +14,8 @@ namespace osu.Game.Tournament.Models [Serializable] public class TournamentRound { - public readonly Bindable Name = new Bindable(); - public readonly Bindable Description = new Bindable(); + public readonly Bindable Name = new Bindable(string.Empty); + public readonly Bindable Description = new Bindable(string.Empty); public readonly BindableInt BestOf = new BindableInt(9) { Default = 9, MinValue = 3, MaxValue = 23 }; diff --git a/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs index 6e4fc8fe1a..1d8c4e7476 100644 --- a/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs @@ -149,7 +149,7 @@ namespace osu.Game.Tournament.Screens.Editors private readonly Bindable beatmapId = new Bindable(); - private readonly Bindable mods = new Bindable(); + private readonly Bindable mods = new Bindable(string.Empty); private readonly Container drawableContainer; diff --git a/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs index b64a3993e6..d5b55823a5 100644 --- a/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs @@ -149,7 +149,7 @@ namespace osu.Game.Tournament.Screens.Editors private readonly Bindable beatmapId = new Bindable(); - private readonly Bindable score = new Bindable(); + private readonly Bindable score = new Bindable(string.Empty); private readonly Container drawableContainer; From 9dc035757fc3e1b763e227de33d442d8641381e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Oct 2021 13:51:41 +0900 Subject: [PATCH 2345/2442] Fix weird textbox behaviour when entering a random mod seed overflowing int backing --- osu.Game/Overlays/Settings/SettingsNumberBox.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Settings/SettingsNumberBox.cs b/osu.Game/Overlays/Settings/SettingsNumberBox.cs index 2fbe522479..d36aa2bfc2 100644 --- a/osu.Game/Overlays/Settings/SettingsNumberBox.cs +++ b/osu.Game/Overlays/Settings/SettingsNumberBox.cs @@ -36,6 +36,7 @@ namespace osu.Game.Overlays.Settings { numberBox = new OutlinedNumberBox { + LengthLimit = 9, // limited to less than a value that could overflow int32 backing. Margin = new MarginPadding { Top = 5 }, RelativeSizeAxes = Axes.X, CommitOnFocusLost = true From b82ed3f1674520ae8680e845d873eb88d39130d5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Oct 2021 14:23:53 +0900 Subject: [PATCH 2346/2442] Fix potential blocking operation on OrderByTotalScoreAsync() In reality this wouldn't be a long process, but the blocking is really noticeable if you add a Task.Delay(1000) in GetTotalScoreAsync(). --- osu.Game/Scoring/ScoreManager.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index dde956233b..5481eea4aa 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -72,9 +72,12 @@ namespace osu.Game.Scoring } } - // We're calling .Result, but this should not be a blocking call due to the above GetDifficultyAsync() calls. - return scores.OrderByDescending(s => GetTotalScoreAsync(s, cancellationToken: cancellationToken).Result) - .ThenBy(s => s.OnlineScoreID) + var totalScores = await Task.WhenAll(scores.Select(s => GetTotalScoreAsync(s, cancellationToken: cancellationToken))).ConfigureAwait(false); + + return scores.Select((s, i) => (index: i, score: s)) + .OrderByDescending(key => totalScores[key.index]) + .ThenBy(key => key.score.OnlineScoreID) + .Select(key => key.score) .ToArray(); } From d6ac6a5cd60263ae778ebb488541e1a28a65a1c8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Oct 2021 15:18:01 +0900 Subject: [PATCH 2347/2442] Fix intermittent results screen test failures --- .../TestScenePlaylistsResultsScreen.cs | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs index 4bcc887b9f..d948aebbbf 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs @@ -32,12 +32,14 @@ namespace osu.Game.Tests.Visual.Playlists private TestResultsScreen resultsScreen; private int currentScoreId; private bool requestComplete; + private int totalCount; [SetUp] public void Setup() => Schedule(() => { currentScoreId = 0; requestComplete = false; + totalCount = 0; bindHandler(); }); @@ -53,7 +55,6 @@ namespace osu.Game.Tests.Visual.Playlists }); createResults(() => userScore); - waitForDisplay(); AddAssert("user score selected", () => this.ChildrenOfType().Single(p => p.Score.OnlineScoreID == userScore.OnlineScoreID).State == PanelState.Expanded); } @@ -62,7 +63,6 @@ namespace osu.Game.Tests.Visual.Playlists public void TestShowNullUserScore() { createResults(); - waitForDisplay(); AddAssert("top score selected", () => this.ChildrenOfType().OrderByDescending(p => p.Score.TotalScore).First().State == PanelState.Expanded); } @@ -79,7 +79,6 @@ namespace osu.Game.Tests.Visual.Playlists }); createResults(() => userScore); - waitForDisplay(); AddAssert("more than 1 panel displayed", () => this.ChildrenOfType().Count() > 1); AddAssert("user score selected", () => this.ChildrenOfType().Single(p => p.Score.OnlineScoreID == userScore.OnlineScoreID).State == PanelState.Expanded); @@ -91,7 +90,6 @@ namespace osu.Game.Tests.Visual.Playlists AddStep("bind delayed handler", () => bindHandler(true)); createResults(); - waitForDisplay(); AddAssert("top score selected", () => this.ChildrenOfType().OrderByDescending(p => p.Score.TotalScore).First().State == PanelState.Expanded); } @@ -100,7 +98,6 @@ namespace osu.Game.Tests.Visual.Playlists public void TestFetchWhenScrolledToTheRight() { createResults(); - waitForDisplay(); AddStep("bind delayed handler", () => bindHandler(true)); @@ -131,7 +128,6 @@ namespace osu.Game.Tests.Visual.Playlists }); createResults(() => userScore); - waitForDisplay(); AddStep("bind delayed handler", () => bindHandler(true)); @@ -161,13 +157,15 @@ namespace osu.Game.Tests.Visual.Playlists })); }); - AddUntilStep("wait for load", () => resultsScreen.ChildrenOfType().FirstOrDefault()?.AllPanelsVisible == true); + waitForDisplay(); } private void waitForDisplay() { - AddUntilStep("wait for request to complete", () => requestComplete); - AddUntilStep("wait for panels to be visible", () => resultsScreen.ChildrenOfType().FirstOrDefault()?.AllPanelsVisible == true); + AddUntilStep("wait for load to complete", () => + requestComplete + && resultsScreen.ScorePanelList.GetScorePanels().Count() == totalCount + && resultsScreen.ScorePanelList.AllPanelsVisible); AddWaitStep("wait for display", 5); } @@ -203,6 +201,7 @@ namespace osu.Game.Tests.Visual.Playlists triggerFail(s); else triggerSuccess(s, createUserResponse(userScore)); + break; case IndexPlaylistScoresRequest i: @@ -248,6 +247,8 @@ namespace osu.Game.Tests.Visual.Playlists } }; + totalCount++; + for (int i = 1; i <= scores_per_result; i++) { multiplayerUserScore.ScoresAround.Lower.Scores.Add(new MultiplayerScore @@ -285,6 +286,8 @@ namespace osu.Game.Tests.Visual.Playlists }, Statistics = userScore.Statistics }); + + totalCount += 2; } addCursor(multiplayerUserScore.ScoresAround.Lower); @@ -325,6 +328,8 @@ namespace osu.Game.Tests.Visual.Playlists { HitResult.Great, 300 } } }); + + totalCount++; } addCursor(result); From f199d6c5212c360fb9c2c16b70d21d3e9730c7dd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Oct 2021 15:26:25 +0900 Subject: [PATCH 2348/2442] Fix another related test failure --- .../Visual/UserInterface/TestSceneDeleteLocalScore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index 189b143a35..9a75d3c309 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -163,7 +163,6 @@ namespace osu.Game.Tests.Visual.UserInterface }); AddUntilStep("wait for fetch", () => leaderboard.Scores != null); - AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s.OnlineScoreID != scoreBeingDeleted.OnlineScoreID)); } @@ -171,6 +170,7 @@ namespace osu.Game.Tests.Visual.UserInterface public void TestDeleteViaDatabase() { AddStep("delete top score", () => scoreManager.Delete(importedScores[0])); + AddUntilStep("wait for fetch", () => leaderboard.Scores != null); AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s.OnlineScoreID != importedScores[0].OnlineScoreID)); } } From 95c67a594bbb693d1a5eccdba22ae2dcf425eeaa Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Oct 2021 18:23:18 +0900 Subject: [PATCH 2349/2442] Fix tests --- .../OsuDifficultyCalculatorTest.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index 19881b5c33..15675e74d1 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -15,13 +15,13 @@ namespace osu.Game.Rulesets.Osu.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; - [TestCase(6.6634445062299665d, "diffcalc-test")] - [TestCase(1.0414203870195022d, "zero-length-sliders")] + [TestCase(6.5867229481955389d, "diffcalc-test")] + [TestCase(1.0416315570967911d, "zero-length-sliders")] public void Test(double expected, string name) => base.Test(expected, name); - [TestCase(8.3858089051603368d, "diffcalc-test")] - [TestCase(1.2723279173428435d, "zero-length-sliders")] + [TestCase(8.2730989071947896d, "diffcalc-test")] + [TestCase(1.2726413186221039d, "zero-length-sliders")] public void TestClockRateAdjusted(double expected, string name) => Test(expected, name, new OsuModDoubleTime()); From f0affa9f5af37796d822c0e8c9b6ad408ebe0636 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Oct 2021 19:50:31 +0900 Subject: [PATCH 2350/2442] Don't refer to BeatmapInfo --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index c236b3750b..b1c628d273 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty double starRating = basePerformance > 0.00001 ? Math.Cbrt(1.12) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0; double preempt = (int)IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate; - double drainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate; + double drainRate = beatmap.Difficulty.DrainRate; int maxCombo = beatmap.HitObjects.Count; // Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above) From 06a78d9729a17f119c18f455b9a1a3e30342e8e8 Mon Sep 17 00:00:00 2001 From: Joseph Ireland Date: Sat, 9 Oct 2021 00:15:27 +0100 Subject: [PATCH 2351/2442] fix taiko tests --- .../TaikoDifficultyCalculatorTest.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs index 0ca5e1bb08..4b0b74ad27 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs @@ -14,13 +14,13 @@ namespace osu.Game.Rulesets.Taiko.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko"; - [TestCase(2.2593624565103561d, "diffcalc-test")] - [TestCase(2.2593624565103561d, "diffcalc-test-strong")] + [TestCase(2.2420075288523802d, "diffcalc-test")] + [TestCase(2.2420075288523802d, "diffcalc-test-strong")] public void Test(double expected, string name) => base.Test(expected, name); - [TestCase(3.1518486708786382d, "diffcalc-test")] - [TestCase(3.1518486708786382d, "diffcalc-test-strong")] + [TestCase(3.134084469440479d, "diffcalc-test")] + [TestCase(3.134084469440479d, "diffcalc-test-strong")] public void TestClockRateAdjusted(double expected, string name) => Test(expected, name, new TaikoModDoubleTime()); From 37632fe4dcacf2ea839005b33ac62017dfc21502 Mon Sep 17 00:00:00 2001 From: StanR Date: Sat, 9 Oct 2021 12:08:57 +0300 Subject: [PATCH 2352/2442] Remove int casts in difficulty calculation, fixup some comments --- .../Difficulty/OsuDifficultyCalculator.cs | 5 ++--- .../Difficulty/OsuPerformanceCalculator.cs | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index b1c628d273..790aa0eb7d 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty double starRating = basePerformance > 0.00001 ? Math.Cbrt(1.12) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0; - double preempt = (int)IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate; + double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate; double drainRate = beatmap.Difficulty.DrainRate; int maxCombo = beatmap.HitObjects.Count; @@ -102,8 +102,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty HitWindows hitWindows = new OsuHitWindows(); hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty); - // Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future - hitWindowGreat = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate; + hitWindowGreat = hitWindows.WindowFor(HitResult.Great) / clockRate; return new Skill[] { diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index f868b5e1ce..4e4dbc02a1 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -40,9 +40,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty countMeh = Score.Statistics.GetValueOrDefault(HitResult.Meh); countMiss = Score.Statistics.GetValueOrDefault(HitResult.Miss); - // Custom multipliers for NoFail and SpunOut. double multiplier = 1.12; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things. + // Custom multipliers for NoFail and SpunOut. if (mods.Any(m => m is OsuModNoFail)) multiplier *= Math.Max(0.90, 1.0 - 0.02 * countMiss); @@ -114,11 +114,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty double approachRateBonus = 1.0 + (0.03 + 0.37 * approachRateTotalHitsFactor) * approachRateFactor; - // We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR. if (mods.Any(m => m is OsuModBlinds)) aimValue *= 1.3 + (totalHits * (0.0016 / (1 + 2 * countMiss)) * Math.Pow(accuracy, 16)) * (1 - 0.003 * Attributes.DrainRate * Attributes.DrainRate); else if (mods.Any(h => h is OsuModHidden)) + { + // We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR. aimValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); + } aimValue *= approachRateBonus; @@ -155,14 +157,20 @@ namespace osu.Game.Rulesets.Osu.Difficulty speedValue *= 1.0 + (0.03 + 0.37 * approachRateTotalHitsFactor) * approachRateFactor; - // Increasing the speed value by object count for Blinds isn't ideal, so the minimum buff is given. if (mods.Any(m => m is OsuModBlinds)) + { + // Increasing the speed value by object count for Blinds isn't ideal, so the minimum buff is given. speedValue *= 1.12; + } else if (mods.Any(m => m is OsuModHidden)) + { + // We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR. speedValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); + } // Scale the speed value with accuracy and OD. speedValue *= (0.95 + Math.Pow(Attributes.OverallDifficulty, 2) / 750) * Math.Pow(accuracy, (14.5 - Math.Max(Attributes.OverallDifficulty, 8)) / 2); + // Scale the speed value with # of 50s to punish doubletapping. speedValue *= Math.Pow(0.98, countMeh < totalHits / 500.0 ? 0 : countMeh - totalHits / 500.0); @@ -199,6 +207,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty accuracyValue *= 1.14; else if (mods.Any(m => m is OsuModHidden)) accuracyValue *= 1.08; + if (mods.Any(m => m is OsuModFlashlight)) accuracyValue *= 1.02; From 8007ba93eb31b86808cdd91bb985b815793aa088 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 9 Oct 2021 14:25:40 +0200 Subject: [PATCH 2353/2442] Fix typo in `TaikoMultiplierAppliedDifficulty` class name --- .../Beatmaps/TaikoBeatmapConverter.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index ef8cf5cb53..50c0ca7f55 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -47,10 +47,10 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps protected override Beatmap ConvertBeatmap(IBeatmap original, CancellationToken cancellationToken) { - if (!(original.Difficulty is TaikoMutliplierAppliedDifficulty)) + if (!(original.Difficulty is TaikoMultiplierAppliedDifficulty)) { // Rewrite the beatmap info to add the slider velocity multiplier - original.Difficulty = new TaikoMutliplierAppliedDifficulty(original.Difficulty); + original.Difficulty = new TaikoMultiplierAppliedDifficulty(original.Difficulty); } Beatmap converted = base.ConvertBeatmap(original, cancellationToken); @@ -191,15 +191,15 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps protected override Beatmap CreateBeatmap() => new TaikoBeatmap(); - private class TaikoMutliplierAppliedDifficulty : BeatmapDifficulty + private class TaikoMultiplierAppliedDifficulty : BeatmapDifficulty { - public TaikoMutliplierAppliedDifficulty(IBeatmapDifficultyInfo difficulty) + public TaikoMultiplierAppliedDifficulty(IBeatmapDifficultyInfo difficulty) { CopyFrom(difficulty); } [UsedImplicitly] - public TaikoMutliplierAppliedDifficulty() + public TaikoMultiplierAppliedDifficulty() { } @@ -208,14 +208,14 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps public override void CopyTo(BeatmapDifficulty other) { base.CopyTo(other); - if (!(other is TaikoMutliplierAppliedDifficulty)) + if (!(other is TaikoMultiplierAppliedDifficulty)) SliderMultiplier /= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; } public override void CopyFrom(IBeatmapDifficultyInfo other) { base.CopyFrom(other); - if (!(other is TaikoMutliplierAppliedDifficulty)) + if (!(other is TaikoMultiplierAppliedDifficulty)) SliderMultiplier *= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; } From 237c06301507da05454283ff3bbd492a713f9e21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 9 Oct 2021 14:26:32 +0200 Subject: [PATCH 2354/2442] Fix typo in `multipleIncrementMods` variable name --- .../Visual/UserInterface/TestSceneFooterButtonMods.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonMods.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonMods.cs index 546e905ded..8d1572769f 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonMods.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonMods.cs @@ -36,9 +36,9 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep(@"Add DoubleTime", () => changeMods(doubleTimeMod)); AddAssert(@"Check DoubleTime multiplier", () => assertModsMultiplier(doubleTimeMod)); - var mutlipleIncrementMods = new Mod[] { new OsuModDoubleTime(), new OsuModHidden(), new OsuModHardRock() }; - AddStep(@"Add multiple Mods", () => changeMods(mutlipleIncrementMods)); - AddAssert(@"Check multiple mod multiplier", () => assertModsMultiplier(mutlipleIncrementMods)); + var multipleIncrementMods = new Mod[] { new OsuModDoubleTime(), new OsuModHidden(), new OsuModHardRock() }; + AddStep(@"Add multiple Mods", () => changeMods(multipleIncrementMods)); + AddAssert(@"Check multiple mod multiplier", () => assertModsMultiplier(multipleIncrementMods)); } [Test] From f6df93f0139af65cf3cd5d87603fc8fcf31f7524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 3 Oct 2021 17:30:22 +0200 Subject: [PATCH 2355/2442] Introduce basic parts of colour scheme to settings sidebar --- osu.Game/Overlays/Settings/SettingsSection.cs | 7 +- osu.Game/Overlays/Settings/Sidebar.cs | 14 +++- osu.Game/Overlays/Settings/SidebarButton.cs | 78 +++++++++++++------ osu.Game/Overlays/SettingsPanel.cs | 12 +-- 4 files changed, 76 insertions(+), 35 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index 2a6f3f5ed7..33c0e9cafa 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -12,7 +12,6 @@ using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osuTK.Graphics; namespace osu.Game.Overlays.Settings { @@ -33,7 +32,7 @@ namespace osu.Game.Overlays.Settings private const int header_size = 26; private const int margin = 20; - private const int border_size = 2; + private const int border_size = 4; public bool MatchingFilter { @@ -63,14 +62,14 @@ namespace osu.Game.Overlays.Settings } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OverlayColourProvider colourProvider, OsuColour colours) { AddRangeInternal(new Drawable[] { new Box { Name = "separator", - Colour = new Color4(0, 0, 0, 255), + Colour = colourProvider.Background6, RelativeSizeAxes = Axes.X, Height = border_size, }, diff --git a/osu.Game/Overlays/Settings/Sidebar.cs b/osu.Game/Overlays/Settings/Sidebar.cs index 4ca6e2ec42..49585b3afb 100644 --- a/osu.Game/Overlays/Settings/Sidebar.cs +++ b/osu.Game/Overlays/Settings/Sidebar.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using osu.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -17,8 +18,9 @@ namespace osu.Game.Overlays.Settings { public class Sidebar : Container, IStateful { + private readonly Box background; private readonly FillFlowContainer content; - public const float DEFAULT_WIDTH = Toolbar.Toolbar.HEIGHT * 1.4f; + public const float DEFAULT_WIDTH = 70; public const int EXPANDED_WIDTH = 200; public event Action StateChanged; @@ -30,7 +32,7 @@ namespace osu.Game.Overlays.Settings RelativeSizeAxes = Axes.Y; InternalChildren = new Drawable[] { - new Box + background = new Box { Colour = OsuColour.Gray(0.02f), RelativeSizeAxes = Axes.Both, @@ -52,6 +54,12 @@ namespace osu.Game.Overlays.Settings }; } + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + background.Colour = colourProvider.Background5; + } + private ScheduledDelegate expandEvent; private ExpandedState state; @@ -80,8 +88,6 @@ namespace osu.Game.Overlays.Settings { public SidebarScrollContainer() { - Content.Anchor = Anchor.CentreLeft; - Content.Origin = Anchor.CentreLeft; RelativeSizeAxes = Axes.Both; ScrollbarVisible = false; } diff --git a/osu.Game/Overlays/Settings/SidebarButton.cs b/osu.Game/Overlays/Settings/SidebarButton.cs index cf6a313a1f..a69b163998 100644 --- a/osu.Game/Overlays/Settings/SidebarButton.cs +++ b/osu.Game/Overlays/Settings/SidebarButton.cs @@ -2,12 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using osuTK; -using osuTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -17,11 +17,16 @@ namespace osu.Game.Overlays.Settings { public class SidebarButton : OsuButton { + private const double fade_duration = 50; + private readonly ConstrainedIconContainer iconContainer; private readonly SpriteText headerText; - private readonly Box selectionIndicator; + private readonly CircularContainer selectionIndicator; private readonly Container text; + [Resolved] + private OverlayColourProvider colourProvider { get; set; } + // always consider as part of flow, even when not visible (for the sake of the initial animation). public override bool IsPresent => true; @@ -47,25 +52,15 @@ namespace osu.Game.Overlays.Settings { selected = value; - if (selected) - { - selectionIndicator.FadeIn(50); - text.FadeColour(Color4.White, 50); - } - else - { - selectionIndicator.FadeOut(50); - text.FadeColour(OsuColour.Gray(0.6f), 50); - } + if (IsLoaded) + updateState(); } } public SidebarButton() { - Height = Sidebar.DEFAULT_WIDTH; RelativeSizeAxes = Axes.X; - - BackgroundColour = Color4.Black; + Height = 46; AddRange(new Drawable[] { @@ -90,21 +85,60 @@ namespace osu.Game.Overlays.Settings }, } }, - selectionIndicator = new Box + selectionIndicator = new CircularContainer { Alpha = 0, - RelativeSizeAxes = Axes.Y, - Width = 5, - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, + Width = 3, + Height = 18, + Masking = true, + CornerRadius = 1.5f, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding + { + Left = 9, + }, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Colour4.White + } }, }); } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load() { - selectionIndicator.Colour = colours.Yellow; + BackgroundColour = colourProvider.Background5; + selectionIndicator.Colour = colourProvider.Highlight1; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + updateState(); + } + + protected override bool OnHover(HoverEvent e) + { + updateState(); + return false; + } + + protected override void OnHoverLost(HoverLostEvent e) => updateState(); + + private void updateState() + { + if (Selected) + { + text.FadeColour(colourProvider.Content1, fade_duration, Easing.OutQuint); + selectionIndicator.FadeIn(fade_duration, Easing.OutQuint); + return; + } + + text.FadeColour(IsHovered ? colourProvider.Light1 : colourProvider.Light3, fade_duration, Easing.OutQuint); + selectionIndicator.FadeOut(fade_duration, Easing.OutQuint); } } } diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index bda4bb5ece..11bfb91703 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using osuTK; -using osuTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; @@ -15,7 +14,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; -using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; @@ -64,6 +62,9 @@ namespace osu.Game.Overlays public IBindable CurrentSection = new Bindable(); + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + protected SettingsPanel(bool showSidebar) { this.showSidebar = showSidebar; @@ -89,7 +90,7 @@ namespace osu.Game.Overlays Origin = Anchor.TopRight, Scale = new Vector2(2, 1), // over-extend to the left for transitions RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(0.05f), + Colour = colourProvider.Background4, Alpha = 1, }, loading = new LoadingLayer @@ -292,11 +293,12 @@ namespace osu.Game.Overlays Direction = FillDirection.Vertical, }; - public SettingsSectionsContainer() + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) { HeaderBackground = new Box { - Colour = Color4.Black, + Colour = colourProvider.Background4, RelativeSizeAxes = Axes.Both }; } From 315581f4c8c09a132481eb2322d68388c008b03d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 9 Oct 2021 19:18:29 +0200 Subject: [PATCH 2356/2442] Adjust horizontal spacing in settings panel --- osu.Game/Overlays/SettingsPanel.cs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index 11bfb91703..7c43875bef 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -23,7 +23,7 @@ namespace osu.Game.Overlays [Cached] public abstract class SettingsPanel : OsuFocusedOverlayContainer { - public const float CONTENT_MARGINS = 15; + public const float CONTENT_MARGINS = 20; public const float TRANSITION_LENGTH = 600; @@ -106,17 +106,23 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both, ExpandableHeader = CreateHeader(), SelectedSection = { BindTarget = CurrentSection }, - FixedHeader = searchTextBox = new SeekLimitedSearchTextBox + FixedHeader = new Container { RelativeSizeAxes = Axes.X, - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Width = 0.95f, - Margin = new MarginPadding + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { - Top = 20, - Bottom = 20 + Vertical = 20, + Horizontal = CONTENT_MARGINS }, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Child = searchTextBox = new SeekLimitedSearchTextBox + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + } }, Footer = CreateFooter().With(f => f.Alpha = 0) }); From 4c293b637f8219cfff0f2dcd22021f089760488e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 9 Oct 2021 19:20:44 +0200 Subject: [PATCH 2357/2442] Restyle settings panel header --- osu.Game/Overlays/Settings/SettingsHeader.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsHeader.cs b/osu.Game/Overlays/Settings/SettingsHeader.cs index a7f1cef74c..69b7b69a29 100644 --- a/osu.Game/Overlays/Settings/SettingsHeader.cs +++ b/osu.Game/Overlays/Settings/SettingsHeader.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Settings } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OverlayColourProvider colourProvider) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; @@ -39,7 +39,7 @@ namespace osu.Game.Overlays.Settings new OsuSpriteText { Text = heading, - Font = OsuFont.GetFont(size: 40), + Font = OsuFont.TorusAlternate.With(size: 40), Margin = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS, @@ -48,7 +48,7 @@ namespace osu.Game.Overlays.Settings }, new OsuSpriteText { - Colour = colours.Pink, + Colour = colourProvider.Content2, Text = subheading, Font = OsuFont.GetFont(size: 18), Margin = new MarginPadding From e23a54f1e66149de2fa39c45e6baefaf0599184f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 9 Oct 2021 19:25:24 +0200 Subject: [PATCH 2358/2442] Adjust setting section appearance & spacings --- osu.Game/Overlays/Settings/SettingsSection.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index 33c0e9cafa..0381afe5ff 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -30,8 +30,7 @@ namespace osu.Game.Overlays.Settings public IEnumerable FilterableChildren => Children.OfType(); public virtual IEnumerable FilterTerms => new[] { Header.ToString() }; - private const int header_size = 26; - private const int margin = 20; + private const int header_size = 24; private const int border_size = 4; public bool MatchingFilter @@ -77,8 +76,8 @@ namespace osu.Game.Overlays.Settings { Padding = new MarginPadding { - Top = margin + border_size, - Bottom = margin + 10, + Top = 28, + Bottom = 40, }, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -86,13 +85,11 @@ namespace osu.Game.Overlays.Settings { header = new OsuSpriteText { - Font = OsuFont.GetFont(size: header_size), + Font = OsuFont.TorusAlternate.With(size: header_size), Text = Header, - Colour = colours.Yellow, Margin = new MarginPadding { - Left = SettingsPanel.CONTENT_MARGINS, - Right = SettingsPanel.CONTENT_MARGINS + Horizontal = SettingsPanel.CONTENT_MARGINS } }, FlowContent From 855a74b8a075dcb66bb7652fd5fa72cc63297717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 9 Oct 2021 19:54:20 +0200 Subject: [PATCH 2359/2442] Adjust vertical spacings in individual subsections --- .../Settings/Sections/Input/TabletSettings.cs | 8 +++++++- .../Overlays/Settings/Sections/MaintenanceSection.cs | 2 -- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 3 --- osu.Game/Overlays/Settings/SettingsSection.cs | 6 +++++- osu.Game/Overlays/Settings/SettingsSubsection.cs | 12 ++++++++---- 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index 8c60e81fb5..c94b418331 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -165,7 +165,13 @@ namespace osu.Game.Overlays.Settings.Sections.Input LabelText = TabletSettingsStrings.Rotation, Current = rotation }, - new RotationPresetButtons(tabletHandler), + new RotationPresetButtons(tabletHandler) + { + Padding = new MarginPadding + { + Horizontal = SettingsPanel.CONTENT_MARGINS + } + }, new SettingsSlider { TransferValueOnCommit = true, diff --git a/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs b/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs index fa0c06167b..9410a87848 100644 --- a/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs +++ b/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs @@ -6,7 +6,6 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Localisation; using osu.Game.Overlays.Settings.Sections.Maintenance; -using osuTK; namespace osu.Game.Overlays.Settings.Sections { @@ -21,7 +20,6 @@ namespace osu.Game.Overlays.Settings.Sections public MaintenanceSection() { - FlowContent.Spacing = new Vector2(0, 5); Children = new Drawable[] { new GeneralSettings() diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index e0d8252930..d18099eb0a 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -16,7 +16,6 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; using osu.Game.Skinning; using osu.Game.Skinning.Editor; -using osuTK; namespace osu.Game.Overlays.Settings.Sections { @@ -63,8 +62,6 @@ namespace osu.Game.Overlays.Settings.Sections [BackgroundDependencyLoader(permitNulls: true)] private void load(OsuConfigManager config, [CanBeNull] SkinEditorOverlay skinEditor) { - FlowContent.Spacing = new Vector2(0, 5); - Children = new Drawable[] { skinDropdown = new SkinSettingsDropdown(), diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index 0381afe5ff..0ae353602e 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -12,6 +12,7 @@ using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osuTK; namespace osu.Game.Overlays.Settings { @@ -30,6 +31,8 @@ namespace osu.Game.Overlays.Settings public IEnumerable FilterableChildren => Children.OfType(); public virtual IEnumerable FilterTerms => new[] { Header.ToString() }; + public const int ITEM_SPACING = 14; + private const int header_size = 24; private const int border_size = 4; @@ -52,8 +55,9 @@ namespace osu.Game.Overlays.Settings { Margin = new MarginPadding { - Top = header_size + Top = 36 }, + Spacing = new Vector2(0, ITEM_SPACING), Direction = FillDirection.Vertical, AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Overlays/Settings/SettingsSubsection.cs b/osu.Game/Overlays/Settings/SettingsSubsection.cs index 4aa9360452..c2cf08ac98 100644 --- a/osu.Game/Overlays/Settings/SettingsSubsection.cs +++ b/osu.Game/Overlays/Settings/SettingsSubsection.cs @@ -46,13 +46,17 @@ namespace osu.Game.Overlays.Settings FlowContent = new FillFlowContainer { + Margin = new MarginPadding { Top = SettingsSection.ITEM_SPACING }, Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 8), + Spacing = new Vector2(0, SettingsSection.ITEM_SPACING), RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, }; } + private const int header_height = 43; + private const int header_font_size = 20; + [BackgroundDependencyLoader] private void load() { @@ -60,9 +64,9 @@ namespace osu.Game.Overlays.Settings { new OsuSpriteText { - Text = Header.ToString().ToUpper(), // TODO: Add localisation support after https://github.com/ppy/osu-framework/pull/4603 is merged. - Margin = new MarginPadding { Vertical = 30, Left = SettingsPanel.CONTENT_MARGINS, Right = SettingsPanel.CONTENT_MARGINS }, - Font = OsuFont.GetFont(weight: FontWeight.Bold), + Text = Header, + Margin = new MarginPadding { Vertical = (header_height - header_font_size) * 0.5f, Horizontal = SettingsPanel.CONTENT_MARGINS }, + Font = OsuFont.GetFont(size: header_font_size), }, FlowContent }); From b8616bf910335bf549522d1b09d38aa036f27619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 9 Oct 2021 20:29:44 +0200 Subject: [PATCH 2360/2442] Adjust appearance of keybinding subpanel --- .../Settings/Sections/Input/KeyBindingRow.cs | 21 +++++++++---------- .../Sections/Input/KeyBindingsSubsection.cs | 3 +-- osu.Game/Overlays/SettingsSubPanel.cs | 7 +++++-- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs index cf8adf2785..da789db79a 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs @@ -78,7 +78,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input private RealmContextFactory realmFactory { get; set; } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OverlayColourProvider colourProvider) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; @@ -101,7 +101,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input EdgeEffect = new EdgeEffectParameters { Radius = 2, - Colour = colours.YellowDark.Opacity(0), + Colour = colourProvider.Highlight1.Opacity(0), Type = EdgeEffectType.Shadow, Hollow = true, }, @@ -110,13 +110,12 @@ namespace osu.Game.Overlays.Settings.Sections.Input new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.6f, + Colour = colourProvider.Background5, }, text = new OsuSpriteText { Text = action.GetLocalisableDescription(), - Margin = new MarginPadding(padding), + Margin = new MarginPadding(1.5f * padding), }, buttons = new FillFlowContainer { @@ -405,7 +404,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input private readonly Box box; public readonly OsuSpriteText Text; - private Color4 hoverColour; + [Resolved] + private OverlayColourProvider colourProvider { get; set; } private bool isBinding; @@ -448,7 +448,6 @@ namespace osu.Game.Overlays.Settings.Sections.Input box = new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4.Black }, Text = new OsuSpriteText { @@ -463,9 +462,9 @@ namespace osu.Game.Overlays.Settings.Sections.Input } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load() { - hoverColour = colours.YellowDark; + updateHoverState(); } protected override bool OnHover(HoverEvent e) @@ -484,12 +483,12 @@ namespace osu.Game.Overlays.Settings.Sections.Input { if (isBinding) { - box.FadeColour(Color4.White, transition_time, Easing.OutQuint); + box.FadeColour(colourProvider.Light2, transition_time, Easing.OutQuint); Text.FadeColour(Color4.Black, transition_time, Easing.OutQuint); } else { - box.FadeColour(IsHovered ? hoverColour : Color4.Black, transition_time, Easing.OutQuint); + box.FadeColour(IsHovered ? colourProvider.Light4 : colourProvider.Background6, transition_time, Easing.OutQuint); Text.FadeColour(IsHovered ? Color4.Black : Color4.White, transition_time, Easing.OutQuint); } } diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs index 2cc2857e9b..39dddbe1e6 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs @@ -27,8 +27,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input { this.variant = variant; - FlowContent.Spacing = new Vector2(0, 1); - FlowContent.Padding = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS, Right = SettingsPanel.CONTENT_MARGINS }; + FlowContent.Spacing = new Vector2(0, 3); } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/SettingsSubPanel.cs b/osu.Game/Overlays/SettingsSubPanel.cs index 1fa233d9d4..e63dcd299a 100644 --- a/osu.Game/Overlays/SettingsSubPanel.cs +++ b/osu.Game/Overlays/SettingsSubPanel.cs @@ -10,7 +10,6 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; using osuTK; -using osuTK.Graphics; namespace osu.Game.Overlays { @@ -36,12 +35,16 @@ namespace osu.Game.Overlays private class BackButton : OsuButton { + [Resolved] + private OverlayColourProvider colourProvider { get; set; } + [BackgroundDependencyLoader] private void load() { Size = new Vector2(Sidebar.DEFAULT_WIDTH); - BackgroundColour = Color4.Black; + BackgroundColour = colourProvider.Background5; + Hover.Colour = Colour4.Transparent; AddRange(new Drawable[] { From fe26d8e8dfce3be34da60ef380d2d3184a76e7f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 9 Oct 2021 20:43:18 +0200 Subject: [PATCH 2361/2442] Share sidebar colouring logic by splitting out `SidebarIconButton` --- osu.Game/Overlays/Settings/Sidebar.cs | 8 +- osu.Game/Overlays/Settings/SidebarButton.cs | 120 ++--------------- .../Overlays/Settings/SidebarIconButton.cs | 126 ++++++++++++++++++ osu.Game/Overlays/SettingsPanel.cs | 6 +- osu.Game/Overlays/SettingsSubPanel.cs | 13 +- 5 files changed, 149 insertions(+), 124 deletions(-) create mode 100644 osu.Game/Overlays/Settings/SidebarIconButton.cs diff --git a/osu.Game/Overlays/Settings/Sidebar.cs b/osu.Game/Overlays/Settings/Sidebar.cs index 49585b3afb..93b1b19b17 100644 --- a/osu.Game/Overlays/Settings/Sidebar.cs +++ b/osu.Game/Overlays/Settings/Sidebar.cs @@ -16,16 +16,16 @@ using osuTK; namespace osu.Game.Overlays.Settings { - public class Sidebar : Container, IStateful + public class Sidebar : Container, IStateful { private readonly Box background; - private readonly FillFlowContainer content; + private readonly FillFlowContainer content; public const float DEFAULT_WIDTH = 70; public const int EXPANDED_WIDTH = 200; public event Action StateChanged; - protected override Container Content => content; + protected override Container Content => content; public Sidebar() { @@ -41,7 +41,7 @@ namespace osu.Game.Overlays.Settings { Children = new[] { - content = new FillFlowContainer + content = new FillFlowContainer { Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, diff --git a/osu.Game/Overlays/Settings/SidebarButton.cs b/osu.Game/Overlays/Settings/SidebarButton.cs index a69b163998..197187e68c 100644 --- a/osu.Game/Overlays/Settings/SidebarButton.cs +++ b/osu.Game/Overlays/Settings/SidebarButton.cs @@ -1,144 +1,46 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osuTK; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Settings { - public class SidebarButton : OsuButton + public abstract class SidebarButton : OsuButton { private const double fade_duration = 50; - private readonly ConstrainedIconContainer iconContainer; - private readonly SpriteText headerText; - private readonly CircularContainer selectionIndicator; - private readonly Container text; - [Resolved] - private OverlayColourProvider colourProvider { get; set; } + protected OverlayColourProvider ColourProvider { get; private set; } - // always consider as part of flow, even when not visible (for the sake of the initial animation). - public override bool IsPresent => true; - - private SettingsSection section; - - public SettingsSection Section - { - get => section; - set - { - section = value; - headerText.Text = value.Header; - iconContainer.Icon = value.CreateIcon(); - } - } - - private bool selected; - - public bool Selected - { - get => selected; - set - { - selected = value; - - if (IsLoaded) - updateState(); - } - } - - public SidebarButton() - { - RelativeSizeAxes = Axes.X; - Height = 46; - - AddRange(new Drawable[] - { - text = new Container - { - Width = Sidebar.DEFAULT_WIDTH, - RelativeSizeAxes = Axes.Y, - Colour = OsuColour.Gray(0.6f), - Children = new Drawable[] - { - headerText = new OsuSpriteText - { - Position = new Vector2(Sidebar.DEFAULT_WIDTH + 10, 0), - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - }, - iconContainer = new ConstrainedIconContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(20), - }, - } - }, - selectionIndicator = new CircularContainer - { - Alpha = 0, - Width = 3, - Height = 18, - Masking = true, - CornerRadius = 1.5f, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Margin = new MarginPadding - { - Left = 9, - }, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Colour4.White - } - }, - }); - } + protected abstract Drawable HoverTarget { get; } [BackgroundDependencyLoader] private void load() { - BackgroundColour = colourProvider.Background5; - selectionIndicator.Colour = colourProvider.Highlight1; + BackgroundColour = ColourProvider.Background5; } protected override void LoadComplete() { base.LoadComplete(); - updateState(); + UpdateState(); + FinishTransforms(true); } protected override bool OnHover(HoverEvent e) { - updateState(); + UpdateState(); return false; } - protected override void OnHoverLost(HoverLostEvent e) => updateState(); + protected override void OnHoverLost(HoverLostEvent e) => UpdateState(); - private void updateState() + protected virtual void UpdateState() { - if (Selected) - { - text.FadeColour(colourProvider.Content1, fade_duration, Easing.OutQuint); - selectionIndicator.FadeIn(fade_duration, Easing.OutQuint); - return; - } - - text.FadeColour(IsHovered ? colourProvider.Light1 : colourProvider.Light3, fade_duration, Easing.OutQuint); - selectionIndicator.FadeOut(fade_duration, Easing.OutQuint); + HoverTarget.FadeColour(IsHovered ? ColourProvider.Light1 : ColourProvider.Light3, fade_duration, Easing.OutQuint); } } } diff --git a/osu.Game/Overlays/Settings/SidebarIconButton.cs b/osu.Game/Overlays/Settings/SidebarIconButton.cs new file mode 100644 index 0000000000..db580f4766 --- /dev/null +++ b/osu.Game/Overlays/Settings/SidebarIconButton.cs @@ -0,0 +1,126 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osuTK; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Overlays.Settings +{ + public class SidebarIconButton : SidebarButton + { + private const double fade_duration = 50; + + private readonly ConstrainedIconContainer iconContainer; + private readonly SpriteText headerText; + private readonly CircularContainer selectionIndicator; + private readonly Container text; + + protected override Drawable HoverTarget => text; + + // always consider as part of flow, even when not visible (for the sake of the initial animation). + public override bool IsPresent => true; + + private SettingsSection section; + + public SettingsSection Section + { + get => section; + set + { + section = value; + headerText.Text = value.Header; + iconContainer.Icon = value.CreateIcon(); + } + } + + private bool selected; + + public bool Selected + { + get => selected; + set + { + selected = value; + + if (IsLoaded) + UpdateState(); + } + } + + public SidebarIconButton() + { + RelativeSizeAxes = Axes.X; + Height = 46; + + AddRange(new Drawable[] + { + text = new Container + { + Width = Sidebar.DEFAULT_WIDTH, + RelativeSizeAxes = Axes.Y, + Colour = OsuColour.Gray(0.6f), + Children = new Drawable[] + { + headerText = new OsuSpriteText + { + Position = new Vector2(Sidebar.DEFAULT_WIDTH + 10, 0), + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + iconContainer = new ConstrainedIconContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(20), + }, + } + }, + selectionIndicator = new CircularContainer + { + Alpha = 0, + Width = 3, + Height = 18, + Masking = true, + CornerRadius = 1.5f, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding + { + Left = 9, + }, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Colour4.White + } + }, + }); + } + + [BackgroundDependencyLoader] + private void load() + { + selectionIndicator.Colour = ColourProvider.Highlight1; + } + + protected override void UpdateState() + { + if (Selected) + { + text.FadeColour(ColourProvider.Content1, fade_duration, Easing.OutQuint); + selectionIndicator.FadeIn(fade_duration, Easing.OutQuint); + return; + } + + selectionIndicator.FadeOut(fade_duration, Easing.OutQuint); + base.UpdateState(); + } + } +} diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index 7c43875bef..0ceb7fc50d 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -44,7 +44,7 @@ namespace osu.Game.Overlays protected override Container Content => ContentContainer; protected Sidebar Sidebar; - private SidebarButton selectedSidebarButton; + private SidebarIconButton selectedSidebarButton; public SettingsSectionsContainer SectionsContainer { get; private set; } @@ -252,11 +252,11 @@ namespace osu.Game.Overlays }); } - private IEnumerable createSidebarButtons() + private IEnumerable createSidebarButtons() { foreach (var section in SectionsContainer) { - yield return new SidebarButton + yield return new SidebarIconButton { Section = section, Action = () => diff --git a/osu.Game/Overlays/SettingsSubPanel.cs b/osu.Game/Overlays/SettingsSubPanel.cs index e63dcd299a..25a245472b 100644 --- a/osu.Game/Overlays/SettingsSubPanel.cs +++ b/osu.Game/Overlays/SettingsSubPanel.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; using osuTK; @@ -33,22 +32,20 @@ namespace osu.Game.Overlays protected override bool DimMainContent => false; // dimming is handled by main overlay - private class BackButton : OsuButton + private class BackButton : SidebarButton { - [Resolved] - private OverlayColourProvider colourProvider { get; set; } + private Container content; + + protected override Drawable HoverTarget => content; [BackgroundDependencyLoader] private void load() { Size = new Vector2(Sidebar.DEFAULT_WIDTH); - BackgroundColour = colourProvider.Background5; - Hover.Colour = Colour4.Transparent; - AddRange(new Drawable[] { - new Container + content = new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, From ccc6d8ff40bf04302b73c390b4b0c7b899644fdf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 10 Oct 2021 11:34:01 +0900 Subject: [PATCH 2362/2442] Improve the animation of the active indicator --- osu.Game/Overlays/Settings/SidebarIconButton.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Settings/SidebarIconButton.cs b/osu.Game/Overlays/Settings/SidebarIconButton.cs index db580f4766..d09873c3ea 100644 --- a/osu.Game/Overlays/Settings/SidebarIconButton.cs +++ b/osu.Game/Overlays/Settings/SidebarIconButton.cs @@ -15,7 +15,10 @@ namespace osu.Game.Overlays.Settings { public class SidebarIconButton : SidebarButton { - private const double fade_duration = 50; + private const double fade_duration = 500; + + private const float selection_indicator_height_active = 18; + private const float selection_indicator_height_inactive = 4; private readonly ConstrainedIconContainer iconContainer; private readonly SpriteText headerText; @@ -85,8 +88,8 @@ namespace osu.Game.Overlays.Settings selectionIndicator = new CircularContainer { Alpha = 0, - Width = 3, - Height = 18, + Width = 4, + Height = selection_indicator_height_inactive, Masking = true, CornerRadius = 1.5f, Anchor = Anchor.CentreLeft, @@ -116,10 +119,12 @@ namespace osu.Game.Overlays.Settings { text.FadeColour(ColourProvider.Content1, fade_duration, Easing.OutQuint); selectionIndicator.FadeIn(fade_duration, Easing.OutQuint); + selectionIndicator.ResizeHeightTo(selection_indicator_height_active, fade_duration, Easing.OutElasticHalf); return; } selectionIndicator.FadeOut(fade_duration, Easing.OutQuint); + selectionIndicator.ResizeHeightTo(selection_indicator_height_inactive, fade_duration, Easing.OutQuint); base.UpdateState(); } } From 49b341daffb677dd5de1c5d6d44101ac029d854b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 10 Oct 2021 11:55:45 +0900 Subject: [PATCH 2363/2442] Remove `HoverTarget` shared state update path Felt quite convoluted to follow. Have just duplicated the single shared line instead. --- osu.Game/Overlays/Settings/SidebarButton.cs | 10 ++----- .../Overlays/Settings/SidebarIconButton.cs | 27 +++++++++---------- osu.Game/Overlays/SettingsSubPanel.cs | 7 +++-- 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/osu.Game/Overlays/Settings/SidebarButton.cs b/osu.Game/Overlays/Settings/SidebarButton.cs index 197187e68c..1a34143e1f 100644 --- a/osu.Game/Overlays/Settings/SidebarButton.cs +++ b/osu.Game/Overlays/Settings/SidebarButton.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Graphics; using osu.Framework.Input.Events; using osu.Game.Graphics.UserInterface; @@ -10,13 +9,11 @@ namespace osu.Game.Overlays.Settings { public abstract class SidebarButton : OsuButton { - private const double fade_duration = 50; + protected const double FADE_DURATION = 500; [Resolved] protected OverlayColourProvider ColourProvider { get; private set; } - protected abstract Drawable HoverTarget { get; } - [BackgroundDependencyLoader] private void load() { @@ -38,9 +35,6 @@ namespace osu.Game.Overlays.Settings protected override void OnHoverLost(HoverLostEvent e) => UpdateState(); - protected virtual void UpdateState() - { - HoverTarget.FadeColour(IsHovered ? ColourProvider.Light1 : ColourProvider.Light3, fade_duration, Easing.OutQuint); - } + protected abstract void UpdateState(); } } diff --git a/osu.Game/Overlays/Settings/SidebarIconButton.cs b/osu.Game/Overlays/Settings/SidebarIconButton.cs index d09873c3ea..fd57996b1b 100644 --- a/osu.Game/Overlays/Settings/SidebarIconButton.cs +++ b/osu.Game/Overlays/Settings/SidebarIconButton.cs @@ -15,17 +15,13 @@ namespace osu.Game.Overlays.Settings { public class SidebarIconButton : SidebarButton { - private const double fade_duration = 500; - private const float selection_indicator_height_active = 18; private const float selection_indicator_height_inactive = 4; private readonly ConstrainedIconContainer iconContainer; private readonly SpriteText headerText; private readonly CircularContainer selectionIndicator; - private readonly Container text; - - protected override Drawable HoverTarget => text; + private readonly Container textIconContent; // always consider as part of flow, even when not visible (for the sake of the initial animation). public override bool IsPresent => true; @@ -64,7 +60,7 @@ namespace osu.Game.Overlays.Settings AddRange(new Drawable[] { - text = new Container + textIconContent = new Container { Width = Sidebar.DEFAULT_WIDTH, RelativeSizeAxes = Axes.Y, @@ -117,15 +113,18 @@ namespace osu.Game.Overlays.Settings { if (Selected) { - text.FadeColour(ColourProvider.Content1, fade_duration, Easing.OutQuint); - selectionIndicator.FadeIn(fade_duration, Easing.OutQuint); - selectionIndicator.ResizeHeightTo(selection_indicator_height_active, fade_duration, Easing.OutElasticHalf); - return; - } + textIconContent.FadeColour(ColourProvider.Content1, FADE_DURATION, Easing.OutQuint); - selectionIndicator.FadeOut(fade_duration, Easing.OutQuint); - selectionIndicator.ResizeHeightTo(selection_indicator_height_inactive, fade_duration, Easing.OutQuint); - base.UpdateState(); + selectionIndicator.FadeIn(FADE_DURATION, Easing.OutQuint); + selectionIndicator.ResizeHeightTo(selection_indicator_height_active, FADE_DURATION, Easing.OutElasticHalf); + } + else + { + textIconContent.FadeColour(IsHovered ? ColourProvider.Light1 : ColourProvider.Light3, FADE_DURATION, Easing.OutQuint); + + selectionIndicator.FadeOut(FADE_DURATION, Easing.OutQuint); + selectionIndicator.ResizeHeightTo(selection_indicator_height_inactive, FADE_DURATION, Easing.OutQuint); + } } } } diff --git a/osu.Game/Overlays/SettingsSubPanel.cs b/osu.Game/Overlays/SettingsSubPanel.cs index 25a245472b..a65d792a9f 100644 --- a/osu.Game/Overlays/SettingsSubPanel.cs +++ b/osu.Game/Overlays/SettingsSubPanel.cs @@ -36,8 +36,6 @@ namespace osu.Game.Overlays { private Container content; - protected override Drawable HoverTarget => content; - [BackgroundDependencyLoader] private void load() { @@ -71,6 +69,11 @@ namespace osu.Game.Overlays } }); } + + protected override void UpdateState() + { + content.FadeColour(IsHovered ? ColourProvider.Light1 : ColourProvider.Light3, FADE_DURATION, Easing.OutQuint); + } } } } From c49d0a501335832ecef06f02a79750cdf6c1ec97 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 10 Oct 2021 15:43:24 +0900 Subject: [PATCH 2364/2442] Rewrite query to be easier to understand --- osu.Game/Scoring/ScoreManager.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 5481eea4aa..236e8a4448 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -74,10 +74,9 @@ namespace osu.Game.Scoring var totalScores = await Task.WhenAll(scores.Select(s => GetTotalScoreAsync(s, cancellationToken: cancellationToken))).ConfigureAwait(false); - return scores.Select((s, i) => (index: i, score: s)) - .OrderByDescending(key => totalScores[key.index]) - .ThenBy(key => key.score.OnlineScoreID) - .Select(key => key.score) + return scores.Select((score, index) => (score: score, totalScore: totalScores[index])) + .OrderByDescending(g => g.totalScore) + .Select(g => g.score) .ToArray(); } From 4475697a9c55897219fc79cf032f2f3da2b5d2ee Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 10 Oct 2021 15:47:39 +0900 Subject: [PATCH 2365/2442] Add score id key --- osu.Game/Scoring/ScoreManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 236e8a4448..c5d475d631 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -76,6 +76,7 @@ namespace osu.Game.Scoring return scores.Select((score, index) => (score: score, totalScore: totalScores[index])) .OrderByDescending(g => g.totalScore) + .ThenBy(g => g.score.OnlineScoreID) .Select(g => g.score) .ToArray(); } From 49a878dc20ce004169333f86021e02f528a6cf9b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 10 Oct 2021 16:04:41 +0900 Subject: [PATCH 2366/2442] Fix comma separator support not actually working --- osu.Game/Graphics/UserInterface/ScoreCounter.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ScoreCounter.cs b/osu.Game/Graphics/UserInterface/ScoreCounter.cs index 7ebf3819e4..069810d736 100644 --- a/osu.Game/Graphics/UserInterface/ScoreCounter.cs +++ b/osu.Game/Graphics/UserInterface/ScoreCounter.cs @@ -1,6 +1,7 @@ // 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 osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Localisation; @@ -29,6 +30,9 @@ namespace osu.Game.Graphics.UserInterface { UseCommaSeparator = useCommaSeparator; + if (useCommaSeparator && leading > 0) + throw new ArgumentException("Should not mix leading zeroes and comma separators as it doesn't make sense"); + RequiredDisplayDigits.Value = leading; RequiredDisplayDigits.BindValueChanged(_ => UpdateDisplay()); } @@ -41,14 +45,15 @@ namespace osu.Game.Graphics.UserInterface protected override LocalisableString FormatCount(double count) { string format = new string('0', RequiredDisplayDigits.Value); + var output = ((long)count).ToString(format); if (UseCommaSeparator) { - for (int i = format.Length - 3; i > 0; i -= 3) - format = format.Insert(i, @","); + for (int i = output.Length - 3; i > 0; i -= 3) + output = output.Insert(i, @","); } - return ((long)count).ToString(format); + return output; } protected override OsuSpriteText CreateSpriteText() From 446f091d329db500d7915aa53d75bb8232a6a3f5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 10 Oct 2021 16:06:12 +0900 Subject: [PATCH 2367/2442] Use comma separator for tournament score displays --- .../Gameplay/Components/TournamentMatchScoreDisplay.cs | 1 + osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/TournamentMatchScoreDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/TournamentMatchScoreDisplay.cs index 994dee4da0..3624c08187 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/TournamentMatchScoreDisplay.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/TournamentMatchScoreDisplay.cs @@ -132,6 +132,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components private OsuSpriteText displayedSpriteText; public MatchScoreCounter() + : base(useCommaSeparator: true) { Margin = new MarginPadding { Top = bar_height, Horizontal = 10 }; } diff --git a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs index d04e60a2ab..37282205f4 100644 --- a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs +++ b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs @@ -4,11 +4,9 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -153,6 +151,7 @@ namespace osu.Game.Screens.Play.HUD private OsuSpriteText displayedSpriteText; public MatchScoreCounter() + : base(useCommaSeparator: true) { Margin = new MarginPadding { Top = bar_height, Horizontal = 10 }; } @@ -173,8 +172,6 @@ namespace osu.Game.Screens.Play.HUD => displayedSpriteText.Font = winning ? OsuFont.Torus.With(weight: FontWeight.Bold, size: font_size, fixedWidth: true) : OsuFont.Torus.With(weight: FontWeight.Regular, size: font_size * 0.8f, fixedWidth: true); - - protected override LocalisableString FormatCount(double count) => count.ToLocalisableString(@"N0"); } } } From 392e7c4e73e255ac7ef32375c8061e1aee9af4ae Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 10 Oct 2021 16:16:59 +0900 Subject: [PATCH 2368/2442] Update tests --- .../OsuDifficultyCalculatorTest.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index b0d46f40fc..15675e74d1 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -15,13 +15,13 @@ namespace osu.Game.Rulesets.Osu.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; - [TestCase(6.6634445062299665d, "diffcalc-test")] - [TestCase(1.0404303969295756d, "zero-length-sliders")] + [TestCase(6.5867229481955389d, "diffcalc-test")] + [TestCase(1.0416315570967911d, "zero-length-sliders")] public void Test(double expected, string name) => base.Test(expected, name); - [TestCase(8.3857915525197733d, "diffcalc-test")] - [TestCase(1.2705229071231638d, "zero-length-sliders")] + [TestCase(8.2730989071947896d, "diffcalc-test")] + [TestCase(1.2726413186221039d, "zero-length-sliders")] public void TestClockRateAdjusted(double expected, string name) => Test(expected, name, new OsuModDoubleTime()); From e30e5bd2147be7b846b83a02bf1452cb5a71a582 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 10 Oct 2021 16:23:35 +0900 Subject: [PATCH 2369/2442] Remove int casts in other calculators --- .../Difficulty/ManiaDifficultyCalculator.cs | 3 +-- .../Difficulty/TaikoDifficultyCalculator.cs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index 9140e8afce..aee3268544 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs @@ -47,8 +47,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty { StarRating = skills[0].DifficultyValue() * star_scaling_factor, Mods = mods, - // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future - GreatHitWindow = (int)Math.Ceiling(getHitWindow300(mods) / clockRate), + GreatHitWindow = Math.Ceiling(getHitWindow300(mods) / clockRate), ScoreMultiplier = getScoreMultiplier(mods), MaxCombo = beatmap.HitObjects.Sum(h => h is HoldNote ? 2 : 1), Skills = skills diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index e755bb2325..7dd47e804b 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -94,8 +94,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty StaminaStrain = staminaRating, RhythmStrain = rhythmRating, ColourStrain = colourRating, - // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future - GreatHitWindow = (int)hitWindows.WindowFor(HitResult.Great) / clockRate, + GreatHitWindow = hitWindows.WindowFor(HitResult.Great) / clockRate, MaxCombo = beatmap.HitObjects.Count(h => h is Hit), Skills = skills }; From 6d6de5b677353235e9a667c17964ef30fb2d1b73 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 10 Oct 2021 16:50:55 +0900 Subject: [PATCH 2370/2442] Remove redundant tuple naming --- osu.Game/Scoring/ScoreManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index c5d475d631..cf22a8fda4 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -74,7 +74,7 @@ namespace osu.Game.Scoring var totalScores = await Task.WhenAll(scores.Select(s => GetTotalScoreAsync(s, cancellationToken: cancellationToken))).ConfigureAwait(false); - return scores.Select((score, index) => (score: score, totalScore: totalScores[index])) + return scores.Select((score, index) => (score, totalScore: totalScores[index])) .OrderByDescending(g => g.totalScore) .ThenBy(g => g.score.OnlineScoreID) .Select(g => g.score) From 06cce0119c4549cd0f67c98fe08a617cda470040 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 10 Oct 2021 17:41:16 +0900 Subject: [PATCH 2371/2442] Use localisable format string for comma separator mode --- .../Graphics/UserInterface/ScoreCounter.cs | 39 ++++++++----------- .../Screens/Play/HUD/DefaultScoreCounter.cs | 1 - .../Screens/Play/HUD/GameplayScoreCounter.cs | 4 +- osu.Game/Skinning/LegacyScoreCounter.cs | 1 - 4 files changed, 19 insertions(+), 26 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ScoreCounter.cs b/osu.Game/Graphics/UserInterface/ScoreCounter.cs index 069810d736..778b0ecbc6 100644 --- a/osu.Game/Graphics/UserInterface/ScoreCounter.cs +++ b/osu.Game/Graphics/UserInterface/ScoreCounter.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Bindables; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Graphics.Sprites; @@ -14,13 +15,10 @@ namespace osu.Game.Graphics.UserInterface protected override double RollingDuration => 1000; protected override Easing RollingEasing => Easing.Out; - /// - /// Whether comma separators should be displayed. - /// - public bool UseCommaSeparator { get; } - public Bindable RequiredDisplayDigits { get; } = new Bindable(); + private string formatString = string.Empty; + /// /// Displays score. /// @@ -28,13 +26,22 @@ namespace osu.Game.Graphics.UserInterface /// Whether comma separators should be displayed. protected ScoreCounter(int leading = 0, bool useCommaSeparator = false) { - UseCommaSeparator = useCommaSeparator; + if (useCommaSeparator) + { + if (leading > 0) + throw new ArgumentException("Should not mix leading zeroes and comma separators as it doesn't make sense"); - if (useCommaSeparator && leading > 0) - throw new ArgumentException("Should not mix leading zeroes and comma separators as it doesn't make sense"); + formatString = @"N0"; + } RequiredDisplayDigits.Value = leading; - RequiredDisplayDigits.BindValueChanged(_ => UpdateDisplay()); + RequiredDisplayDigits.BindValueChanged(displayDigitsChanged, true); + } + + private void displayDigitsChanged(ValueChangedEvent _) + { + formatString = new string('0', RequiredDisplayDigits.Value); + UpdateDisplay(); } protected override double GetProportionalDuration(double currentValue, double newValue) @@ -42,19 +49,7 @@ namespace osu.Game.Graphics.UserInterface return currentValue > newValue ? currentValue - newValue : newValue - currentValue; } - protected override LocalisableString FormatCount(double count) - { - string format = new string('0', RequiredDisplayDigits.Value); - var output = ((long)count).ToString(format); - - if (UseCommaSeparator) - { - for (int i = output.Length - 3; i > 0; i -= 3) - output = output.Insert(i, @","); - } - - return output; - } + protected override LocalisableString FormatCount(double count) => ((long)count).ToLocalisableString(formatString); protected override OsuSpriteText CreateSpriteText() => base.CreateSpriteText().With(s => s.Font = s.Font.With(fixedWidth: true)); diff --git a/osu.Game/Screens/Play/HUD/DefaultScoreCounter.cs b/osu.Game/Screens/Play/HUD/DefaultScoreCounter.cs index 63de5c8de5..87b19e8433 100644 --- a/osu.Game/Screens/Play/HUD/DefaultScoreCounter.cs +++ b/osu.Game/Screens/Play/HUD/DefaultScoreCounter.cs @@ -11,7 +11,6 @@ namespace osu.Game.Screens.Play.HUD public class DefaultScoreCounter : GameplayScoreCounter, ISkinnableDrawable { public DefaultScoreCounter() - : base(6) { Anchor = Anchor.TopCentre; Origin = Anchor.TopCentre; diff --git a/osu.Game/Screens/Play/HUD/GameplayScoreCounter.cs b/osu.Game/Screens/Play/HUD/GameplayScoreCounter.cs index e09630d2c4..e05eff5f3e 100644 --- a/osu.Game/Screens/Play/HUD/GameplayScoreCounter.cs +++ b/osu.Game/Screens/Play/HUD/GameplayScoreCounter.cs @@ -14,8 +14,8 @@ namespace osu.Game.Screens.Play.HUD { private Bindable scoreDisplayMode; - protected GameplayScoreCounter(int leading = 0, bool useCommaSeparator = false) - : base(leading, useCommaSeparator) + protected GameplayScoreCounter() + : base(6) { } diff --git a/osu.Game/Skinning/LegacyScoreCounter.cs b/osu.Game/Skinning/LegacyScoreCounter.cs index a12defe87e..0c9a82074f 100644 --- a/osu.Game/Skinning/LegacyScoreCounter.cs +++ b/osu.Game/Skinning/LegacyScoreCounter.cs @@ -16,7 +16,6 @@ namespace osu.Game.Skinning public bool UsesFixedAnchor { get; set; } public LegacyScoreCounter() - : base(6) { Anchor = Anchor.TopRight; Origin = Anchor.TopRight; From 794b4c46cf9c93ddf9c1294a722f3e4114d11e0d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 10 Oct 2021 17:56:32 +0900 Subject: [PATCH 2372/2442] Split score counter class into two distinct classes to simplify usages --- .../Components/TournamentMatchScoreDisplay.cs | 3 +-- .../CommaSeparatedScoreCounter.cs | 24 +++++++++++++++++++ .../Graphics/UserInterface/ScoreCounter.cs | 20 ++++------------ .../Screens/Play/HUD/MatchScoreDisplay.cs | 3 +-- 4 files changed, 30 insertions(+), 20 deletions(-) create mode 100644 osu.Game/Graphics/UserInterface/CommaSeparatedScoreCounter.cs diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/TournamentMatchScoreDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/TournamentMatchScoreDisplay.cs index 3624c08187..77101e4023 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/TournamentMatchScoreDisplay.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/TournamentMatchScoreDisplay.cs @@ -127,12 +127,11 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components score2Text.X = Math.Max(5 + score2Text.DrawWidth / 2, score2Bar.DrawWidth); } - private class MatchScoreCounter : ScoreCounter + private class MatchScoreCounter : CommaSeparatedScoreCounter { private OsuSpriteText displayedSpriteText; public MatchScoreCounter() - : base(useCommaSeparator: true) { Margin = new MarginPadding { Top = bar_height, Horizontal = 10 }; } diff --git a/osu.Game/Graphics/UserInterface/CommaSeparatedScoreCounter.cs b/osu.Game/Graphics/UserInterface/CommaSeparatedScoreCounter.cs new file mode 100644 index 0000000000..4e1c612f09 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/CommaSeparatedScoreCounter.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Extensions.LocalisationExtensions; +using osu.Framework.Graphics; +using osu.Framework.Localisation; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Graphics.UserInterface +{ + public abstract class CommaSeparatedScoreCounter : RollingCounter + { + protected override double RollingDuration => 1000; + protected override Easing RollingEasing => Easing.Out; + + protected override double GetProportionalDuration(double currentValue, double newValue) => + currentValue > newValue ? currentValue - newValue : newValue - currentValue; + + protected override LocalisableString FormatCount(double count) => ((long)count).ToLocalisableString(@"N0"); + + protected override OsuSpriteText CreateSpriteText() + => base.CreateSpriteText().With(s => s.Font = s.Font.With(fixedWidth: true)); + } +} diff --git a/osu.Game/Graphics/UserInterface/ScoreCounter.cs b/osu.Game/Graphics/UserInterface/ScoreCounter.cs index 778b0ecbc6..25f19aa0a9 100644 --- a/osu.Game/Graphics/UserInterface/ScoreCounter.cs +++ b/osu.Game/Graphics/UserInterface/ScoreCounter.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 osu.Framework.Bindables; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; @@ -17,23 +16,14 @@ namespace osu.Game.Graphics.UserInterface public Bindable RequiredDisplayDigits { get; } = new Bindable(); - private string formatString = string.Empty; + private string formatString; /// /// Displays score. /// /// How many leading zeroes the counter will have. - /// Whether comma separators should be displayed. - protected ScoreCounter(int leading = 0, bool useCommaSeparator = false) + protected ScoreCounter(int leading = 0) { - if (useCommaSeparator) - { - if (leading > 0) - throw new ArgumentException("Should not mix leading zeroes and comma separators as it doesn't make sense"); - - formatString = @"N0"; - } - RequiredDisplayDigits.Value = leading; RequiredDisplayDigits.BindValueChanged(displayDigitsChanged, true); } @@ -44,10 +34,8 @@ namespace osu.Game.Graphics.UserInterface UpdateDisplay(); } - protected override double GetProportionalDuration(double currentValue, double newValue) - { - return currentValue > newValue ? currentValue - newValue : newValue - currentValue; - } + protected override double GetProportionalDuration(double currentValue, double newValue) => + currentValue > newValue ? currentValue - newValue : newValue - currentValue; protected override LocalisableString FormatCount(double count) => ((long)count).ToLocalisableString(formatString); diff --git a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs index 37282205f4..b1c07512dd 100644 --- a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs +++ b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs @@ -146,12 +146,11 @@ namespace osu.Game.Screens.Play.HUD Score2Text.X = Math.Max(5 + Score2Text.DrawWidth / 2, score2Bar.DrawWidth); } - protected class MatchScoreCounter : ScoreCounter + protected class MatchScoreCounter : CommaSeparatedScoreCounter { private OsuSpriteText displayedSpriteText; public MatchScoreCounter() - : base(useCommaSeparator: true) { Margin = new MarginPadding { Top = bar_height, Horizontal = 10 }; } From 21ee24ea6d82ab5b8f42700f1829673f18d79add Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 10 Oct 2021 20:21:41 +0200 Subject: [PATCH 2373/2442] Add rounded button variant --- .../UserInterface/TestSceneRoundedButton.cs | 44 +++++++++++++++++ osu.Game/Graphics/OsuColour.cs | 5 ++ .../Graphics/UserInterfaceV2/RoundedButton.cs | 48 +++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneRoundedButton.cs create mode 100644 osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneRoundedButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneRoundedButton.cs new file mode 100644 index 0000000000..9ccfba7c74 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneRoundedButton.cs @@ -0,0 +1,44 @@ +// 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 NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.UserInterfaceV2; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneRoundedButton : OsuTestScene + { + [Test] + public void TestBasic() + { + RoundedButton button = null; + + AddStep("create button", () => Child = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Colour4.DarkGray + }, + button = new RoundedButton + { + Width = 400, + Text = "Test button", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Action = () => { } + } + } + }); + + AddToggleStep("toggle disabled", disabled => button.Action = disabled ? (Action)null : () => { }); + } + } +} diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index d7cfc4094c..c1acdc392c 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -225,6 +225,11 @@ namespace osu.Game.Graphics public readonly Color4 GrayE = Color4Extensions.FromHex(@"eee"); public readonly Color4 GrayF = Color4Extensions.FromHex(@"fff"); + /// + /// Equivalent to 's . + /// + public readonly Color4 Blue3 = Color4Extensions.FromHex(@"3399cc"); + /// /// Equivalent to 's . /// diff --git a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs new file mode 100644 index 0000000000..3782320ead --- /dev/null +++ b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs @@ -0,0 +1,48 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Effects; +using osu.Game.Graphics.UserInterface; +using osuTK; + +namespace osu.Game.Graphics.UserInterfaceV2 +{ + public class RoundedButton : OsuButton + { + public override float Height + { + get => base.Height; + set + { + base.Height = value; + + if (IsLoaded) + updateCornerRadius(); + } + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundColour = colours.Blue3; + + Content.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Offset = new Vector2(0, 2), + Radius = 4, + Colour = Colour4.Black.Opacity(0.15f) + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + updateCornerRadius(); + } + + private void updateCornerRadius() => Content.CornerRadius = DrawHeight / 2; + } +} From b30dd2d4ed97bdbf6cbdd3c06df9d95850405d11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 10 Oct 2021 20:32:56 +0200 Subject: [PATCH 2374/2442] Use rounded button in settings sidebar --- osu.Game/Graphics/OsuColour.cs | 5 +++++ .../Graphics/UserInterfaceV2/RoundedButton.cs | 13 ++++++++++++- .../Settings/DangerousSettingsButton.cs | 5 +---- .../Sections/Input/KeyBindingsSubsection.cs | 3 +-- .../Sections/Maintenance/GeneralSettings.cs | 19 +++++++++---------- osu.Game/Overlays/Settings/SettingsButton.cs | 4 ++-- 6 files changed, 30 insertions(+), 19 deletions(-) diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index c1acdc392c..af2bb26871 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -225,6 +225,11 @@ namespace osu.Game.Graphics public readonly Color4 GrayE = Color4Extensions.FromHex(@"eee"); public readonly Color4 GrayF = Color4Extensions.FromHex(@"fff"); + /// + /// Equivalent to 's . + /// + public readonly Color4 Pink3 = Color4Extensions.FromHex(@"cc3378"); + /// /// Equivalent to 's . /// diff --git a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs index 3782320ead..5cbbc40405 100644 --- a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs +++ b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs @@ -1,15 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Game.Graphics.UserInterface; using osuTK; namespace osu.Game.Graphics.UserInterfaceV2 { - public class RoundedButton : OsuButton + public class RoundedButton : OsuButton, IFilterable { public override float Height { @@ -44,5 +46,14 @@ namespace osu.Game.Graphics.UserInterfaceV2 } private void updateCornerRadius() => Content.CornerRadius = DrawHeight / 2; + + public virtual IEnumerable FilterTerms => new[] { Text.ToString() }; + + public bool MatchingFilter + { + set => this.FadeTo(value ? 1 : 0); + } + + public bool FilteringActive { get; set; } } } diff --git a/osu.Game/Overlays/Settings/DangerousSettingsButton.cs b/osu.Game/Overlays/Settings/DangerousSettingsButton.cs index c02db40eca..4ca3ace8a1 100644 --- a/osu.Game/Overlays/Settings/DangerousSettingsButton.cs +++ b/osu.Game/Overlays/Settings/DangerousSettingsButton.cs @@ -14,10 +14,7 @@ namespace osu.Game.Overlays.Settings [BackgroundDependencyLoader] private void load(OsuColour colours) { - BackgroundColour = colours.Pink; - - Triangles.ColourDark = colours.PinkDark; - Triangles.ColourLight = colours.PinkLight; + BackgroundColour = colours.Pink3; } } } diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs index 39dddbe1e6..2051af6f3c 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs @@ -7,7 +7,6 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Game.Database; -using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Rulesets; using osu.Game.Localisation; @@ -59,7 +58,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input } } - public class ResetButton : DangerousTriangleButton + public class ResetButton : DangerousSettingsButton { [BackgroundDependencyLoader] private void load() diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index 803c8332c1..43df58a8b1 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -10,7 +10,6 @@ using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Collections; using osu.Game.Database; -using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; using osu.Game.Scoring; using osu.Game.Skinning; @@ -21,15 +20,15 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { protected override LocalisableString Header => "General"; - private TriangleButton importBeatmapsButton; - private TriangleButton importScoresButton; - private TriangleButton importSkinsButton; - private TriangleButton importCollectionsButton; - private TriangleButton deleteBeatmapsButton; - private TriangleButton deleteScoresButton; - private TriangleButton deleteSkinsButton; - private TriangleButton restoreButton; - private TriangleButton undeleteButton; + private SettingsButton importBeatmapsButton; + private SettingsButton importScoresButton; + private SettingsButton importSkinsButton; + private SettingsButton importCollectionsButton; + private SettingsButton deleteBeatmapsButton; + private SettingsButton deleteScoresButton; + private SettingsButton deleteSkinsButton; + private SettingsButton restoreButton; + private SettingsButton undeleteButton; [BackgroundDependencyLoader(permitNulls: true)] private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, [CanBeNull] CollectionManager collectionManager, [CanBeNull] StableImportManager stableImportManager, DialogOverlay dialogOverlay) diff --git a/osu.Game/Overlays/Settings/SettingsButton.cs b/osu.Game/Overlays/Settings/SettingsButton.cs index 87b1aa0e46..be7f2de480 100644 --- a/osu.Game/Overlays/Settings/SettingsButton.cs +++ b/osu.Game/Overlays/Settings/SettingsButton.cs @@ -6,11 +6,11 @@ using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; using osu.Framework.Localisation; -using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; namespace osu.Game.Overlays.Settings { - public class SettingsButton : TriangleButton, IHasTooltip + public class SettingsButton : RoundedButton, IHasTooltip { public SettingsButton() { From 3d6602b8df754fb8e1fefcb66d048b6854c336d8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Oct 2021 14:05:31 +0900 Subject: [PATCH 2375/2442] Ensure `FailAnimation` is disposed synchronously to avoid test failures --- osu.Game/Screens/Play/Player.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 090210e611..444bea049b 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -947,7 +947,7 @@ namespace osu.Game.Screens.Play public override void OnSuspending(IScreen next) { - screenSuspension?.Expire(); + screenSuspension?.RemoveAndDisposeImmediately(); fadeOut(); base.OnSuspending(next); @@ -955,7 +955,8 @@ namespace osu.Game.Screens.Play public override bool OnExiting(IScreen next) { - screenSuspension?.Expire(); + screenSuspension?.RemoveAndDisposeImmediately(); + failAnimation?.RemoveAndDisposeImmediately(); // if arriving here and the results screen preparation task hasn't run, it's safe to say the user has not completed the beatmap. if (prepareScoreForDisplayTask == null) From 6b35ccae95368c6e81aee1835d44cb945b320bc2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Oct 2021 17:01:37 +0900 Subject: [PATCH 2376/2442] Fix some cases where interface specifications can be used but weren't --- .../Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs | 6 +++--- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- osu.Game/Beatmaps/BeatmapModelDownloader.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs index 79767bc671..558b874234 100644 --- a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs @@ -168,14 +168,14 @@ namespace osu.Game.Tests.Online return new TestBeatmapModelManager(this, storage, contextFactory, rulesets, api, host); } - protected override BeatmapModelDownloader CreateBeatmapModelDownloader(BeatmapModelManager modelManager, IAPIProvider api, GameHost host) + protected override BeatmapModelDownloader CreateBeatmapModelDownloader(IBeatmapModelManager manager, IAPIProvider api, GameHost host) { - return new TestBeatmapModelDownloader(modelManager, api, host); + return new TestBeatmapModelDownloader(manager, api, host); } internal class TestBeatmapModelDownloader : BeatmapModelDownloader { - public TestBeatmapModelDownloader(BeatmapModelManager modelManager, IAPIProvider apiProvider, GameHost gameHost) + public TestBeatmapModelDownloader(IBeatmapModelManager modelManager, IAPIProvider apiProvider, GameHost gameHost) : base(modelManager, apiProvider, gameHost) { } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 240db22c00..14175f251b 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -54,7 +54,7 @@ namespace osu.Game.Beatmaps } } - protected virtual BeatmapModelDownloader CreateBeatmapModelDownloader(BeatmapModelManager modelManager, IAPIProvider api, GameHost host) + protected virtual BeatmapModelDownloader CreateBeatmapModelDownloader(IBeatmapModelManager modelManager, IAPIProvider api, GameHost host) { return new BeatmapModelDownloader(modelManager, api, host); } diff --git a/osu.Game/Beatmaps/BeatmapModelDownloader.cs b/osu.Game/Beatmaps/BeatmapModelDownloader.cs index ae482eeafd..30dc95a966 100644 --- a/osu.Game/Beatmaps/BeatmapModelDownloader.cs +++ b/osu.Game/Beatmaps/BeatmapModelDownloader.cs @@ -13,7 +13,7 @@ namespace osu.Game.Beatmaps protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) => new DownloadBeatmapSetRequest(set, minimiseDownloadSize); - public BeatmapModelDownloader(BeatmapModelManager beatmapModelManager, IAPIProvider api, GameHost host = null) + public BeatmapModelDownloader(IBeatmapModelManager beatmapModelManager, IAPIProvider api, GameHost host = null) : base(beatmapModelManager, api, host) { } From c7675be3eff7130d7862ed26cbda6f1ecf05b2b9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Oct 2021 14:20:21 +0900 Subject: [PATCH 2377/2442] Fix typo in `IModelImporter`'s xmldoc --- osu.Game/Database/IModelImporter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/IModelImporter.cs b/osu.Game/Database/IModelImporter.cs index e94af01772..8e658cb0f5 100644 --- a/osu.Game/Database/IModelImporter.cs +++ b/osu.Game/Database/IModelImporter.cs @@ -10,7 +10,7 @@ using osu.Game.Overlays.Notifications; namespace osu.Game.Database { /// - /// A class which handles importing of asociated models to the game store. + /// A class which handles importing of associated models to the game store. /// /// The model type. public interface IModelImporter : IPostNotifications From b9460112929531743485ceaf1875fbd899b3f17d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Oct 2021 15:58:22 +0900 Subject: [PATCH 2378/2442] Update tests to run inside a `GameHost` to allow running on update thread --- osu.Game.Tests/Database/RealmTest.cs | 76 +++++++++++++++++++--------- 1 file changed, 51 insertions(+), 25 deletions(-) diff --git a/osu.Game.Tests/Database/RealmTest.cs b/osu.Game.Tests/Database/RealmTest.cs index 576f901c1a..f5752aa606 100644 --- a/osu.Game.Tests/Database/RealmTest.cs +++ b/osu.Game.Tests/Database/RealmTest.cs @@ -4,7 +4,6 @@ using System; using System.Runtime.CompilerServices; using System.Threading.Tasks; -using Nito.AsyncEx; using NUnit.Framework; using osu.Framework.Logging; using osu.Framework.Platform; @@ -28,42 +27,69 @@ namespace osu.Game.Tests.Database protected void RunTestWithRealm(Action testAction, [CallerMemberName] string caller = "") { - AsyncContext.Run(() => + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(caller)) { - var testStorage = storage.GetStorageForDirectory(caller); - - using (var realmFactory = new RealmContextFactory(testStorage, caller)) + host.Run(new RealmTestGame(() => { - Logger.Log($"Running test using realm file {testStorage.GetFullPath(realmFactory.Filename)}"); - testAction(realmFactory, testStorage); + var testStorage = storage.GetStorageForDirectory(caller); - realmFactory.Dispose(); + using (var realmFactory = new RealmContextFactory(testStorage, caller)) + { + Logger.Log($"Running test using realm file {testStorage.GetFullPath(realmFactory.Filename)}"); + testAction(realmFactory, testStorage); - Logger.Log($"Final database size: {getFileSize(testStorage, realmFactory)}"); - realmFactory.Compact(); - Logger.Log($"Final database size after compact: {getFileSize(testStorage, realmFactory)}"); - } - }); + realmFactory.Dispose(); + + Logger.Log($"Final database size: {getFileSize(testStorage, realmFactory)}"); + realmFactory.Compact(); + Logger.Log($"Final database size after compact: {getFileSize(testStorage, realmFactory)}"); + } + })); + } } protected void RunTestWithRealmAsync(Func testAction, [CallerMemberName] string caller = "") { - AsyncContext.Run(async () => + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(caller)) { - var testStorage = storage.GetStorageForDirectory(caller); - - using (var realmFactory = new RealmContextFactory(testStorage, caller)) + host.Run(new RealmTestGame(async () => { - Logger.Log($"Running test using realm file {testStorage.GetFullPath(realmFactory.Filename)}"); - await testAction(realmFactory, testStorage); + var testStorage = storage.GetStorageForDirectory(caller); - realmFactory.Dispose(); + using (var realmFactory = new RealmContextFactory(testStorage, caller)) + { + Logger.Log($"Running test using realm file {testStorage.GetFullPath(realmFactory.Filename)}"); + await testAction(realmFactory, testStorage); - Logger.Log($"Final database size: {getFileSize(testStorage, realmFactory)}"); - realmFactory.Compact(); - Logger.Log($"Final database size after compact: {getFileSize(testStorage, realmFactory)}"); - } - }); + realmFactory.Dispose(); + + Logger.Log($"Final database size: {getFileSize(testStorage, realmFactory)}"); + realmFactory.Compact(); + } + })); + } + } + + private class RealmTestGame : Framework.Game + { + public RealmTestGame(Func work) + { + // ReSharper disable once AsyncVoidLambda + Scheduler.Add(async () => + { + await work().ConfigureAwait(true); + Exit(); + }); + } + + public RealmTestGame(Action work) + { + Scheduler.Add(() => + { + work(); + Exit(); + }); + } } private static long getFileSize(Storage testStorage, RealmContextFactory realmFactory) From f43badabf408dc65fe90dca4f0cfa158c4cf4459 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Oct 2021 15:20:12 +0900 Subject: [PATCH 2379/2442] Add back update thread verification in `RealmContextFactory` --- osu.Game/Database/RealmContextFactory.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/RealmContextFactory.cs b/osu.Game/Database/RealmContextFactory.cs index 0ff902a8bc..c3810eb441 100644 --- a/osu.Game/Database/RealmContextFactory.cs +++ b/osu.Game/Database/RealmContextFactory.cs @@ -135,9 +135,8 @@ namespace osu.Game.Database if (IsDisposed) throw new ObjectDisposedException(nameof(RealmContextFactory)); - // TODO: this can be added for safety once we figure how to bypass in test - // if (!ThreadSafety.IsUpdateThread) - // throw new InvalidOperationException($"{nameof(BlockAllOperations)} must be called from the update thread."); + if (!ThreadSafety.IsUpdateThread) + throw new InvalidOperationException($"{nameof(BlockAllOperations)} must be called from the update thread."); Logger.Log(@"Blocking realm operations.", LoggingTarget.Database); From 6ca415da9f237635686504f1695b8792a719e5f8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Oct 2021 15:25:00 +0900 Subject: [PATCH 2380/2442] Add basic realm models Only the file related ones are really required outside of tests, but seems like as good an opportunity as ever to get the rest of the models into the game project. --- osu.Game/Database/IHasRealmFiles.cs | 18 ++++ osu.Game/Database/INamedFile.cs | 17 +++ osu.Game/Models/RealmBeatmap.cs | 122 ++++++++++++++++++++++ osu.Game/Models/RealmBeatmapDifficulty.cs | 43 ++++++++ osu.Game/Models/RealmBeatmapMetadata.cs | 45 ++++++++ osu.Game/Models/RealmBeatmapSet.cs | 78 ++++++++++++++ osu.Game/Models/RealmFile.cs | 20 ++++ osu.Game/Models/RealmNamedFileUsage.cs | 32 ++++++ osu.Game/Models/RealmRuleset.cs | 63 +++++++++++ 9 files changed, 438 insertions(+) create mode 100644 osu.Game/Database/IHasRealmFiles.cs create mode 100644 osu.Game/Database/INamedFile.cs create mode 100644 osu.Game/Models/RealmBeatmap.cs create mode 100644 osu.Game/Models/RealmBeatmapDifficulty.cs create mode 100644 osu.Game/Models/RealmBeatmapMetadata.cs create mode 100644 osu.Game/Models/RealmBeatmapSet.cs create mode 100644 osu.Game/Models/RealmFile.cs create mode 100644 osu.Game/Models/RealmNamedFileUsage.cs create mode 100644 osu.Game/Models/RealmRuleset.cs diff --git a/osu.Game/Database/IHasRealmFiles.cs b/osu.Game/Database/IHasRealmFiles.cs new file mode 100644 index 0000000000..2adfe73d1e --- /dev/null +++ b/osu.Game/Database/IHasRealmFiles.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Game.Models; + +namespace osu.Game.Database +{ + /// + /// A model that contains a list of files it is responsible for. + /// + public interface IHasRealmFiles + { + IList Files { get; } + + string Hash { get; set; } + } +} diff --git a/osu.Game/Database/INamedFile.cs b/osu.Game/Database/INamedFile.cs new file mode 100644 index 0000000000..9c94aed38c --- /dev/null +++ b/osu.Game/Database/INamedFile.cs @@ -0,0 +1,17 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Models; + +namespace osu.Game.Database +{ + /// + /// Represent a join model which gives a filename and scope to a . + /// + public interface INamedFile + { + public string Filename { get; set; } + + public RealmFile File { get; set; } + } +} diff --git a/osu.Game/Models/RealmBeatmap.cs b/osu.Game/Models/RealmBeatmap.cs new file mode 100644 index 0000000000..09f8dafeb6 --- /dev/null +++ b/osu.Game/Models/RealmBeatmap.cs @@ -0,0 +1,122 @@ +// 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 JetBrains.Annotations; +using Newtonsoft.Json; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Database; +using osu.Game.Rulesets; +using Realms; + +#nullable enable + +namespace osu.Game.Models +{ + /// + /// A single beatmap difficulty. + /// + [ExcludeFromDynamicCompile] + [Serializable] + [MapTo("Beatmap")] + public class RealmBeatmap : RealmObject, IHasGuidPrimaryKey, IBeatmapInfo + { + [PrimaryKey] + public Guid ID { get; set; } = Guid.NewGuid(); + + public string DifficultyName { get; set; } = string.Empty; + + public RealmRuleset Ruleset { get; set; } = null!; + + public RealmBeatmapDifficulty Difficulty { get; set; } = null!; + + public RealmBeatmapMetadata Metadata { get; set; } = null!; + + public RealmBeatmapSet? BeatmapSet { get; set; } + + public BeatmapSetOnlineStatus Status + { + get => (BeatmapSetOnlineStatus)StatusInt; + set => StatusInt = (int)value; + } + + [MapTo(nameof(Status))] + public int StatusInt { get; set; } + + public int? OnlineID { get; set; } + + public double Length { get; set; } + + public double BPM { get; set; } + + public string Hash { get; set; } = string.Empty; + + public double StarRating { get; set; } + + public string MD5Hash { get; set; } = string.Empty; + + [JsonIgnore] + public bool Hidden { get; set; } + + public RealmBeatmap(RealmRuleset ruleset, RealmBeatmapDifficulty difficulty, RealmBeatmapMetadata metadata) + { + Ruleset = ruleset; + Difficulty = difficulty; + Metadata = metadata; + } + + [UsedImplicitly] + private RealmBeatmap() + { + } + + #region Properties we may not want persisted (but also maybe no harm?) + + public double AudioLeadIn { get; set; } + + public float StackLeniency { get; set; } = 0.7f; + + public bool SpecialStyle { get; set; } + + public bool LetterboxInBreaks { get; set; } + + public bool WidescreenStoryboard { get; set; } + + public bool EpilepsyWarning { get; set; } + + public bool SamplesMatchPlaybackRate { get; set; } + + public double DistanceSpacing { get; set; } + + public int BeatDivisor { get; set; } + + public int GridSize { get; set; } + + public double TimelineZoom { get; set; } + + #endregion + + /// + /// Returns a shallow-clone of this . + /// + public RealmBeatmap Clone() => (RealmBeatmap)MemberwiseClone(); + + public bool AudioEquals(RealmBeatmap? other) => other != null + && BeatmapSet != null + && other.BeatmapSet != null + && BeatmapSet.Hash == other.BeatmapSet.Hash + && Metadata.AudioFile == other.Metadata.AudioFile; + + public bool BackgroundEquals(RealmBeatmap? other) => other != null + && BeatmapSet != null + && other.BeatmapSet != null + && BeatmapSet.Hash == other.BeatmapSet.Hash + && Metadata.BackgroundFile == other.Metadata.BackgroundFile; + + IBeatmapMetadataInfo IBeatmapInfo.Metadata => Metadata; + IBeatmapSetInfo? IBeatmapInfo.BeatmapSet => BeatmapSet; + IRulesetInfo IBeatmapInfo.Ruleset => Ruleset; + IBeatmapDifficultyInfo IBeatmapInfo.Difficulty => Difficulty; + } +} diff --git a/osu.Game/Models/RealmBeatmapDifficulty.cs b/osu.Game/Models/RealmBeatmapDifficulty.cs new file mode 100644 index 0000000000..44bfdda491 --- /dev/null +++ b/osu.Game/Models/RealmBeatmapDifficulty.cs @@ -0,0 +1,43 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Beatmaps; +using Realms; + +#nullable enable + +namespace osu.Game.Models +{ + [MapTo("BeatmapDifficulty")] + public class RealmBeatmapDifficulty : EmbeddedObject, IBeatmapDifficultyInfo + { + public float DrainRate { get; set; } = IBeatmapDifficultyInfo.DEFAULT_DIFFICULTY; + public float CircleSize { get; set; } = IBeatmapDifficultyInfo.DEFAULT_DIFFICULTY; + public float OverallDifficulty { get; set; } = IBeatmapDifficultyInfo.DEFAULT_DIFFICULTY; + public float ApproachRate { get; set; } = IBeatmapDifficultyInfo.DEFAULT_DIFFICULTY; + + public double SliderMultiplier { get; set; } = 1; + public double SliderTickRate { get; set; } = 1; + + /// + /// Returns a shallow-clone of this . + /// + public RealmBeatmapDifficulty Clone() + { + var diff = new RealmBeatmapDifficulty(); + CopyTo(diff); + return diff; + } + + public void CopyTo(RealmBeatmapDifficulty difficulty) + { + difficulty.ApproachRate = ApproachRate; + difficulty.DrainRate = DrainRate; + difficulty.CircleSize = CircleSize; + difficulty.OverallDifficulty = OverallDifficulty; + + difficulty.SliderMultiplier = SliderMultiplier; + difficulty.SliderTickRate = SliderTickRate; + } + } +} diff --git a/osu.Game/Models/RealmBeatmapMetadata.cs b/osu.Game/Models/RealmBeatmapMetadata.cs new file mode 100644 index 0000000000..00dd120791 --- /dev/null +++ b/osu.Game/Models/RealmBeatmapMetadata.cs @@ -0,0 +1,45 @@ +// 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 Newtonsoft.Json; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using Realms; + +#nullable enable + +namespace osu.Game.Models +{ + [ExcludeFromDynamicCompile] + [Serializable] + [MapTo("BeatmapMetadata")] + public class RealmBeatmapMetadata : RealmObject, IBeatmapMetadataInfo + { + public string Title { get; set; } = string.Empty; + + [JsonProperty("title_unicode")] + public string TitleUnicode { get; set; } = string.Empty; + + public string Artist { get; set; } = string.Empty; + + [JsonProperty("artist_unicode")] + public string ArtistUnicode { get; set; } = string.Empty; + + public string Author { get; set; } = string.Empty; // eventually should be linked to a persisted User. = string.Empty; + + public string Source { get; set; } = string.Empty; + + [JsonProperty(@"tags")] + public string Tags { get; set; } = string.Empty; + + /// + /// The time in milliseconds to begin playing the track for preview purposes. + /// If -1, the track should begin playing at 40% of its length. + /// + public int PreviewTime { get; set; } + + public string AudioFile { get; set; } = string.Empty; + public string BackgroundFile { get; set; } = string.Empty; + } +} diff --git a/osu.Game/Models/RealmBeatmapSet.cs b/osu.Game/Models/RealmBeatmapSet.cs new file mode 100644 index 0000000000..314ca4494b --- /dev/null +++ b/osu.Game/Models/RealmBeatmapSet.cs @@ -0,0 +1,78 @@ +// 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 osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Database; +using Realms; + +#nullable enable + +namespace osu.Game.Models +{ + [ExcludeFromDynamicCompile] + [MapTo("BeatmapSet")] + public class RealmBeatmapSet : RealmObject, IHasGuidPrimaryKey, IHasRealmFiles, ISoftDelete, IEquatable, IBeatmapSetInfo + { + [PrimaryKey] + public Guid ID { get; set; } = Guid.NewGuid(); + + public int? OnlineID { get; set; } + + public DateTimeOffset DateAdded { get; set; } + + public IBeatmapMetadataInfo? Metadata => Beatmaps.FirstOrDefault()?.Metadata; + + public IList Beatmaps { get; } = null!; + + public IList Files { get; } = null!; + + public bool DeletePending { get; set; } + + public string Hash { get; set; } = string.Empty; + + /// + /// Whether deleting this beatmap set should be prohibited (due to it being a system requirement to be present). + /// + public bool Protected { get; set; } + + public double MaxStarDifficulty => Beatmaps.Max(b => b.StarRating); + + public double MaxLength => Beatmaps.Max(b => b.Length); + + public double MaxBPM => Beatmaps.Max(b => b.BPM); + + /// + /// Returns the storage path for the file in this beatmapset with the given filename, if any exists, otherwise null. + /// The path returned is relative to the user file storage. + /// + /// The name of the file to get the storage path of. + public string? GetPathForFile(string filename) => Files.SingleOrDefault(f => string.Equals(f.Filename, filename, StringComparison.OrdinalIgnoreCase))?.File.StoragePath; + + public override string ToString() => Metadata?.ToString() ?? base.ToString(); + + public bool Equals(RealmBeatmapSet? other) + { + if (other == null) + return false; + + if (IsManaged && other.IsManaged) + return ID == other.ID; + + if (OnlineID.HasValue && other.OnlineID.HasValue) + return OnlineID == other.OnlineID; + + if (!string.IsNullOrEmpty(Hash) && !string.IsNullOrEmpty(other.Hash)) + return Hash == other.Hash; + + return ReferenceEquals(this, other); + } + + IEnumerable IBeatmapSetInfo.Beatmaps => Beatmaps; + + IEnumerable IBeatmapSetInfo.Files => Files; + } +} diff --git a/osu.Game/Models/RealmFile.cs b/osu.Game/Models/RealmFile.cs new file mode 100644 index 0000000000..6836d79d2d --- /dev/null +++ b/osu.Game/Models/RealmFile.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.IO; +using osu.Game.IO; +using Realms; + +#nullable enable + +namespace osu.Game.Models +{ + [MapTo("File")] + public class RealmFile : RealmObject, IFileInfo + { + [PrimaryKey] + public string Hash { get; set; } = string.Empty; + + public string StoragePath => Path.Combine(Hash.Remove(1), Hash.Remove(2), Hash); + } +} diff --git a/osu.Game/Models/RealmNamedFileUsage.cs b/osu.Game/Models/RealmNamedFileUsage.cs new file mode 100644 index 0000000000..59b446112d --- /dev/null +++ b/osu.Game/Models/RealmNamedFileUsage.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. + +using JetBrains.Annotations; +using osu.Game.Database; +using osu.Game.IO; +using Realms; + +#nullable enable + +namespace osu.Game.Models +{ + public class RealmNamedFileUsage : EmbeddedObject, INamedFile, INamedFileUsage + { + public RealmFile File { get; set; } = null!; + + public string Filename { get; set; } = null!; + + public RealmNamedFileUsage(RealmFile file, string filename) + { + File = file; + Filename = filename; + } + + [UsedImplicitly] + private RealmNamedFileUsage() + { + } + + IFileInfo INamedFileUsage.File => File; + } +} diff --git a/osu.Game/Models/RealmRuleset.cs b/osu.Game/Models/RealmRuleset.cs new file mode 100644 index 0000000000..0dcd701ed2 --- /dev/null +++ b/osu.Game/Models/RealmRuleset.cs @@ -0,0 +1,63 @@ +// 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 JetBrains.Annotations; +using osu.Framework.Testing; +using osu.Game.Rulesets; +using Realms; + +#nullable enable + +namespace osu.Game.Models +{ + [ExcludeFromDynamicCompile] + [MapTo("Ruleset")] + public class RealmRuleset : RealmObject, IEquatable, IRulesetInfo + { + [PrimaryKey] + public string ShortName { get; set; } = string.Empty; + + public int? OnlineID { get; set; } + + public string Name { get; set; } = string.Empty; + + public string InstantiationInfo { get; set; } = string.Empty; + + public RealmRuleset(string shortName, string name, string instantiationInfo, int? onlineID = null) + { + ShortName = shortName; + Name = name; + InstantiationInfo = instantiationInfo; + OnlineID = onlineID; + } + + [UsedImplicitly] + private RealmRuleset() + { + } + + public RealmRuleset(int? onlineID, string name, string shortName, bool available) + { + OnlineID = onlineID; + Name = name; + ShortName = shortName; + Available = available; + } + + public bool Available { get; set; } + + public bool Equals(RealmRuleset? other) => other != null && OnlineID == other.OnlineID && Available == other.Available && Name == other.Name && InstantiationInfo == other.InstantiationInfo; + + public override string ToString() => Name; + + public RealmRuleset Clone() => new RealmRuleset + { + OnlineID = OnlineID, + Name = Name, + ShortName = ShortName, + InstantiationInfo = InstantiationInfo, + Available = Available + }; + } +} From 03bf88ae813ad2b4026489d44fc8f9a6490936ba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Oct 2021 15:26:16 +0900 Subject: [PATCH 2381/2442] Add realm `FileStore` and test coverage --- osu.Game.Tests/Database/FileStoreTests.cs | 114 ++++++++++++++++++++++ osu.Game.Tests/Database/RealmTest.cs | 42 ++++++++ osu.Game/Stores/RealmFileStore.cs | 113 +++++++++++++++++++++ 3 files changed, 269 insertions(+) create mode 100644 osu.Game.Tests/Database/FileStoreTests.cs create mode 100644 osu.Game/Stores/RealmFileStore.cs diff --git a/osu.Game.Tests/Database/FileStoreTests.cs b/osu.Game.Tests/Database/FileStoreTests.cs new file mode 100644 index 0000000000..861de5303d --- /dev/null +++ b/osu.Game.Tests/Database/FileStoreTests.cs @@ -0,0 +1,114 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Diagnostics; +using System.IO; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Logging; +using osu.Game.Models; +using osu.Game.Stores; + +#nullable enable + +namespace osu.Game.Tests.Database +{ + public class FileStoreTests : RealmTest + { + [Test] + public void TestImportFile() + { + RunTestWithRealm((realmFactory, storage) => + { + var realm = realmFactory.Context; + var files = new RealmFileStore(realmFactory, storage); + + var testData = new MemoryStream(new byte[] { 0, 1, 2, 3 }); + + realm.Write(() => files.Add(testData, realm)); + + Assert.True(files.Storage.Exists("0/05/054edec1d0211f624fed0cbca9d4f9400b0e491c43742af2c5b0abebf0c990d8")); + Assert.True(files.Storage.Exists(realm.All().First().StoragePath)); + }); + } + + [Test] + public void TestImportSameFileTwice() + { + RunTestWithRealm((realmFactory, storage) => + { + var realm = realmFactory.Context; + var files = new RealmFileStore(realmFactory, storage); + + var testData = new MemoryStream(new byte[] { 0, 1, 2, 3 }); + + realm.Write(() => files.Add(testData, realm)); + realm.Write(() => files.Add(testData, realm)); + + Assert.AreEqual(1, realm.All().Count()); + }); + } + + [Test] + public void TestDontPurgeReferenced() + { + RunTestWithRealm((realmFactory, storage) => + { + var realm = realmFactory.Context; + var files = new RealmFileStore(realmFactory, storage); + + var file = realm.Write(() => files.Add(new MemoryStream(new byte[] { 0, 1, 2, 3 }), realm)); + + var timer = new Stopwatch(); + timer.Start(); + + realm.Write(() => + { + // attach the file to an arbitrary beatmap + var beatmapSet = CreateBeatmapSet(CreateRuleset()); + + beatmapSet.Files.Add(new RealmNamedFileUsage(file, "arbitrary.resource")); + + realm.Add(beatmapSet); + }); + + Logger.Log($"Import complete at {timer.ElapsedMilliseconds}"); + + string path = file.StoragePath; + + Assert.True(realm.All().Any()); + Assert.True(files.Storage.Exists(path)); + + files.Cleanup(); + Logger.Log($"Cleanup complete at {timer.ElapsedMilliseconds}"); + + Assert.True(realm.All().Any()); + Assert.True(file.IsValid); + Assert.True(files.Storage.Exists(path)); + }); + } + + [Test] + public void TestPurgeUnreferenced() + { + RunTestWithRealm((realmFactory, storage) => + { + var realm = realmFactory.Context; + var files = new RealmFileStore(realmFactory, storage); + + var file = realm.Write(() => files.Add(new MemoryStream(new byte[] { 0, 1, 2, 3 }), realm)); + + string path = file.StoragePath; + + Assert.True(realm.All().Any()); + Assert.True(files.Storage.Exists(path)); + + files.Cleanup(); + + Assert.False(realm.All().Any()); + Assert.False(file.IsValid); + Assert.False(files.Storage.Exists(path)); + }); + } + } +} diff --git a/osu.Game.Tests/Database/RealmTest.cs b/osu.Game.Tests/Database/RealmTest.cs index f5752aa606..04c9f2577a 100644 --- a/osu.Game.Tests/Database/RealmTest.cs +++ b/osu.Game.Tests/Database/RealmTest.cs @@ -5,10 +5,12 @@ using System; using System.Runtime.CompilerServices; using System.Threading.Tasks; using NUnit.Framework; +using osu.Framework.Extensions; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Database; +using osu.Game.Models; #nullable enable @@ -70,6 +72,46 @@ namespace osu.Game.Tests.Database } } + protected static RealmBeatmapSet CreateBeatmapSet(RealmRuleset ruleset) + { + RealmFile createRealmFile() => new RealmFile { Hash = Guid.NewGuid().ToString().ComputeSHA2Hash() }; + + var metadata = new RealmBeatmapMetadata + { + Title = "My Love", + Artist = "Kuba Oms" + }; + + var beatmapSet = new RealmBeatmapSet + { + Beatmaps = + { + new RealmBeatmap(ruleset, new RealmBeatmapDifficulty(), metadata) { DifficultyName = "Easy", }, + new RealmBeatmap(ruleset, new RealmBeatmapDifficulty(), metadata) { DifficultyName = "Normal", }, + new RealmBeatmap(ruleset, new RealmBeatmapDifficulty(), metadata) { DifficultyName = "Hard", }, + new RealmBeatmap(ruleset, new RealmBeatmapDifficulty(), metadata) { DifficultyName = "Insane", } + }, + Files = + { + new RealmNamedFileUsage(createRealmFile(), "test [easy].osu"), + new RealmNamedFileUsage(createRealmFile(), "test [normal].osu"), + new RealmNamedFileUsage(createRealmFile(), "test [hard].osu"), + new RealmNamedFileUsage(createRealmFile(), "test [insane].osu"), + } + }; + + for (int i = 0; i < 8; i++) + beatmapSet.Files.Add(new RealmNamedFileUsage(createRealmFile(), $"hitsound{i}.mp3")); + + foreach (var b in beatmapSet.Beatmaps) + b.BeatmapSet = beatmapSet; + + return beatmapSet; + } + + protected static RealmRuleset CreateRuleset() => + new RealmRuleset(0, "osu!", "osu", true); + private class RealmTestGame : Framework.Game { public RealmTestGame(Func work) diff --git a/osu.Game/Stores/RealmFileStore.cs b/osu.Game/Stores/RealmFileStore.cs new file mode 100644 index 0000000000..aac52b193c --- /dev/null +++ b/osu.Game/Stores/RealmFileStore.cs @@ -0,0 +1,113 @@ +// 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.IO; +using System.Linq; +using osu.Framework.Extensions; +using osu.Framework.IO.Stores; +using osu.Framework.Logging; +using osu.Framework.Platform; +using osu.Game.Database; +using osu.Game.Models; +using Realms; + +#nullable enable + +namespace osu.Game.Stores +{ + /// + /// Handles the Store and retrieval of Files/FileSets to the database backing + /// + public class RealmFileStore + { + private readonly RealmContextFactory realmFactory; + public readonly IResourceStore Store; + + public Storage Storage; + + public RealmFileStore(RealmContextFactory realmFactory, Storage storage) + { + this.realmFactory = realmFactory; + + Storage = storage.GetStorageForDirectory(@"files"); + Store = new StorageBackedResourceStore(Storage); + } + + /// + /// Add a new file to the game-wide database, copying it to permanent storage if not already present. + /// + /// The file data stream. + /// The realm instance to add to. Should already be in a transaction. + /// + public RealmFile Add(Stream data, Realm realm) + { + string hash = data.ComputeSHA2Hash(); + + var existing = realm.Find(hash); + + var file = existing ?? new RealmFile { Hash = hash }; + + if (!checkFileExistsAndMatchesHash(file)) + copyToStore(file, data); + + if (!file.IsManaged) + realm.Add(file); + + return file; + } + + private void copyToStore(RealmFile file, Stream data) + { + data.Seek(0, SeekOrigin.Begin); + + using (var output = Storage.GetStream(file.StoragePath, FileAccess.Write)) + data.CopyTo(output); + + data.Seek(0, SeekOrigin.Begin); + } + + private bool checkFileExistsAndMatchesHash(RealmFile file) + { + string path = file.StoragePath; + + // we may be re-adding a file to fix missing store entries. + if (!Storage.Exists(path)) + return false; + + // even if the file already exists, check the existing checksum for safety. + using (var stream = Storage.GetStream(path)) + return stream.ComputeSHA2Hash() == file.Hash; + } + + public void Cleanup() + { + var realm = realmFactory.Context; + + // can potentially be run asynchronously, although we will need to consider operation order for disk deletion vs realm removal. + using (var transaction = realm.BeginWrite()) + { + // TODO: consider using a realm native query to avoid iterating all files (https://github.com/realm/realm-dotnet/issues/2659#issuecomment-927823707) + var files = realm.All().ToList(); + + foreach (var file in files) + { + if (file.BacklinksCount > 0) + continue; + + try + { + Storage.Delete(file.StoragePath); + realm.Remove(file); + } + catch (Exception e) + { + Logger.Error(e, $@"Could not delete databased file {file.Hash}"); + } + } + + transaction.Commit(); + } + } + } +} From b01d82b3fd8e05548a1eee87cc33c7b5920649df Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Sep 2021 23:46:16 +0900 Subject: [PATCH 2382/2442] Add `RealmLive` implementation --- osu.Game.Tests/Database/RealmLiveTests.cs | 199 +++++++++++++++++++++ osu.Game/Database/RealmLive.cs | 111 ++++++++++++ osu.Game/Database/RealmObjectExtensions.cs | 13 ++ 3 files changed, 323 insertions(+) create mode 100644 osu.Game.Tests/Database/RealmLiveTests.cs create mode 100644 osu.Game/Database/RealmLive.cs diff --git a/osu.Game.Tests/Database/RealmLiveTests.cs b/osu.Game.Tests/Database/RealmLiveTests.cs new file mode 100644 index 0000000000..d6ea24e848 --- /dev/null +++ b/osu.Game.Tests/Database/RealmLiveTests.cs @@ -0,0 +1,199 @@ +// 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.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using NUnit.Framework; +using osu.Game.Database; +using osu.Game.Models; +using Realms; + +#nullable enable + +namespace osu.Game.Tests.Database +{ + public class RealmLiveTests : RealmTest + { + [Test] + public void TestValueAccessWithOpenContext() + { + RunTestWithRealm((realmFactory, _) => + { + RealmLive? liveBeatmap = null; + Task.Factory.StartNew(() => + { + using (var threadContext = realmFactory.CreateContext()) + { + var beatmap = threadContext.Write(r => r.Add(new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata()))); + + liveBeatmap = beatmap.ToLive(); + } + }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait(); + + Debug.Assert(liveBeatmap != null); + + Task.Factory.StartNew(() => + { + Assert.DoesNotThrow(() => + { + using (realmFactory.CreateContext()) + { + var resolved = liveBeatmap.Value; + + Assert.IsTrue(resolved.Realm.IsClosed); + Assert.IsTrue(resolved.IsValid); + + // can access properties without a crash. + Assert.IsFalse(resolved.Hidden); + } + }); + }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait(); + }); + } + + [Test] + public void TestScopedReadWithoutContext() + { + RunTestWithRealm((realmFactory, _) => + { + RealmLive? liveBeatmap = null; + Task.Factory.StartNew(() => + { + using (var threadContext = realmFactory.CreateContext()) + { + var beatmap = threadContext.Write(r => r.Add(new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata()))); + + liveBeatmap = beatmap.ToLive(); + } + }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait(); + + Debug.Assert(liveBeatmap != null); + + Task.Factory.StartNew(() => + { + liveBeatmap.PerformRead(beatmap => + { + Assert.IsTrue(beatmap.IsValid); + Assert.IsFalse(beatmap.Hidden); + }); + }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait(); + }); + } + + [Test] + public void TestScopedWriteWithoutContext() + { + RunTestWithRealm((realmFactory, _) => + { + RealmLive? liveBeatmap = null; + Task.Factory.StartNew(() => + { + using (var threadContext = realmFactory.CreateContext()) + { + var beatmap = threadContext.Write(r => r.Add(new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata()))); + + liveBeatmap = beatmap.ToLive(); + } + }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait(); + + Debug.Assert(liveBeatmap != null); + + Task.Factory.StartNew(() => + { + liveBeatmap.PerformWrite(beatmap => { beatmap.Hidden = true; }); + liveBeatmap.PerformRead(beatmap => { Assert.IsTrue(beatmap.Hidden); }); + }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait(); + }); + } + + [Test] + public void TestValueAccessWithoutOpenContextFails() + { + RunTestWithRealm((realmFactory, _) => + { + RealmLive? liveBeatmap = null; + Task.Factory.StartNew(() => + { + using (var threadContext = realmFactory.CreateContext()) + { + var beatmap = threadContext.Write(r => r.Add(new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata()))); + + liveBeatmap = beatmap.ToLive(); + } + }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait(); + + Debug.Assert(liveBeatmap != null); + + Task.Factory.StartNew(() => + { + Assert.Throws(() => + { + var unused = liveBeatmap.Value; + }); + }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait(); + }); + } + + [Test] + public void TestLiveAssumptions() + { + RunTestWithRealm((realmFactory, _) => + { + int changesTriggered = 0; + + using (var updateThreadContext = realmFactory.CreateContext()) + { + updateThreadContext.All().SubscribeForNotifications(gotChange); + RealmLive? liveBeatmap = null; + + Task.Factory.StartNew(() => + { + using (var threadContext = realmFactory.CreateContext()) + { + var ruleset = CreateRuleset(); + var beatmap = threadContext.Write(r => r.Add(new RealmBeatmap(ruleset, new RealmBeatmapDifficulty(), new RealmBeatmapMetadata()))); + + // add a second beatmap to ensure that a full refresh occurs below. + // not just a refresh from the resolved Live. + threadContext.Write(r => r.Add(new RealmBeatmap(ruleset, new RealmBeatmapDifficulty(), new RealmBeatmapMetadata()))); + + liveBeatmap = beatmap.ToLive(); + } + }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait(); + + Debug.Assert(liveBeatmap != null); + + // not yet seen by main context + Assert.AreEqual(0, updateThreadContext.All().Count()); + Assert.AreEqual(0, changesTriggered); + + var resolved = liveBeatmap.Value; + + // retrieval causes an implicit refresh. even changes that aren't related to the retrieval are fired at this point. + Assert.AreEqual(2, updateThreadContext.All().Count()); + Assert.AreEqual(1, changesTriggered); + + // even though the realm that this instance was resolved for was closed, it's still valid. + Assert.IsTrue(resolved.Realm.IsClosed); + Assert.IsTrue(resolved.IsValid); + + // can access properties without a crash. + Assert.IsFalse(resolved.Hidden); + + updateThreadContext.Write(r => + { + // can use with the main context. + r.Remove(resolved); + }); + } + + void gotChange(IRealmCollection sender, ChangeSet changes, Exception error) + { + changesTriggered++; + } + }); + } + } +} diff --git a/osu.Game/Database/RealmLive.cs b/osu.Game/Database/RealmLive.cs new file mode 100644 index 0000000000..71fb44f617 --- /dev/null +++ b/osu.Game/Database/RealmLive.cs @@ -0,0 +1,111 @@ +// 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.Threading; +using Realms; + +#nullable enable + +namespace osu.Game.Database +{ + /// + /// Provides a method of working with realm objects over longer application lifetimes. + /// + /// The underlying object type. + public class RealmLive : ILive where T : RealmObject, IHasGuidPrimaryKey + { + public Guid ID { get; } + + private readonly SynchronizationContext? fetchedContext; + private readonly int fetchedThreadId; + + /// + /// The original live data used to create this instance. + /// + private readonly T data; + + /// + /// Construct a new instance of live realm data. + /// + /// The realm data. + public RealmLive(T data) + { + this.data = data; + + fetchedContext = SynchronizationContext.Current; + fetchedThreadId = Thread.CurrentThread.ManagedThreadId; + + ID = data.ID; + } + + /// + /// Perform a read operation on this live object. + /// + /// The action to perform. + public void PerformRead(Action perform) + { + if (originalDataValid) + { + perform(data); + return; + } + + using (var realm = Realm.GetInstance(data.Realm.Config)) + perform(realm.Find(ID)); + } + + /// + /// Perform a read operation on this live object. + /// + /// The action to perform. + public TReturn PerformRead(Func perform) + { + if (typeof(RealmObjectBase).IsAssignableFrom(typeof(TReturn))) + throw new InvalidOperationException($"Realm live objects should not exit the scope of {nameof(PerformRead)}."); + + if (originalDataValid) + return perform(data); + + using (var realm = Realm.GetInstance(data.Realm.Config)) + return perform(realm.Find(ID)); + } + + /// + /// Perform a write operation on this live object. + /// + /// The action to perform. + public void PerformWrite(Action perform) => + PerformRead(t => + { + var transaction = t.Realm.BeginWrite(); + perform(t); + transaction.Commit(); + }); + + public T Value + { + get + { + if (originalDataValid) + return data; + + T retrieved; + + using (var realm = Realm.GetInstance(data.Realm.Config)) + retrieved = realm.Find(ID); + + if (!retrieved.IsValid) + throw new InvalidOperationException("Attempted to access value without an open context"); + + return retrieved; + } + } + + private bool originalDataValid => isCorrectThread && data.IsValid && !data.Realm.IsClosed; + + // this matches realm's internal thread validation (see https://github.com/realm/realm-dotnet/blob/903b4d0b304f887e37e2d905384fb572a6496e70/Realm/Realm/Native/SynchronizationContextScheduler.cs#L72) + private bool isCorrectThread + => (fetchedContext != null && SynchronizationContext.Current == fetchedContext) || fetchedThreadId == Thread.CurrentThread.ManagedThreadId; + } +} diff --git a/osu.Game/Database/RealmObjectExtensions.cs b/osu.Game/Database/RealmObjectExtensions.cs index c5aa1399a3..18a926fa8c 100644 --- a/osu.Game/Database/RealmObjectExtensions.cs +++ b/osu.Game/Database/RealmObjectExtensions.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; using AutoMapper; using osu.Game.Input.Bindings; using Realms; @@ -47,5 +48,17 @@ namespace osu.Game.Database return mapper.Map(item); } + + public static List> ToLive(this IEnumerable realmList) + where T : RealmObject, IHasGuidPrimaryKey + { + return realmList.Select(l => new RealmLive(l)).ToList(); + } + + public static RealmLive ToLive(this T realmObject) + where T : RealmObject, IHasGuidPrimaryKey + { + return new RealmLive(realmObject); + } } } From 81a0fbfc40d2a6afca48fb64141eecabf5aab011 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Oct 2021 14:30:27 +0900 Subject: [PATCH 2383/2442] Add `Live<>` casting test --- osu.Game.Tests/Database/RealmLiveTests.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/osu.Game.Tests/Database/RealmLiveTests.cs b/osu.Game.Tests/Database/RealmLiveTests.cs index d6ea24e848..33aa1afb89 100644 --- a/osu.Game.Tests/Database/RealmLiveTests.cs +++ b/osu.Game.Tests/Database/RealmLiveTests.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using NUnit.Framework; +using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Models; using Realms; @@ -16,6 +17,19 @@ namespace osu.Game.Tests.Database { public class RealmLiveTests : RealmTest { + [Test] + public void TestLiveCastability() + { + RunTestWithRealm((realmFactory, _) => + { + RealmLive beatmap = realmFactory.CreateContext().Write(r => r.Add(new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata()))).ToLive(); + + ILive iBeatmap = beatmap; + + Assert.AreEqual(0, iBeatmap.Value.Length); + }); + } + [Test] public void TestValueAccessWithOpenContext() { From 43aacb383170dcd854561c890fa8c7d6c6f5e1f7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Oct 2021 16:11:15 +0900 Subject: [PATCH 2384/2442] Fix two different skins displaying at the same time when rapidly switching --- osu.Game/Skinning/SkinnableTargetContainer.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinnableTargetContainer.cs b/osu.Game/Skinning/SkinnableTargetContainer.cs index e7125bb034..20c2fcc075 100644 --- a/osu.Game/Skinning/SkinnableTargetContainer.cs +++ b/osu.Game/Skinning/SkinnableTargetContainer.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using System.Threading; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -22,6 +23,8 @@ namespace osu.Game.Skinning public bool ComponentsLoaded { get; private set; } + private CancellationTokenSource cancellationSource; + public SkinnableTargetContainer(SkinnableTarget target) { Target = target; @@ -38,6 +41,9 @@ namespace osu.Game.Skinning content = CurrentSkin.GetDrawableComponent(new SkinnableTargetComponent(Target)) as SkinnableTargetComponentsContainer; + cancellationSource?.Cancel(); + cancellationSource = null; + if (content != null) { LoadComponentAsync(content, wrapper => @@ -45,7 +51,7 @@ namespace osu.Game.Skinning AddInternal(wrapper); components.AddRange(wrapper.Children.OfType()); ComponentsLoaded = true; - }); + }, (cancellationSource = new CancellationTokenSource()).Token); } else ComponentsLoaded = true; From b4092549c03f9a2d3076899518e26bb460e013d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Oct 2021 16:35:36 +0900 Subject: [PATCH 2385/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 5a0e7479fa..fefc2f6438 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 4877ddf725..ff382f5227 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index edce9d27fe..fff0cbf418 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 484a95229e64383a94a12851a5814047465a1b47 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Oct 2021 16:36:04 +0900 Subject: [PATCH 2386/2442] Update toast implementations temporarily to expedite getting tests back in line --- osu.Game/Overlays/OSD/TrackedSettingToast.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/OSD/TrackedSettingToast.cs b/osu.Game/Overlays/OSD/TrackedSettingToast.cs index 51214fe460..198aa1438a 100644 --- a/osu.Game/Overlays/OSD/TrackedSettingToast.cs +++ b/osu.Game/Overlays/OSD/TrackedSettingToast.cs @@ -29,7 +29,7 @@ namespace osu.Game.Overlays.OSD private Sample sampleChange; public TrackedSettingToast(SettingDescription description) - : base(description.Name, description.Value, description.Shortcut) + : base(description.Name.ToString(), description.Value.ToString(), description.Shortcut.ToString()) { FillFlowContainer optionLights; From 22e90076fb5e2c91ef55fa49d3c059ac69253c63 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Oct 2021 18:05:45 +0900 Subject: [PATCH 2387/2442] Add temporary logging --- osu.Game/Audio/Effects/AudioFilter.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/osu.Game/Audio/Effects/AudioFilter.cs b/osu.Game/Audio/Effects/AudioFilter.cs index ee48bdd7d9..7a51d7be8f 100644 --- a/osu.Game/Audio/Effects/AudioFilter.cs +++ b/osu.Game/Audio/Effects/AudioFilter.cs @@ -1,11 +1,13 @@ // 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.Diagnostics; using ManagedBass.Fx; using osu.Framework.Audio.Mixing; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Logging; namespace osu.Game.Audio.Effects { @@ -130,8 +132,15 @@ namespace osu.Game.Audio.Effects { base.Dispose(isDisposing); - if (mixer.Effects.Contains(filter)) - detachFilter(); + try + { + if (mixer.Effects.Contains(filter)) + detachFilter(); + } + catch (Exception e) + { + Logger.Log($"Exception in audio filter disposal: {e}"); + } } } } From b1ad3161dd33119d57cf42715aae1f74a1157be9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 11 Oct 2021 21:25:02 +0200 Subject: [PATCH 2388/2442] Add failing test case for frame stable clock direction flip scenario --- .../TestSceneFrameStabilityContainer.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFrameStabilityContainer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFrameStabilityContainer.cs index 5eb71e92c2..881e3f097c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFrameStabilityContainer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFrameStabilityContainer.cs @@ -103,6 +103,30 @@ namespace osu.Game.Tests.Visual.Gameplay checkFrameCount(0); } + [Test] + public void TestSeekToSameTimePreservesRate() + { + AddStep("set manual clock rate", () => manualClock.Rate = 1); + seekManualTo(5000); + createStabilityContainer(); + checkRate(1); + + seekManualTo(10000); + checkRate(1); + + seekManualTo(10000); + checkRate(1); + + seekManualTo(5000); + checkRate(-1); + + seekManualTo(5000); + checkRate(-1); + + seekManualTo(10000); + checkRate(1); + } + private const int max_frames_catchup = 50; private void createStabilityContainer(double gameplayStartTime = double.MinValue) => AddStep("create container", () => @@ -116,6 +140,9 @@ namespace osu.Game.Tests.Visual.Gameplay private void checkFrameCount(int frames) => AddAssert($"elapsed frames is {frames}", () => consumer.ElapsedFrames == frames); + private void checkRate(double rate) => + AddAssert($"clock rate is {rate}", () => consumer.Clock.Rate == rate); + public class ClockConsumingChild : CompositeDrawable { private readonly OsuSpriteText text; From 56eae703fed67c5694004bf4d33d24b3ccb8dbad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 11 Oct 2021 21:39:48 +0200 Subject: [PATCH 2389/2442] Avoid changing frame stable clock direction if time hasn't changed between frames --- osu.Game/Rulesets/UI/FrameStabilityContainer.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs index e9865f6c8b..c0b339a231 100644 --- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs +++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs @@ -55,7 +55,10 @@ namespace osu.Game.Rulesets.UI /// /// The current direction of playback to be exposed to frame stable children. /// - private int direction; + /// + /// Initially it is presumed that playback will proceed in the forward direction. + /// + private int direction = 1; [BackgroundDependencyLoader(true)] private void load(GameplayClock clock, ISamplePlaybackDisabler sampleDisabler) @@ -139,7 +142,9 @@ namespace osu.Game.Rulesets.UI state = PlaybackState.NotValid; } - if (state == PlaybackState.Valid) + // if the proposed time is the same as the current time, assume that the clock will continue progressing in the same direction as previously. + // this avoids spurious flips in direction from -1 to 1 during rewinds. + if (state == PlaybackState.Valid && proposedTime != manualClock.CurrentTime) direction = proposedTime >= manualClock.CurrentTime ? 1 : -1; double timeBehind = Math.Abs(proposedTime - parentGameplayClock.CurrentTime); From ff382259ca2048be6399eacc31b79df491e96dee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 11 Oct 2021 23:10:56 +0200 Subject: [PATCH 2390/2442] Use rounded buttons in tablet rotation preset settings --- .../Settings/TestSceneTabletSettings.cs | 4 + .../Sections/Input/RotationPresetButtons.cs | 74 ++++++++++++------- 2 files changed, 52 insertions(+), 26 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs index 997eac709d..dc5b0e0d77 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs @@ -3,6 +3,7 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input.Handlers.Tablet; @@ -21,6 +22,9 @@ namespace osu.Game.Tests.Visual.Settings private TestTabletHandler tabletHandler; private TabletSettings settings; + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + [SetUpSteps] public void SetUpSteps() { diff --git a/osu.Game/Overlays/Settings/Sections/Input/RotationPresetButtons.cs b/osu.Game/Overlays/Settings/Sections/Input/RotationPresetButtons.cs index 26610628d5..3ef5ce8941 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/RotationPresetButtons.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/RotationPresetButtons.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -8,16 +9,24 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Handlers.Tablet; using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; namespace osu.Game.Overlays.Settings.Sections.Input { - internal class RotationPresetButtons : FillFlowContainer + internal class RotationPresetButtons : CompositeDrawable { + public new MarginPadding Padding + { + get => base.Padding; + set => base.Padding = value; + } + private readonly ITabletHandler tabletHandler; private Bindable rotation; + private readonly RotationButton[] rotationPresets = new RotationButton[preset_count]; + private const int preset_count = 4; private const int height = 50; public RotationPresetButtons(ITabletHandler tabletHandler) @@ -27,18 +36,39 @@ namespace osu.Game.Overlays.Settings.Sections.Input RelativeSizeAxes = Axes.X; Height = height; - for (int i = 0; i < 360; i += 90) + IEnumerable createColumns(int count) { - var presetRotation = i; - - Add(new RotationButton(i) + for (int i = 0; i < count; ++i) { - RelativeSizeAxes = Axes.X, - Height = height, - Width = 0.25f, - Text = $@"{presetRotation}º", - Action = () => tabletHandler.Rotation.Value = presetRotation, - }); + if (i > 0) + yield return new Dimension(GridSizeMode.Absolute, 10); + + yield return new Dimension(); + } + } + + GridContainer grid; + + InternalChild = grid = new GridContainer + { + RelativeSizeAxes = Axes.Both, + ColumnDimensions = createColumns(preset_count).ToArray() + }; + + grid.Content = new[] { new Drawable[preset_count * 2 - 1] }; + + for (int i = 0; i < preset_count; i++) + { + var rotationValue = i * 90; + + var rotationPreset = new RotationButton(rotationValue) + { + RelativeSizeAxes = Axes.Both, + Height = 1, + Text = $@"{rotationValue}º", + Action = () => tabletHandler.Rotation.Value = rotationValue, + }; + grid.Content[0][2 * i] = rotationPresets[i] = rotationPreset; } } @@ -49,16 +79,19 @@ namespace osu.Game.Overlays.Settings.Sections.Input rotation = tabletHandler.Rotation.GetBoundCopy(); rotation.BindValueChanged(val => { - foreach (var b in Children.OfType()) + foreach (var b in rotationPresets) b.IsSelected = b.Preset == val.NewValue; }, true); } - public class RotationButton : TriangleButton + public class RotationButton : RoundedButton { [Resolved] private OsuColour colours { get; set; } + [Resolved] + private OverlayColourProvider colourProvider { get; set; } + public readonly int Preset; public RotationButton(int preset) @@ -91,18 +124,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input private void updateColour() { - if (isSelected) - { - BackgroundColour = colours.BlueDark; - Triangles.ColourDark = colours.BlueDarker; - Triangles.ColourLight = colours.Blue; - } - else - { - BackgroundColour = colours.Gray4; - Triangles.ColourDark = colours.Gray5; - Triangles.ColourLight = colours.Gray6; - } + BackgroundColour = isSelected ? colours.Blue3 : colourProvider.Background3; } } } From 1550a3b470d44c93e809c9cf68d45bee7eac5e87 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Oct 2021 11:11:55 +0900 Subject: [PATCH 2391/2442] Rethrow exception after logging to make tracking on CI easier --- osu.Game/Audio/Effects/AudioFilter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Audio/Effects/AudioFilter.cs b/osu.Game/Audio/Effects/AudioFilter.cs index 7a51d7be8f..5eaa87af8d 100644 --- a/osu.Game/Audio/Effects/AudioFilter.cs +++ b/osu.Game/Audio/Effects/AudioFilter.cs @@ -140,6 +140,7 @@ namespace osu.Game.Audio.Effects catch (Exception e) { Logger.Log($"Exception in audio filter disposal: {e}"); + throw; } } } From df83f0db08d3d66765febfe17ed4af84cb504f70 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Oct 2021 11:28:11 +0900 Subject: [PATCH 2392/2442] Fix cross-thread list manipulation in `SkinProvidingContainer` --- osu.Game/Skinning/SkinProvidingContainer.cs | 27 ++++++++------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index ada6e4b788..d2c3a6e837 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio.Sample; @@ -43,7 +44,7 @@ namespace osu.Game.Skinning /// /// A dictionary mapping each source to a wrapper which handles lookup allowances. /// - private readonly List<(ISkin skin, DisableableSkinSource wrapped)> skinSources = new List<(ISkin, DisableableSkinSource)>(); + private (ISkin skin, DisableableSkinSource wrapped)[] skinSources = Array.Empty<(ISkin skin, DisableableSkinSource wrapped)>(); /// /// Constructs a new initialised with a single skin source. @@ -173,32 +174,24 @@ namespace osu.Game.Skinning /// The skin to add. protected void AddSource(ISkin skin) { - skinSources.Add((skin, new DisableableSkinSource(skin, this))); + skinSources = skinSources.Append((skin, new DisableableSkinSource(skin, this))).ToArray(); if (skin is ISkinSource source) source.SourceChanged += TriggerSourceChanged; } - /// - /// Remove a skin from this provider. - /// - /// The skin to remove. - protected void RemoveSource(ISkin skin) - { - if (skinSources.RemoveAll(s => s.skin == skin) == 0) - return; - - if (skin is ISkinSource source) - source.SourceChanged -= TriggerSourceChanged; - } - /// /// Clears all skin sources. /// protected void ResetSources() { - foreach (var i in skinSources.ToArray()) - RemoveSource(i.skin); + foreach (var skin in skinSources) + { + if (skin.skin is ISkinSource source) + source.SourceChanged -= TriggerSourceChanged; + } + + skinSources = Array.Empty<(ISkin skin, DisableableSkinSource wrapped)>(); } /// From e0c54e3207811a14f90c94ac6e13fd0eea00cac4 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 12 Oct 2021 09:37:11 +0700 Subject: [PATCH 2393/2442] add `OpenChangelog` link action --- osu.Game/Online/Chat/MessageFormatter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index 0e4ea694aa..cfffac7741 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -324,6 +324,7 @@ namespace osu.Game.Online.Chat SearchBeatmapSet, OpenWiki, Custom, + OpenChangelog, } public class Link : IComparable From 39a3482458b586787e0612503f55a77463a57d7f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Oct 2021 11:55:04 +0900 Subject: [PATCH 2394/2442] Replace Add/Reset methods with single `Set` method --- .../Skins/TestSceneSkinProvidingContainer.cs | 4 +-- .../Skinning/RulesetSkinProvidingContainer.cs | 5 +--- osu.Game/Skinning/SkinProvidingContainer.cs | 29 ++++++++----------- 3 files changed, 14 insertions(+), 24 deletions(-) diff --git a/osu.Game.Tests/Skins/TestSceneSkinProvidingContainer.cs b/osu.Game.Tests/Skins/TestSceneSkinProvidingContainer.cs index ab47067411..2247f685e2 100644 --- a/osu.Game.Tests/Skins/TestSceneSkinProvidingContainer.cs +++ b/osu.Game.Tests/Skins/TestSceneSkinProvidingContainer.cs @@ -6,7 +6,6 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; @@ -67,8 +66,7 @@ namespace osu.Game.Tests.Skins protected override void OnSourceChanged() { - ResetSources(); - sources.ForEach(AddSource); + SetSources(sources); } } diff --git a/osu.Game/Skinning/RulesetSkinProvidingContainer.cs b/osu.Game/Skinning/RulesetSkinProvidingContainer.cs index f5a7788359..e63af0dfbd 100644 --- a/osu.Game/Skinning/RulesetSkinProvidingContainer.cs +++ b/osu.Game/Skinning/RulesetSkinProvidingContainer.cs @@ -60,8 +60,6 @@ namespace osu.Game.Skinning protected override void OnSourceChanged() { - ResetSources(); - // Populate a local list first so we can adjust the returned order as we go. var sources = new List(); @@ -91,8 +89,7 @@ namespace osu.Game.Skinning else sources.Add(rulesetResourcesSkin); - foreach (var skin in sources) - AddSource(skin); + SetSources(sources); } protected ISkin GetLegacyRulesetTransformedSkin(ISkin legacySkin) diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index d2c3a6e837..b769332e37 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -53,7 +53,7 @@ namespace osu.Game.Skinning : this() { if (skin != null) - AddSource(skin); + SetSources(new[] { skin }); } /// @@ -169,21 +169,10 @@ namespace osu.Game.Skinning } /// - /// Add a new skin to this provider. Will be added to the end of the lookup order precedence. + /// Replace the sources used for lookups in this container. /// - /// The skin to add. - protected void AddSource(ISkin skin) - { - skinSources = skinSources.Append((skin, new DisableableSkinSource(skin, this))).ToArray(); - - if (skin is ISkinSource source) - source.SourceChanged += TriggerSourceChanged; - } - - /// - /// Clears all skin sources. - /// - protected void ResetSources() + /// The new sources. + protected void SetSources(IEnumerable sources) { foreach (var skin in skinSources) { @@ -191,11 +180,17 @@ namespace osu.Game.Skinning source.SourceChanged -= TriggerSourceChanged; } - skinSources = Array.Empty<(ISkin skin, DisableableSkinSource wrapped)>(); + skinSources = sources.Select(skin => (skin, new DisableableSkinSource(skin, this))).ToArray(); + + foreach (var skin in skinSources) + { + if (skin.skin is ISkinSource source) + source.SourceChanged += TriggerSourceChanged; + } } /// - /// Invoked when any source has changed (either or a source registered via ). + /// Invoked when any source has changed (either or sources replaced via ). /// This is also invoked once initially during to ensure sources are ready for children consumption. /// protected virtual void OnSourceChanged() { } From 7fcb01bdf162beb4a53270cd90a7a6927dec2de1 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 12 Oct 2021 09:37:42 +0700 Subject: [PATCH 2395/2442] add changelog links test --- osu.Game.Tests/Chat/MessageFormatterTests.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game.Tests/Chat/MessageFormatterTests.cs b/osu.Game.Tests/Chat/MessageFormatterTests.cs index 2c2c4dc24e..af87fc17ad 100644 --- a/osu.Game.Tests/Chat/MessageFormatterTests.cs +++ b/osu.Game.Tests/Chat/MessageFormatterTests.cs @@ -509,5 +509,17 @@ namespace osu.Game.Tests.Chat Assert.AreEqual(LinkAction.External, result.Action); Assert.AreEqual("/relative", result.Argument); } + + [TestCase("https://dev.ppy.sh/home/changelog", "")] + [TestCase("https://dev.ppy.sh/home/changelog/lazer/2021.1012", "lazer/2021.1012")] + public void TestChangelogLinks(string link, string expectedArg) + { + MessageFormatter.WebsiteRootUrl = "dev.ppy.sh"; + + LinkDetails result = MessageFormatter.GetLinkDetails(link); + + Assert.AreEqual(LinkAction.OpenChangelog, result.Action); + Assert.AreEqual(expectedArg, result.Argument); + } } } From 47c7701e47be09d20f9c86475715477a9bc5b333 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 12 Oct 2021 09:40:45 +0700 Subject: [PATCH 2396/2442] handle changelog link in message formatter --- osu.Game/Online/Chat/MessageFormatter.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index cfffac7741..201ba6239b 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -177,6 +177,24 @@ namespace osu.Game.Online.Chat case "wiki": return new LinkDetails(LinkAction.OpenWiki, string.Join('/', args.Skip(3))); + + case "home": + if (mainArg != "changelog") + // handle link other than changelog as external for now + return new LinkDetails(LinkAction.External, url); + + switch (args.Length) + { + case 4: + // https://osu.ppy.sh/home/changelog + return new LinkDetails(LinkAction.OpenChangelog, string.Empty); + + case 6: + // https://osu.ppy.sh/home/changelog/lazer/2021.1006 + return new LinkDetails(LinkAction.OpenChangelog, $"{args[4]}/{args[5]}"); + } + + break; } } From 80722c7dc79d33ca948ccbf0aae91de4c919f472 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 12 Oct 2021 09:41:59 +0700 Subject: [PATCH 2397/2442] change `changelogOverlay` to field in `OsuGame` --- osu.Game/OsuGame.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 8a018f17d9..d233afe3aa 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -90,6 +90,8 @@ namespace osu.Game private WikiOverlay wikiOverlay; + private ChangelogOverlay changelogOverlay; + private SkinEditorOverlay skinEditor; private Container overlayContent; @@ -769,7 +771,7 @@ namespace osu.Game loadComponentSingleFile(chatOverlay = new ChatOverlay(), overlayContent.Add, true); loadComponentSingleFile(new MessageNotifier(), AddInternal, true); loadComponentSingleFile(Settings = new SettingsOverlay(), leftFloatingOverlayContent.Add, true); - var changelogOverlay = loadComponentSingleFile(new ChangelogOverlay(), overlayContent.Add, true); + loadComponentSingleFile(changelogOverlay = new ChangelogOverlay(), overlayContent.Add, true); loadComponentSingleFile(userProfile = new UserProfileOverlay(), overlayContent.Add, true); loadComponentSingleFile(beatmapSetOverlay = new BeatmapSetOverlay(), overlayContent.Add, true); loadComponentSingleFile(wikiOverlay = new WikiOverlay(), overlayContent.Add, true); From 6c84cf66589128096b9701b337eabc5d18520ac4 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 12 Oct 2021 09:42:29 +0700 Subject: [PATCH 2398/2442] add `ShowChangelogListing` and `ShowChangelogBuild` --- osu.Game/OsuGame.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index d233afe3aa..9ce049e1a1 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -403,6 +403,18 @@ namespace osu.Game /// The wiki page to show public void ShowWiki(string path) => waitForReady(() => wikiOverlay, _ => wikiOverlay.ShowPage(path)); + /// + /// Show changelog listing overlay + /// + public void ShowChangelogListing() => waitForReady(() => changelogOverlay, _ => changelogOverlay.ShowListing()); + + /// + /// Show changelog's build as an overlay + /// + /// The update stream name + /// The build version of the update stream + public void ShowChangelogBuild(string updateStream, string version) => waitForReady(() => changelogOverlay, _ => changelogOverlay.ShowBuild(updateStream, version)); + /// /// Present a beatmap at song select immediately. /// The user should have already requested this interactively. From 81246a110cc47d56875dc54134e2071eac22cb6b Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 12 Oct 2021 09:43:32 +0700 Subject: [PATCH 2399/2442] add `OpenChangelog` in `OsuGame.HandleLink` --- osu.Game/OsuGame.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 9ce049e1a1..7895715045 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -338,6 +338,17 @@ namespace osu.Game ShowWiki(link.Argument); break; + case LinkAction.OpenChangelog: + if (string.IsNullOrEmpty(link.Argument)) + ShowChangelogListing(); + else + { + var changelogArgs = link.Argument.Split("/"); + ShowChangelogBuild(changelogArgs[0], changelogArgs[1]); + } + + break; + default: throw new NotImplementedException($"This {nameof(LinkAction)} ({link.Action.ToString()}) is missing an associated action."); } From 077dcf5cd99d98be377356250c2cbdf1f8f1bbe3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Oct 2021 12:50:28 +0900 Subject: [PATCH 2400/2442] Add missing documentation for `SourceChanged` --- osu.Game/Skinning/ISkinSource.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Skinning/ISkinSource.cs b/osu.Game/Skinning/ISkinSource.cs index ba3e2bf6ad..a5ed0fc990 100644 --- a/osu.Game/Skinning/ISkinSource.cs +++ b/osu.Game/Skinning/ISkinSource.cs @@ -12,6 +12,9 @@ namespace osu.Game.Skinning /// public interface ISkinSource : ISkin { + /// + /// Fired whenever a source change occurs, signalling that consumers should re-query as required. + /// event Action SourceChanged; /// From a849e7343edd8cbf01e58fab98fd63c3c9e6b560 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Oct 2021 13:04:18 +0900 Subject: [PATCH 2401/2442] Add lock to ensure no threading shenanigans --- osu.Game/Skinning/SkinProvidingContainer.cs | 23 +++++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index b769332e37..fd80992000 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -41,6 +41,8 @@ namespace osu.Game.Skinning protected virtual bool AllowColourLookup => true; + private readonly object sourceSetLock = new object(); + /// /// A dictionary mapping each source to a wrapper which handles lookup allowances. /// @@ -174,18 +176,21 @@ namespace osu.Game.Skinning /// The new sources. protected void SetSources(IEnumerable sources) { - foreach (var skin in skinSources) + lock (sourceSetLock) { - if (skin.skin is ISkinSource source) - source.SourceChanged -= TriggerSourceChanged; - } + foreach (var skin in skinSources) + { + if (skin.skin is ISkinSource source) + source.SourceChanged -= TriggerSourceChanged; + } - skinSources = sources.Select(skin => (skin, new DisableableSkinSource(skin, this))).ToArray(); + skinSources = sources.Select(skin => (skin, new DisableableSkinSource(skin, this))).ToArray(); - foreach (var skin in skinSources) - { - if (skin.skin is ISkinSource source) - source.SourceChanged += TriggerSourceChanged; + foreach (var skin in skinSources) + { + if (skin.skin is ISkinSource source) + source.SourceChanged += TriggerSourceChanged; + } } } From d7cbacc5a066947d6500313dcd296df43364a2e0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Oct 2021 13:04:48 +0900 Subject: [PATCH 2402/2442] Rename `OnSourceChanged` and expand on xmldoc to mention that it doesn't fire `SourceChanged` --- osu.Game.Tests/Skins/TestSceneSkinProvidingContainer.cs | 2 +- osu.Game/Skinning/RulesetSkinProvidingContainer.cs | 2 +- osu.Game/Skinning/SkinProvidingContainer.cs | 9 ++++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Skins/TestSceneSkinProvidingContainer.cs b/osu.Game.Tests/Skins/TestSceneSkinProvidingContainer.cs index 2247f685e2..ffb3d41d18 100644 --- a/osu.Game.Tests/Skins/TestSceneSkinProvidingContainer.cs +++ b/osu.Game.Tests/Skins/TestSceneSkinProvidingContainer.cs @@ -64,7 +64,7 @@ namespace osu.Game.Tests.Skins public new void TriggerSourceChanged() => base.TriggerSourceChanged(); - protected override void OnSourceChanged() + protected override void RefreshSources() { SetSources(sources); } diff --git a/osu.Game/Skinning/RulesetSkinProvidingContainer.cs b/osu.Game/Skinning/RulesetSkinProvidingContainer.cs index e63af0dfbd..b884794739 100644 --- a/osu.Game/Skinning/RulesetSkinProvidingContainer.cs +++ b/osu.Game/Skinning/RulesetSkinProvidingContainer.cs @@ -58,7 +58,7 @@ namespace osu.Game.Skinning return base.CreateChildDependencies(parent); } - protected override void OnSourceChanged() + protected override void RefreshSources() { // Populate a local list first so we can adjust the returned order as we go. var sources = new List(); diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index fd80992000..c8e4c2c7b6 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -173,6 +173,9 @@ namespace osu.Game.Skinning /// /// Replace the sources used for lookups in this container. /// + /// + /// This does not implicitly fire a event. Consider calling if required. + /// /// The new sources. protected void SetSources(IEnumerable sources) { @@ -195,15 +198,15 @@ namespace osu.Game.Skinning } /// - /// Invoked when any source has changed (either or sources replaced via ). + /// Invoked after any consumed source change, before the external event is fired. /// This is also invoked once initially during to ensure sources are ready for children consumption. /// - protected virtual void OnSourceChanged() { } + protected virtual void RefreshSources() { } protected void TriggerSourceChanged() { // Expose to implementations, giving them a chance to react before notifying external consumers. - OnSourceChanged(); + RefreshSources(); SourceChanged?.Invoke(); } From e982f485c7bd5df017892b7d8dafeaa1f4d49a5e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Oct 2021 13:17:48 +0900 Subject: [PATCH 2403/2442] Remove drop shadow from `RoundedButton` As per @arflyte's spec, this should not have been there in the first place. --- osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs index 5cbbc40405..27e28f1e03 100644 --- a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs +++ b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs @@ -5,9 +5,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Effects; using osu.Game.Graphics.UserInterface; -using osuTK; namespace osu.Game.Graphics.UserInterfaceV2 { @@ -29,14 +27,6 @@ namespace osu.Game.Graphics.UserInterfaceV2 private void load(OsuColour colours) { BackgroundColour = colours.Blue3; - - Content.EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Offset = new Vector2(0, 2), - Radius = 4, - Colour = Colour4.Black.Opacity(0.15f) - }; } protected override void LoadComplete() From a986870a998762f635e27b149e9b04d66d18dd6e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Oct 2021 13:41:35 +0900 Subject: [PATCH 2404/2442] Reorder sections to be more in line with how often they are adjusted --- osu.Game/Overlays/SettingsOverlay.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index 55e8aee266..af91677adb 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -24,12 +24,12 @@ namespace osu.Game.Overlays protected override IEnumerable CreateSections() => new SettingsSection[] { new GeneralSection(), - new GraphicsSection(), - new AudioSection(), + new SkinSection(), new InputSection(createSubPanel(new KeyBindingPanel())), new UserInterfaceSection(), new GameplaySection(), - new SkinSection(), + new AudioSection(), + new GraphicsSection(), new OnlineSection(), new MaintenanceSection(), new DebugSection(), From 1d3d67c5f1ba160698380077061698122a8c2d4b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Oct 2021 13:56:10 +0900 Subject: [PATCH 2405/2442] Move gameplay cursor settings to gameplay section --- .../Settings/Sections/Gameplay/GeneralSettings.cs | 11 +++++++++++ osu.Game/Overlays/Settings/Sections/SkinSection.cs | 11 ----------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index 3a0265e453..44b2a28d28 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -81,6 +81,17 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay Current = config.GetBindable(OsuSetting.ScoreDisplayMode), Keywords = new[] { "scoring" } }, + new SettingsSlider + { + LabelText = SkinSettingsStrings.GameplayCursorSize, + Current = config.GetBindable(OsuSetting.GameplayCursorSize), + KeyboardStep = 0.01f + }, + new SettingsCheckbox + { + LabelText = SkinSettingsStrings.AutoCursorSize, + Current = config.GetBindable(OsuSetting.AutoCursorSize) + }, }; if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index d18099eb0a..929936acb4 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -71,17 +71,6 @@ namespace osu.Game.Overlays.Settings.Sections Action = () => skinEditor?.Toggle(), }, new ExportSkinButton(), - new SettingsSlider - { - LabelText = SkinSettingsStrings.GameplayCursorSize, - Current = config.GetBindable(OsuSetting.GameplayCursorSize), - KeyboardStep = 0.01f - }, - new SettingsCheckbox - { - LabelText = SkinSettingsStrings.AutoCursorSize, - Current = config.GetBindable(OsuSetting.AutoCursorSize) - }, new SettingsCheckbox { LabelText = SkinSettingsStrings.BeatmapSkins, From 8285f065c20a09ec3db33862f5d7f5270a2ac2f7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Oct 2021 14:26:57 +0900 Subject: [PATCH 2406/2442] Reorganise gameplay settings into more sections --- .../Localisation/GameplaySettingsStrings.cs | 25 ++++ .../Sections/Gameplay/AudioSettings.cs | 34 ++++++ .../Sections/Gameplay/BackgroundSettings.cs | 48 ++++++++ .../Sections/Gameplay/BeatmapSettings.cs | 39 +++++++ .../Sections/Gameplay/GeneralSettings.cs | 107 ------------------ .../Settings/Sections/Gameplay/HUDSettings.cs | 52 +++++++++ .../Sections/Gameplay/InputSettings.cs | 45 ++++++++ .../Settings/Sections/GameplaySection.cs | 6 +- .../Overlays/Settings/Sections/SkinSection.cs | 15 --- 9 files changed, 248 insertions(+), 123 deletions(-) create mode 100644 osu.Game/Overlays/Settings/Sections/Gameplay/AudioSettings.cs create mode 100644 osu.Game/Overlays/Settings/Sections/Gameplay/BackgroundSettings.cs create mode 100644 osu.Game/Overlays/Settings/Sections/Gameplay/BeatmapSettings.cs delete mode 100644 osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs create mode 100644 osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs create mode 100644 osu.Game/Overlays/Settings/Sections/Gameplay/InputSettings.cs diff --git a/osu.Game/Localisation/GameplaySettingsStrings.cs b/osu.Game/Localisation/GameplaySettingsStrings.cs index 6d6381b429..fa92187650 100644 --- a/osu.Game/Localisation/GameplaySettingsStrings.cs +++ b/osu.Game/Localisation/GameplaySettingsStrings.cs @@ -14,11 +14,36 @@ namespace osu.Game.Localisation /// public static LocalisableString GameplaySectionHeader => new TranslatableString(getKey(@"gameplay_section_header"), @"Gameplay"); + /// + /// "Beatmap" + /// + public static LocalisableString BeatmapHeader => new TranslatableString(getKey(@"beatmap_header"), @"Beatmap"); + /// /// "General" /// public static LocalisableString GeneralHeader => new TranslatableString(getKey(@"general_header"), @"General"); + /// + /// "Audio" + /// + public static LocalisableString AudioHeader => new TranslatableString(getKey(@"audio"), @"Audio"); + + /// + /// "HUD" + /// + public static LocalisableString HUDHeader => new TranslatableString(getKey(@"h_u_d"), @"HUD"); + + /// + /// "Input" + /// + public static LocalisableString InputHeader => new TranslatableString(getKey(@"input"), @"Input"); + + /// + /// "Background" + /// + public static LocalisableString BackgroundHeader => new TranslatableString(getKey(@"background"), @"Background"); + /// /// "Background dim" /// diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/AudioSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/AudioSettings.cs new file mode 100644 index 0000000000..dba64d695a --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/AudioSettings.cs @@ -0,0 +1,34 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Localisation; +using osu.Game.Configuration; +using osu.Game.Localisation; + +namespace osu.Game.Overlays.Settings.Sections.Gameplay +{ + public class AudioSettings : SettingsSubsection + { + protected override LocalisableString Header => GameplaySettingsStrings.AudioHeader; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + Children = new Drawable[] + { + new SettingsCheckbox + { + LabelText = GameplaySettingsStrings.PositionalHitsounds, + Current = config.GetBindable(OsuSetting.PositionalHitSounds) + }, + new SettingsCheckbox + { + LabelText = GameplaySettingsStrings.AlwaysPlayFirstComboBreak, + Current = config.GetBindable(OsuSetting.AlwaysPlayFirstComboBreak) + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/BackgroundSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/BackgroundSettings.cs new file mode 100644 index 0000000000..94e0c5e494 --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/BackgroundSettings.cs @@ -0,0 +1,48 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Localisation; +using osu.Game.Configuration; +using osu.Game.Localisation; + +namespace osu.Game.Overlays.Settings.Sections.Gameplay +{ + public class BackgroundSettings : SettingsSubsection + { + protected override LocalisableString Header => GameplaySettingsStrings.BackgroundHeader; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + Children = new Drawable[] + { + new SettingsSlider + { + LabelText = GameplaySettingsStrings.BackgroundDim, + Current = config.GetBindable(OsuSetting.DimLevel), + KeyboardStep = 0.01f, + DisplayAsPercentage = true + }, + new SettingsSlider + { + LabelText = GameplaySettingsStrings.BackgroundBlur, + Current = config.GetBindable(OsuSetting.BlurLevel), + KeyboardStep = 0.01f, + DisplayAsPercentage = true + }, + new SettingsCheckbox + { + LabelText = GameplaySettingsStrings.LightenDuringBreaks, + Current = config.GetBindable(OsuSetting.LightenDuringBreaks) + }, + new SettingsCheckbox + { + LabelText = GameplaySettingsStrings.FadePlayfieldWhenHealthLow, + Current = config.GetBindable(OsuSetting.FadePlayfieldWhenHealthLow), + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/BeatmapSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/BeatmapSettings.cs new file mode 100644 index 0000000000..a5d1fc5fc3 --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/BeatmapSettings.cs @@ -0,0 +1,39 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Localisation; +using osu.Game.Configuration; +using osu.Game.Localisation; + +namespace osu.Game.Overlays.Settings.Sections.Gameplay +{ + public class BeatmapSettings : SettingsSubsection + { + protected override LocalisableString Header => GameplaySettingsStrings.BeatmapHeader; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + Children = new Drawable[] + { + new SettingsCheckbox + { + LabelText = SkinSettingsStrings.BeatmapSkins, + Current = config.GetBindable(OsuSetting.BeatmapSkins) + }, + new SettingsCheckbox + { + LabelText = SkinSettingsStrings.BeatmapColours, + Current = config.GetBindable(OsuSetting.BeatmapColours) + }, + new SettingsCheckbox + { + LabelText = SkinSettingsStrings.BeatmapHitsounds, + Current = config.GetBindable(OsuSetting.BeatmapHitsounds) + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs deleted file mode 100644 index 44b2a28d28..0000000000 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Localisation; -using osu.Game.Configuration; -using osu.Game.Localisation; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Overlays.Settings.Sections.Gameplay -{ - public class GeneralSettings : SettingsSubsection - { - protected override LocalisableString Header => GameplaySettingsStrings.GeneralHeader; - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - Children = new Drawable[] - { - new SettingsSlider - { - LabelText = GameplaySettingsStrings.BackgroundDim, - Current = config.GetBindable(OsuSetting.DimLevel), - KeyboardStep = 0.01f, - DisplayAsPercentage = true - }, - new SettingsSlider - { - LabelText = GameplaySettingsStrings.BackgroundBlur, - Current = config.GetBindable(OsuSetting.BlurLevel), - KeyboardStep = 0.01f, - DisplayAsPercentage = true - }, - new SettingsCheckbox - { - LabelText = GameplaySettingsStrings.LightenDuringBreaks, - Current = config.GetBindable(OsuSetting.LightenDuringBreaks) - }, - new SettingsEnumDropdown - { - LabelText = GameplaySettingsStrings.HUDVisibilityMode, - Current = config.GetBindable(OsuSetting.HUDVisibilityMode) - }, - new SettingsCheckbox - { - LabelText = GameplaySettingsStrings.ShowDifficultyGraph, - Current = config.GetBindable(OsuSetting.ShowProgressGraph) - }, - new SettingsCheckbox - { - LabelText = GameplaySettingsStrings.ShowHealthDisplayWhenCantFail, - Current = config.GetBindable(OsuSetting.ShowHealthDisplayWhenCantFail), - Keywords = new[] { "hp", "bar" } - }, - new SettingsCheckbox - { - LabelText = GameplaySettingsStrings.FadePlayfieldWhenHealthLow, - Current = config.GetBindable(OsuSetting.FadePlayfieldWhenHealthLow), - }, - new SettingsCheckbox - { - LabelText = GameplaySettingsStrings.AlwaysShowKeyOverlay, - Current = config.GetBindable(OsuSetting.KeyOverlay) - }, - new SettingsCheckbox - { - LabelText = GameplaySettingsStrings.PositionalHitsounds, - Current = config.GetBindable(OsuSetting.PositionalHitSounds) - }, - new SettingsCheckbox - { - LabelText = GameplaySettingsStrings.AlwaysPlayFirstComboBreak, - Current = config.GetBindable(OsuSetting.AlwaysPlayFirstComboBreak) - }, - new SettingsEnumDropdown - { - LabelText = GameplaySettingsStrings.ScoreDisplayMode, - Current = config.GetBindable(OsuSetting.ScoreDisplayMode), - Keywords = new[] { "scoring" } - }, - new SettingsSlider - { - LabelText = SkinSettingsStrings.GameplayCursorSize, - Current = config.GetBindable(OsuSetting.GameplayCursorSize), - KeyboardStep = 0.01f - }, - new SettingsCheckbox - { - LabelText = SkinSettingsStrings.AutoCursorSize, - Current = config.GetBindable(OsuSetting.AutoCursorSize) - }, - }; - - if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows) - { - Add(new SettingsCheckbox - { - LabelText = GameplaySettingsStrings.DisableWinKey, - Current = config.GetBindable(OsuSetting.GameplayDisableWinKey) - }); - } - } - } -} diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs new file mode 100644 index 0000000000..2ffd19a020 --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs @@ -0,0 +1,52 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Localisation; +using osu.Game.Configuration; +using osu.Game.Localisation; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Overlays.Settings.Sections.Gameplay +{ + public class HUDSettings : SettingsSubsection + { + protected override LocalisableString Header => GameplaySettingsStrings.HUDHeader; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + Children = new Drawable[] + { + new SettingsEnumDropdown + { + LabelText = GameplaySettingsStrings.HUDVisibilityMode, + Current = config.GetBindable(OsuSetting.HUDVisibilityMode) + }, + new SettingsEnumDropdown + { + LabelText = GameplaySettingsStrings.ScoreDisplayMode, + Current = config.GetBindable(OsuSetting.ScoreDisplayMode), + Keywords = new[] { "scoring" } + }, + new SettingsCheckbox + { + LabelText = GameplaySettingsStrings.ShowDifficultyGraph, + Current = config.GetBindable(OsuSetting.ShowProgressGraph) + }, + new SettingsCheckbox + { + LabelText = GameplaySettingsStrings.ShowHealthDisplayWhenCantFail, + Current = config.GetBindable(OsuSetting.ShowHealthDisplayWhenCantFail), + Keywords = new[] { "hp", "bar" } + }, + new SettingsCheckbox + { + LabelText = GameplaySettingsStrings.AlwaysShowKeyOverlay, + Current = config.GetBindable(OsuSetting.KeyOverlay) + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/InputSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/InputSettings.cs new file mode 100644 index 0000000000..962572ca6e --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/InputSettings.cs @@ -0,0 +1,45 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Localisation; +using osu.Game.Configuration; +using osu.Game.Localisation; + +namespace osu.Game.Overlays.Settings.Sections.Gameplay +{ + public class InputSettings : SettingsSubsection + { + protected override LocalisableString Header => GameplaySettingsStrings.InputHeader; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + Children = new Drawable[] + { + new SettingsSlider + { + LabelText = SkinSettingsStrings.GameplayCursorSize, + Current = config.GetBindable(OsuSetting.GameplayCursorSize), + KeyboardStep = 0.01f + }, + new SettingsCheckbox + { + LabelText = SkinSettingsStrings.AutoCursorSize, + Current = config.GetBindable(OsuSetting.AutoCursorSize) + }, + }; + + if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows) + { + Add(new SettingsCheckbox + { + LabelText = GameplaySettingsStrings.DisableWinKey, + Current = config.GetBindable(OsuSetting.GameplayDisableWinKey) + }); + } + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs index 42d9d48d73..c4da641574 100644 --- a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs +++ b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs @@ -27,7 +27,11 @@ namespace osu.Game.Overlays.Settings.Sections { Children = new Drawable[] { - new GeneralSettings(), + new AudioSettings(), + new BeatmapSettings(), + new BackgroundSettings(), + new HUDSettings(), + new InputSettings(), new ModsSettings(), }; } diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 929936acb4..dc652e20a4 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -71,21 +71,6 @@ namespace osu.Game.Overlays.Settings.Sections Action = () => skinEditor?.Toggle(), }, new ExportSkinButton(), - new SettingsCheckbox - { - LabelText = SkinSettingsStrings.BeatmapSkins, - Current = config.GetBindable(OsuSetting.BeatmapSkins) - }, - new SettingsCheckbox - { - LabelText = SkinSettingsStrings.BeatmapColours, - Current = config.GetBindable(OsuSetting.BeatmapColours) - }, - new SettingsCheckbox - { - LabelText = SkinSettingsStrings.BeatmapHitsounds, - Current = config.GetBindable(OsuSetting.BeatmapHitsounds) - }, }; managerUpdated = skins.ItemUpdated.GetBoundCopy(); From c4347de57ef3d9e84f6cf20058a2d8c6f4b5e2a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Oct 2021 14:31:08 +0900 Subject: [PATCH 2407/2442] Move ruleset settings to own section --- .../Localisation/RulesetSettingsStrings.cs | 19 ++++++++ .../Settings/Sections/GameplaySection.cs | 26 +---------- .../Settings/Sections/RulesetSection.cs | 44 +++++++++++++++++++ osu.Game/Overlays/SettingsOverlay.cs | 1 + 4 files changed, 65 insertions(+), 25 deletions(-) create mode 100644 osu.Game/Localisation/RulesetSettingsStrings.cs create mode 100644 osu.Game/Overlays/Settings/Sections/RulesetSection.cs diff --git a/osu.Game/Localisation/RulesetSettingsStrings.cs b/osu.Game/Localisation/RulesetSettingsStrings.cs new file mode 100644 index 0000000000..a356c9e20b --- /dev/null +++ b/osu.Game/Localisation/RulesetSettingsStrings.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class RulesetSettingsStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.RulesetSettings"; + + /// + /// "Rulesets" + /// + public static LocalisableString Rulesets => new TranslatableString(getKey(@"rulesets"), @"Rulesets"); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} diff --git a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs index c4da641574..995df190bd 100644 --- a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs +++ b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs @@ -1,16 +1,11 @@ // 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 osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Game.Overlays.Settings.Sections.Gameplay; -using osu.Game.Rulesets; -using System.Linq; using osu.Framework.Graphics.Sprites; -using osu.Framework.Logging; using osu.Framework.Localisation; using osu.Game.Localisation; +using osu.Game.Overlays.Settings.Sections.Gameplay; namespace osu.Game.Overlays.Settings.Sections { @@ -35,24 +30,5 @@ namespace osu.Game.Overlays.Settings.Sections new ModsSettings(), }; } - - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) - { - foreach (Ruleset ruleset in rulesets.AvailableRulesets.Select(info => info.CreateInstance())) - { - try - { - SettingsSubsection section = ruleset.CreateSettings(); - - if (section != null) - Add(section); - } - catch (Exception e) - { - Logger.Error(e, "Failed to load ruleset settings"); - } - } - } } } diff --git a/osu.Game/Overlays/Settings/Sections/RulesetSection.cs b/osu.Game/Overlays/Settings/Sections/RulesetSection.cs new file mode 100644 index 0000000000..f9d15fc821 --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/RulesetSection.cs @@ -0,0 +1,44 @@ +// 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.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; +using osu.Framework.Logging; +using osu.Game.Rulesets; +using osu.Game.Localisation; + +namespace osu.Game.Overlays.Settings.Sections +{ + public class RulesetSection : SettingsSection + { + public override LocalisableString Header => RulesetSettingsStrings.Rulesets; + + public override Drawable CreateIcon() => new SpriteIcon + { + Icon = FontAwesome.Regular.Square + }; + + [BackgroundDependencyLoader] + private void load(RulesetStore rulesets) + { + foreach (Ruleset ruleset in rulesets.AvailableRulesets.Select(info => info.CreateInstance())) + { + try + { + SettingsSubsection section = ruleset.CreateSettings(); + + if (section != null) + Add(section); + } + catch (Exception e) + { + Logger.Error(e, "Failed to load ruleset settings"); + } + } + } + } +} diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index af91677adb..c84cba8189 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -28,6 +28,7 @@ namespace osu.Game.Overlays new InputSection(createSubPanel(new KeyBindingPanel())), new UserInterfaceSection(), new GameplaySection(), + new RulesetSection(), new AudioSection(), new GraphicsSection(), new OnlineSection(), From 5ca1d1d12ce6591f4e7f1d8a00e8672a01401d42 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Oct 2021 14:34:00 +0900 Subject: [PATCH 2408/2442] Reorder graphics settings and move gameplay related pieces out --- .../Localisation/GraphicsSettingsStrings.cs | 5 +++ .../Sections/Gameplay/BeatmapSettings.cs | 5 +++ .../Sections/Gameplay/GeneralSettings.cs | 36 +++++++++++++++++++ .../Settings/Sections/Gameplay/HUDSettings.cs | 7 ---- .../Settings/Sections/GameplaySection.cs | 1 + ...etailSettings.cs => ScreenshotSettings.cs} | 14 ++------ .../Settings/Sections/GraphicsSection.cs | 4 +-- 7 files changed, 51 insertions(+), 21 deletions(-) create mode 100644 osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs rename osu.Game/Overlays/Settings/Sections/Graphics/{DetailSettings.cs => ScreenshotSettings.cs} (67%) diff --git a/osu.Game/Localisation/GraphicsSettingsStrings.cs b/osu.Game/Localisation/GraphicsSettingsStrings.cs index 0e384f983f..f85cc0f2ae 100644 --- a/osu.Game/Localisation/GraphicsSettingsStrings.cs +++ b/osu.Game/Localisation/GraphicsSettingsStrings.cs @@ -104,6 +104,11 @@ namespace osu.Game.Localisation /// public static LocalisableString HitLighting => new TranslatableString(getKey(@"hit_lighting"), @"Hit lighting"); + /// + /// "Screenshots" + /// + public static LocalisableString Screenshots => new TranslatableString(getKey(@"screenshots"), @"Screenshots"); + /// /// "Screenshot format" /// diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/BeatmapSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/BeatmapSettings.cs index a5d1fc5fc3..aaa60ce81b 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/BeatmapSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/BeatmapSettings.cs @@ -33,6 +33,11 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay LabelText = SkinSettingsStrings.BeatmapHitsounds, Current = config.GetBindable(OsuSetting.BeatmapHitsounds) }, + new SettingsCheckbox + { + LabelText = GraphicsSettingsStrings.StoryboardVideo, + Current = config.GetBindable(OsuSetting.ShowStoryboard) + }, }; } } diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs new file mode 100644 index 0000000000..d4e4fd571d --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -0,0 +1,36 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Localisation; +using osu.Game.Configuration; +using osu.Game.Localisation; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Overlays.Settings.Sections.Gameplay +{ + public class GeneralSettings : SettingsSubsection + { + protected override LocalisableString Header => GameplaySettingsStrings.GeneralHeader; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + Children = new Drawable[] + { + new SettingsEnumDropdown + { + LabelText = GameplaySettingsStrings.ScoreDisplayMode, + Current = config.GetBindable(OsuSetting.ScoreDisplayMode), + Keywords = new[] { "scoring" } + }, + new SettingsCheckbox + { + LabelText = GraphicsSettingsStrings.HitLighting, + Current = config.GetBindable(OsuSetting.HitLighting) + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs index 2ffd19a020..e1b452e322 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/HUDSettings.cs @@ -6,7 +6,6 @@ using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Configuration; using osu.Game.Localisation; -using osu.Game.Rulesets.Scoring; namespace osu.Game.Overlays.Settings.Sections.Gameplay { @@ -24,12 +23,6 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay LabelText = GameplaySettingsStrings.HUDVisibilityMode, Current = config.GetBindable(OsuSetting.HUDVisibilityMode) }, - new SettingsEnumDropdown - { - LabelText = GameplaySettingsStrings.ScoreDisplayMode, - Current = config.GetBindable(OsuSetting.ScoreDisplayMode), - Keywords = new[] { "scoring" } - }, new SettingsCheckbox { LabelText = GameplaySettingsStrings.ShowDifficultyGraph, diff --git a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs index 995df190bd..dd4e561451 100644 --- a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs +++ b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs @@ -22,6 +22,7 @@ namespace osu.Game.Overlays.Settings.Sections { Children = new Drawable[] { + new GeneralSettings(), new AudioSettings(), new BeatmapSettings(), new BackgroundSettings(), diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/ScreenshotSettings.cs similarity index 67% rename from osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs rename to osu.Game/Overlays/Settings/Sections/Graphics/ScreenshotSettings.cs index 20b1d8d801..dbb9ddc1c1 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/ScreenshotSettings.cs @@ -9,25 +9,15 @@ using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Graphics { - public class DetailSettings : SettingsSubsection + public class ScreenshotSettings : SettingsSubsection { - protected override LocalisableString Header => GraphicsSettingsStrings.DetailSettingsHeader; + protected override LocalisableString Header => GraphicsSettingsStrings.Screenshots; [BackgroundDependencyLoader] private void load(OsuConfigManager config) { Children = new Drawable[] { - new SettingsCheckbox - { - LabelText = GraphicsSettingsStrings.StoryboardVideo, - Current = config.GetBindable(OsuSetting.ShowStoryboard) - }, - new SettingsCheckbox - { - LabelText = GraphicsSettingsStrings.HitLighting, - Current = config.GetBindable(OsuSetting.HitLighting) - }, new SettingsEnumDropdown { LabelText = GraphicsSettingsStrings.ScreenshotFormat, diff --git a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs index fd0718f9f2..591848506a 100644 --- a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs +++ b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs @@ -22,9 +22,9 @@ namespace osu.Game.Overlays.Settings.Sections { Children = new Drawable[] { - new RendererSettings(), new LayoutSettings(), - new DetailSettings(), + new RendererSettings(), + new ScreenshotSettings(), }; } } From 59202d27c799d3f98a71543a726344c03fd801e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Oct 2021 14:42:22 +0900 Subject: [PATCH 2409/2442] Add some missing labels --- osu.Game/Configuration/RandomSelectAlgorithm.cs | 2 +- osu.Game/Localisation/AudioSettingsStrings.cs | 5 +++++ osu.Game/Localisation/SkinSettingsStrings.cs | 5 +++++ .../Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs | 1 + osu.Game/Overlays/Settings/Sections/SkinSection.cs | 5 ++++- 5 files changed, 16 insertions(+), 2 deletions(-) diff --git a/osu.Game/Configuration/RandomSelectAlgorithm.cs b/osu.Game/Configuration/RandomSelectAlgorithm.cs index 8d0c87374f..b22f2ae485 100644 --- a/osu.Game/Configuration/RandomSelectAlgorithm.cs +++ b/osu.Game/Configuration/RandomSelectAlgorithm.cs @@ -10,7 +10,7 @@ namespace osu.Game.Configuration [Description("Never repeat")] RandomPermutation, - [Description("Random")] + [Description("True Random")] Random } } diff --git a/osu.Game/Localisation/AudioSettingsStrings.cs b/osu.Game/Localisation/AudioSettingsStrings.cs index aa6eabd7d1..008781c2e5 100644 --- a/osu.Game/Localisation/AudioSettingsStrings.cs +++ b/osu.Game/Localisation/AudioSettingsStrings.cs @@ -24,6 +24,11 @@ namespace osu.Game.Localisation /// public static LocalisableString VolumeHeader => new TranslatableString(getKey(@"volume_header"), @"Volume"); + /// + /// "Output device" + /// + public static LocalisableString OutputDevice => new TranslatableString(getKey(@"output_device"), @"Output device"); + /// /// "Master" /// diff --git a/osu.Game/Localisation/SkinSettingsStrings.cs b/osu.Game/Localisation/SkinSettingsStrings.cs index f22b4d6bf5..8b74b94d59 100644 --- a/osu.Game/Localisation/SkinSettingsStrings.cs +++ b/osu.Game/Localisation/SkinSettingsStrings.cs @@ -14,6 +14,11 @@ namespace osu.Game.Localisation /// public static LocalisableString SkinSectionHeader => new TranslatableString(getKey(@"skin_section_header"), @"Skin"); + /// + /// "Current skin" + /// + public static LocalisableString CurrentSkin => new TranslatableString(getKey(@"current_skin"), @"Current skin"); + /// /// "Skin layout editor" /// diff --git a/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs index d697b45424..0c54ae2763 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs @@ -28,6 +28,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio { dropdown = new AudioDeviceSettingsDropdown { + LabelText = AudioSettingsStrings.OutputDevice, Keywords = new[] { "speaker", "headphone", "output" } } }; diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index dc652e20a4..00198235c5 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -64,7 +64,10 @@ namespace osu.Game.Overlays.Settings.Sections { Children = new Drawable[] { - skinDropdown = new SkinSettingsDropdown(), + skinDropdown = new SkinSettingsDropdown + { + LabelText = SkinSettingsStrings.CurrentSkin + }, new SettingsButton { Text = SkinSettingsStrings.SkinLayoutEditor, From 24b87cf6552c16f9a49f0d0611d0b037e381ea79 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Oct 2021 14:53:19 +0900 Subject: [PATCH 2410/2442] Change some icons to be more descriptive (still placeholder) --- osu.Game/Overlays/Settings/Sections/GameplaySection.cs | 2 +- osu.Game/Overlays/Settings/Sections/RulesetSection.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs index dd4e561451..120e2d908c 100644 --- a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs +++ b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs @@ -15,7 +15,7 @@ namespace osu.Game.Overlays.Settings.Sections public override Drawable CreateIcon() => new SpriteIcon { - Icon = FontAwesome.Regular.Circle + Icon = FontAwesome.Regular.DotCircle }; public GameplaySection() diff --git a/osu.Game/Overlays/Settings/Sections/RulesetSection.cs b/osu.Game/Overlays/Settings/Sections/RulesetSection.cs index f9d15fc821..b9339d5299 100644 --- a/osu.Game/Overlays/Settings/Sections/RulesetSection.cs +++ b/osu.Game/Overlays/Settings/Sections/RulesetSection.cs @@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Settings.Sections public override Drawable CreateIcon() => new SpriteIcon { - Icon = FontAwesome.Regular.Square + Icon = FontAwesome.Solid.Chess }; [BackgroundDependencyLoader] From 129416835f2e7d78d7cbf5b120c7d9708de953f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Oct 2021 15:40:12 +0900 Subject: [PATCH 2411/2442] Remove stray `string.Empty` specification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Models/RealmBeatmapMetadata.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Models/RealmBeatmapMetadata.cs b/osu.Game/Models/RealmBeatmapMetadata.cs index 00dd120791..6ea7170d0f 100644 --- a/osu.Game/Models/RealmBeatmapMetadata.cs +++ b/osu.Game/Models/RealmBeatmapMetadata.cs @@ -26,7 +26,7 @@ namespace osu.Game.Models [JsonProperty("artist_unicode")] public string ArtistUnicode { get; set; } = string.Empty; - public string Author { get; set; } = string.Empty; // eventually should be linked to a persisted User. = string.Empty; + public string Author { get; set; } = string.Empty; // eventually should be linked to a persisted User. public string Source { get; set; } = string.Empty; From ce128476ae59d1011bf17fac4699ddfcb8f8a59f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Oct 2021 15:46:32 +0900 Subject: [PATCH 2412/2442] Remove public setter of `RealmFileStore.Storage` --- osu.Game/Stores/RealmFileStore.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Stores/RealmFileStore.cs b/osu.Game/Stores/RealmFileStore.cs index aac52b193c..5082b2c65b 100644 --- a/osu.Game/Stores/RealmFileStore.cs +++ b/osu.Game/Stores/RealmFileStore.cs @@ -17,14 +17,15 @@ using Realms; namespace osu.Game.Stores { /// - /// Handles the Store and retrieval of Files/FileSets to the database backing + /// Handles the storing of files to the file system (and database) backing. /// public class RealmFileStore { private readonly RealmContextFactory realmFactory; + public readonly IResourceStore Store; - public Storage Storage; + public readonly Storage Storage; public RealmFileStore(RealmContextFactory realmFactory, Storage storage) { From 0df9ab3eec5d6a3d039d3a0cac5ff62e297dd62f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Oct 2021 16:04:09 +0900 Subject: [PATCH 2413/2442] Fix migration blocking code running on the wrong thread --- osu.Game/OsuGameBase.cs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 7f4fe8a943..09eb482d16 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; +using System.Threading; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; @@ -410,11 +411,28 @@ namespace osu.Game { Logger.Log($@"Migrating osu! data from ""{Storage.GetFullPath(string.Empty)}"" to ""{path}""..."); - using (realmFactory.BlockAllOperations()) + IDisposable realmBlocker = null; + + try { - contextFactory.FlushConnections(); + ManualResetEventSlim readyToRun = new ManualResetEventSlim(); + + Scheduler.Add(() => + { + realmBlocker = realmFactory.BlockAllOperations(); + contextFactory.FlushConnections(); + + readyToRun.Set(); + }, false); + + readyToRun.Wait(); + (Storage as OsuStorage)?.Migrate(Host.GetStorage(path)); } + finally + { + realmBlocker?.Dispose(); + } Logger.Log(@"Migration complete!"); } From 76c64751de3007a21f6092359c60e4dbae4f0aaa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Oct 2021 16:18:12 +0900 Subject: [PATCH 2414/2442] Remove `RealmBeatmap.Clone` for the time being (incorrectly implemented) --- osu.Game/Models/RealmBeatmap.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game/Models/RealmBeatmap.cs b/osu.Game/Models/RealmBeatmap.cs index 09f8dafeb6..5049c1384d 100644 --- a/osu.Game/Models/RealmBeatmap.cs +++ b/osu.Game/Models/RealmBeatmap.cs @@ -97,11 +97,6 @@ namespace osu.Game.Models #endregion - /// - /// Returns a shallow-clone of this . - /// - public RealmBeatmap Clone() => (RealmBeatmap)MemberwiseClone(); - public bool AudioEquals(RealmBeatmap? other) => other != null && BeatmapSet != null && other.BeatmapSet != null From c66e50ac55147889526c3bfcc4109c9ba8eca339 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Oct 2021 16:19:58 +0900 Subject: [PATCH 2415/2442] Remove temporary logging --- osu.Game/Audio/Effects/AudioFilter.cs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/osu.Game/Audio/Effects/AudioFilter.cs b/osu.Game/Audio/Effects/AudioFilter.cs index 5eaa87af8d..268c31aa4c 100644 --- a/osu.Game/Audio/Effects/AudioFilter.cs +++ b/osu.Game/Audio/Effects/AudioFilter.cs @@ -132,16 +132,8 @@ namespace osu.Game.Audio.Effects { base.Dispose(isDisposing); - try - { - if (mixer.Effects.Contains(filter)) - detachFilter(); - } - catch (Exception e) - { - Logger.Log($"Exception in audio filter disposal: {e}"); - throw; - } + if (mixer.Effects.Contains(filter)) + detachFilter(); } } } From 6fec821a17d24b0462e42ebba7d64eaa5fd520a0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Oct 2021 16:20:13 +0900 Subject: [PATCH 2416/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index fefc2f6438..956093b2ac 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ff382f5227..184c9d3f63 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index fff0cbf418..38b920420b 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 55bd7d25126f0441a1367188e80e7f17d606f200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 9 Oct 2021 16:12:08 +0200 Subject: [PATCH 2417/2442] Add failing coverage for saving difficulty params from editor --- osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs index 2258a209e2..7540a0c69a 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs @@ -32,6 +32,8 @@ namespace osu.Game.Tests.Visual.Editing AddUntilStep("wait for editor load", () => editor != null); + AddStep("Set overall difficulty", () => editorBeatmap.Difficulty.OverallDifficulty = 7); + AddStep("Add timing point", () => editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint())); AddStep("Enter compose mode", () => InputManager.Key(Key.F1)); @@ -57,6 +59,7 @@ namespace osu.Game.Tests.Visual.Editing AddUntilStep("Wait for editor load", () => editor != null); AddAssert("Beatmap contains single hitcircle", () => editorBeatmap.HitObjects.Count == 1); + AddAssert("Beatmap has correct overall difficulty", () => editorBeatmap.Difficulty.OverallDifficulty == 7); } } } From b79cf0b58b3e5c3a136e912ab4b68dab2e3eec9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 9 Oct 2021 15:00:47 +0200 Subject: [PATCH 2418/2442] Add failing coverage for conversion not altering original beatmap --- osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index 64f1ee4a7a..6d63525011 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; using osu.Game.IO; +using osu.Game.IO.Serialization; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; @@ -109,6 +110,8 @@ namespace osu.Game.Tests.Beatmaps { var beatmap = GetBeatmap(name); + string beforeConversion = beatmap.Serialize(); + var converterResult = new Dictionary>(); var working = new ConversionWorkingBeatmap(beatmap) @@ -122,6 +125,10 @@ namespace osu.Game.Tests.Beatmaps working.GetPlayableBeatmap(CreateRuleset().RulesetInfo, mods); + string afterConversion = beatmap.Serialize(); + + Assert.AreEqual(beforeConversion, afterConversion, "Conversion altered original beatmap"); + return new ConvertResult { Mappings = converterResult.Select(r => From 1373cc02d7293a1d9884de1c791cad513d57bb4e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Oct 2021 16:42:09 +0900 Subject: [PATCH 2419/2442] Shallow clone `BeatmapInfo` during conversion process to avoid overwriting fields --- osu.Game/Beatmaps/BeatmapConverter.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs index f3434c5153..627e54c803 100644 --- a/osu.Game/Beatmaps/BeatmapConverter.cs +++ b/osu.Game/Beatmaps/BeatmapConverter.cs @@ -40,7 +40,13 @@ namespace osu.Game.Beatmaps public IBeatmap Convert(CancellationToken cancellationToken = default) { // We always operate on a clone of the original beatmap, to not modify it game-wide - return ConvertBeatmap(Beatmap.Clone(), cancellationToken); + var original = Beatmap.Clone(); + + // Shallow clone isn't enough to ensure we don't mutate beatmap info unexpectedly. + // Can potentially be removed after `Beatmap.Difficulty` doesn't save back to `Beatmap.BeatmapInfo`. + original.BeatmapInfo = original.BeatmapInfo.Clone(); + + return ConvertBeatmap(original, cancellationToken); } /// From d1b0dd1f6b72112aa10c18f9106b6b08a14b8d48 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 12 Oct 2021 17:13:36 +0900 Subject: [PATCH 2420/2442] Fix GameHosts being run on TPL threads --- osu.Game.Tests/ImportTest.cs | 2 +- osu.Game.Tournament.Tests/NonVisual/TournamentHostTest.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/ImportTest.cs b/osu.Game.Tests/ImportTest.cs index e888f51e98..dbeb453d4d 100644 --- a/osu.Game.Tests/ImportTest.cs +++ b/osu.Game.Tests/ImportTest.cs @@ -17,7 +17,7 @@ namespace osu.Game.Tests protected virtual TestOsuGameBase LoadOsuIntoHost(GameHost host, bool withBeatmap = false) { var osu = new TestOsuGameBase(withBeatmap); - Task.Run(() => host.Run(osu)) + Task.Factory.StartNew(() => host.Run(osu), TaskCreationOptions.LongRunning) .ContinueWith(t => Assert.Fail($"Host threw exception {t.Exception}"), TaskContinuationOptions.OnlyOnFaulted); waitForOrAssert(() => osu.IsLoaded, @"osu! failed to start in a reasonable amount of time"); diff --git a/osu.Game.Tournament.Tests/NonVisual/TournamentHostTest.cs b/osu.Game.Tournament.Tests/NonVisual/TournamentHostTest.cs index b14684200f..319a768e65 100644 --- a/osu.Game.Tournament.Tests/NonVisual/TournamentHostTest.cs +++ b/osu.Game.Tournament.Tests/NonVisual/TournamentHostTest.cs @@ -14,7 +14,7 @@ namespace osu.Game.Tournament.Tests.NonVisual public static TournamentGameBase LoadTournament(GameHost host, TournamentGameBase tournament = null) { tournament ??= new TournamentGameBase(); - Task.Run(() => host.Run(tournament)) + Task.Factory.StartNew(() => host.Run(tournament), TaskCreationOptions.LongRunning) .ContinueWith(t => Assert.Fail($"Host threw exception {t.Exception}"), TaskContinuationOptions.OnlyOnFaulted); WaitForOrAssert(() => tournament.IsLoaded, @"osu! failed to start in a reasonable amount of time"); return tournament; From e6cd0a837173ecfe18c79e7c4045b44fd5591b87 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Oct 2021 17:17:40 +0900 Subject: [PATCH 2421/2442] Remove unused using statements --- osu.Game/Audio/Effects/AudioFilter.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Audio/Effects/AudioFilter.cs b/osu.Game/Audio/Effects/AudioFilter.cs index 268c31aa4c..ee48bdd7d9 100644 --- a/osu.Game/Audio/Effects/AudioFilter.cs +++ b/osu.Game/Audio/Effects/AudioFilter.cs @@ -1,13 +1,11 @@ // 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.Diagnostics; using ManagedBass.Fx; using osu.Framework.Audio.Mixing; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Logging; namespace osu.Game.Audio.Effects { From 79ac64a0885620f9755fb5cc82cb226c34cb21c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Oct 2021 19:40:35 +0900 Subject: [PATCH 2422/2442] Split out editor save steps to try and catch test failure --- osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs index 2258a209e2..f4e704b22b 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs @@ -41,11 +41,11 @@ namespace osu.Game.Tests.Visual.Editing AddStep("Move to playfield", () => InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.Centre)); AddStep("Place single hitcircle", () => InputManager.Click(MouseButton.Left)); - AddStep("Save and exit", () => - { - InputManager.Keys(PlatformAction.Save); - InputManager.Key(Key.Escape); - }); + AddAssert("Beatmap contains single hitcircle", () => editorBeatmap.HitObjects.Count == 1); + + AddStep("Save", () => InputManager.Keys(PlatformAction.Save)); + + AddStep("Exit", () => InputManager.Key(Key.Escape)); AddUntilStep("Wait for main menu", () => Game.ScreenStack.CurrentScreen is MainMenu); From 91c286b1ade4e8ff9941f972dd5718f9544b0ceb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 12 Oct 2021 22:07:57 +0900 Subject: [PATCH 2423/2442] Fix intermittent TestScenePlaySongSelect test failures --- .../Visual/SongSelect/TestScenePlaySongSelect.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 067f1cabb4..4811fc979e 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -142,6 +142,8 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("store selected beatmap", () => selected = Beatmap.Value); + AddUntilStep("wait for beatmaps to load", () => songSelect.Carousel.ChildrenOfType().Any()); + AddStep("select next and enter", () => { InputManager.MoveMouseTo(songSelect.Carousel.ChildrenOfType() @@ -599,10 +601,10 @@ namespace osu.Game.Tests.Visual.SongSelect }); FilterableDifficultyIcon difficultyIcon = null; - AddStep("Find an icon", () => + AddUntilStep("Find an icon", () => { - difficultyIcon = set.ChildrenOfType() - .First(icon => getDifficultyIconIndex(set, icon) != getCurrentBeatmapIndex()); + return (difficultyIcon = set.ChildrenOfType() + .FirstOrDefault(icon => getDifficultyIconIndex(set, icon) != getCurrentBeatmapIndex())) != null; }); AddStep("Click on a difficulty", () => @@ -765,10 +767,10 @@ namespace osu.Game.Tests.Visual.SongSelect }); FilterableGroupedDifficultyIcon groupIcon = null; - AddStep("Find group icon for different ruleset", () => + AddUntilStep("Find group icon for different ruleset", () => { - groupIcon = set.ChildrenOfType() - .First(icon => icon.Items.First().BeatmapInfo.Ruleset.ID == 3); + return (groupIcon = set.ChildrenOfType() + .FirstOrDefault(icon => icon.Items.First().BeatmapInfo.Ruleset.ID == 3)) != null; }); AddAssert("Check ruleset is osu!", () => Ruleset.Value.ID == 0); From 8dcfc3dd7e2608ef1bc715a546dab144e1d9fab3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 12 Oct 2021 18:31:20 +0200 Subject: [PATCH 2424/2442] Replace no-op seeks with wait steps --- .../Visual/Gameplay/TestSceneFrameStabilityContainer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFrameStabilityContainer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFrameStabilityContainer.cs index 881e3f097c..ae0decaee1 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFrameStabilityContainer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFrameStabilityContainer.cs @@ -104,7 +104,7 @@ namespace osu.Game.Tests.Visual.Gameplay } [Test] - public void TestSeekToSameTimePreservesRate() + public void TestRatePreservedWhenTimeNotProgressing() { AddStep("set manual clock rate", () => manualClock.Rate = 1); seekManualTo(5000); @@ -114,13 +114,13 @@ namespace osu.Game.Tests.Visual.Gameplay seekManualTo(10000); checkRate(1); - seekManualTo(10000); + AddWaitStep("wait some", 3); checkRate(1); seekManualTo(5000); checkRate(-1); - seekManualTo(5000); + AddWaitStep("wait some", 3); checkRate(-1); seekManualTo(10000); From 94de24075e52d154ca042a8bf9116f5f434d8e23 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Oct 2021 12:18:56 +0900 Subject: [PATCH 2425/2442] Ensure startup imports trigger notifications --- osu.Game/OsuGame.cs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 7895715045..020cdebab6 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -211,13 +211,6 @@ namespace osu.Game [BackgroundDependencyLoader] private void load() { - if (args?.Length > 0) - { - var paths = args.Where(a => !a.StartsWith('-')).ToArray(); - if (paths.Length > 0) - Task.Run(() => Import(paths)); - } - dependencies.CacheAs(this); dependencies.Cache(SentryLogger); @@ -867,6 +860,19 @@ namespace osu.Game { if (mode.NewValue != OverlayActivation.All) CloseAllOverlays(); }; + + // Importantly, this should be run after binding PostNotification to the import handlers so they can present the import after game startup. + handleStartupImport(); + } + + private void handleStartupImport() + { + if (args?.Length > 0) + { + var paths = args.Where(a => !a.StartsWith('-')).ToArray(); + if (paths.Length > 0) + Task.Run(() => Import(paths)); + } } private void showOverlayAboveOthers(OverlayContainer overlay, OverlayContainer[] otherOverlays) From f69a56a26ac5debb0e365332e9f623fedb5fa937 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Oct 2021 12:19:10 +0900 Subject: [PATCH 2426/2442] Add test coverage of startup import sequence --- .../Navigation/TestSceneStartupImport.cs | 31 +++++++++++++++++++ osu.Game/Database/ArchiveModelManager.cs | 4 +-- .../Database/ImportProgressNotification.cs | 15 +++++++++ osu.Game/Tests/Visual/OsuGameTestScene.cs | 7 +++-- 4 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 osu.Game.Tests/Visual/Navigation/TestSceneStartupImport.cs create mode 100644 osu.Game/Database/ImportProgressNotification.cs diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneStartupImport.cs b/osu.Game.Tests/Visual/Navigation/TestSceneStartupImport.cs new file mode 100644 index 0000000000..cb7c334656 --- /dev/null +++ b/osu.Game.Tests/Visual/Navigation/TestSceneStartupImport.cs @@ -0,0 +1,31 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Testing; +using osu.Game.Database; +using osu.Game.Tests.Resources; + +namespace osu.Game.Tests.Visual.Navigation +{ + public class TestSceneStartupImport : OsuGameTestScene + { + private string importFilename; + + protected override TestOsuGame CreateTestGame() => new TestOsuGame(LocalStorage, API, new[] { importFilename }); + + public override void SetUpSteps() + { + AddStep("Prepare import beatmap", () => importFilename = TestResources.GetTestBeatmapForImport()); + + base.SetUpSteps(); + } + + [Test] + public void TestImportCreatedNotification() + { + AddUntilStep("Import notification was presented", () => Game.Notifications.ChildrenOfType().Count() == 1); + } + } +} diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index ee1a7e2900..c235fc7728 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -116,7 +116,7 @@ namespace osu.Game.Database /// One or more archive locations on disk. public Task Import(params string[] paths) { - var notification = new ProgressNotification { State = ProgressNotificationState.Active }; + var notification = new ImportProgressNotification(); PostNotification?.Invoke(notification); @@ -125,7 +125,7 @@ namespace osu.Game.Database public Task Import(params ImportTask[] tasks) { - var notification = new ProgressNotification { State = ProgressNotificationState.Active }; + var notification = new ImportProgressNotification(); PostNotification?.Invoke(notification); diff --git a/osu.Game/Database/ImportProgressNotification.cs b/osu.Game/Database/ImportProgressNotification.cs new file mode 100644 index 0000000000..aaee3e117f --- /dev/null +++ b/osu.Game/Database/ImportProgressNotification.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Overlays.Notifications; + +namespace osu.Game.Database +{ + public class ImportProgressNotification : ProgressNotification + { + public ImportProgressNotification() + { + State = ProgressNotificationState.Active; + } + } +} diff --git a/osu.Game/Tests/Visual/OsuGameTestScene.cs b/osu.Game/Tests/Visual/OsuGameTestScene.cs index 77db697cb6..6a11bd3fea 100644 --- a/osu.Game/Tests/Visual/OsuGameTestScene.cs +++ b/osu.Game/Tests/Visual/OsuGameTestScene.cs @@ -78,9 +78,11 @@ namespace osu.Game.Tests.Visual protected void CreateGame() { - AddGame(Game = new TestOsuGame(LocalStorage, API)); + AddGame(Game = CreateTestGame()); } + protected virtual TestOsuGame CreateTestGame() => new TestOsuGame(LocalStorage, API); + protected void PushAndConfirm(Func newScreen) { Screen screen = null; @@ -135,7 +137,8 @@ namespace osu.Game.Tests.Visual public new void PerformFromScreen(Action action, IEnumerable validScreens = null) => base.PerformFromScreen(action, validScreens); - public TestOsuGame(Storage storage, IAPIProvider api) + public TestOsuGame(Storage storage, IAPIProvider api, string[] args = null) + : base(args) { Storage = storage; API = api; From cf10239e70408533a0138f9afb5a45ee4e615e99 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 13 Oct 2021 12:51:41 +0900 Subject: [PATCH 2427/2442] Add a few nullabilities and DCC excludes --- osu.Game/Database/IHasRealmFiles.cs | 2 ++ osu.Game/Database/INamedFile.cs | 8 +++++--- osu.Game/Models/RealmBeatmapDifficulty.cs | 2 ++ osu.Game/Models/RealmFile.cs | 2 ++ osu.Game/Models/RealmNamedFileUsage.cs | 2 ++ osu.Game/Stores/RealmFileStore.cs | 2 ++ 6 files changed, 15 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/IHasRealmFiles.cs b/osu.Game/Database/IHasRealmFiles.cs index 2adfe73d1e..024d9f2a89 100644 --- a/osu.Game/Database/IHasRealmFiles.cs +++ b/osu.Game/Database/IHasRealmFiles.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using osu.Game.Models; +#nullable enable + namespace osu.Game.Database { /// diff --git a/osu.Game/Database/INamedFile.cs b/osu.Game/Database/INamedFile.cs index 9c94aed38c..2bd45d4e42 100644 --- a/osu.Game/Database/INamedFile.cs +++ b/osu.Game/Database/INamedFile.cs @@ -3,15 +3,17 @@ using osu.Game.Models; +#nullable enable + namespace osu.Game.Database { /// - /// Represent a join model which gives a filename and scope to a . + /// Represents a join model which gives a filename and scope to a . /// public interface INamedFile { - public string Filename { get; set; } + string Filename { get; set; } - public RealmFile File { get; set; } + RealmFile File { get; set; } } } diff --git a/osu.Game/Models/RealmBeatmapDifficulty.cs b/osu.Game/Models/RealmBeatmapDifficulty.cs index 44bfdda491..3c1dad69e4 100644 --- a/osu.Game/Models/RealmBeatmapDifficulty.cs +++ b/osu.Game/Models/RealmBeatmapDifficulty.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Testing; using osu.Game.Beatmaps; using Realms; @@ -8,6 +9,7 @@ using Realms; namespace osu.Game.Models { + [ExcludeFromDynamicCompile] [MapTo("BeatmapDifficulty")] public class RealmBeatmapDifficulty : EmbeddedObject, IBeatmapDifficultyInfo { diff --git a/osu.Game/Models/RealmFile.cs b/osu.Game/Models/RealmFile.cs index 6836d79d2d..2715f4be45 100644 --- a/osu.Game/Models/RealmFile.cs +++ b/osu.Game/Models/RealmFile.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.IO; +using osu.Framework.Testing; using osu.Game.IO; using Realms; @@ -9,6 +10,7 @@ using Realms; namespace osu.Game.Models { + [ExcludeFromDynamicCompile] [MapTo("File")] public class RealmFile : RealmObject, IFileInfo { diff --git a/osu.Game/Models/RealmNamedFileUsage.cs b/osu.Game/Models/RealmNamedFileUsage.cs index 59b446112d..ba12d51d0b 100644 --- a/osu.Game/Models/RealmNamedFileUsage.cs +++ b/osu.Game/Models/RealmNamedFileUsage.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using JetBrains.Annotations; +using osu.Framework.Testing; using osu.Game.Database; using osu.Game.IO; using Realms; @@ -10,6 +11,7 @@ using Realms; namespace osu.Game.Models { + [ExcludeFromDynamicCompile] public class RealmNamedFileUsage : EmbeddedObject, INamedFile, INamedFileUsage { public RealmFile File { get; set; } = null!; diff --git a/osu.Game/Stores/RealmFileStore.cs b/osu.Game/Stores/RealmFileStore.cs index 5082b2c65b..f7b7471634 100644 --- a/osu.Game/Stores/RealmFileStore.cs +++ b/osu.Game/Stores/RealmFileStore.cs @@ -8,6 +8,7 @@ using osu.Framework.Extensions; using osu.Framework.IO.Stores; using osu.Framework.Logging; using osu.Framework.Platform; +using osu.Framework.Testing; using osu.Game.Database; using osu.Game.Models; using Realms; @@ -19,6 +20,7 @@ namespace osu.Game.Stores /// /// Handles the storing of files to the file system (and database) backing. /// + [ExcludeFromDynamicCompile] public class RealmFileStore { private readonly RealmContextFactory realmFactory; From b37096f44062fbff4b4dd9e23708af55ffb4f249 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Oct 2021 13:25:30 +0900 Subject: [PATCH 2428/2442] Avoid using bindable for `AudioFilter` cutoff It doesn't play nicely with screen exiting, as it is automatically unbound during the exit process. Easiest to just avoid using this for now. --- .../Visual/Audio/TestSceneAudioFilter.cs | 27 +++++++--- osu.Game/Audio/Effects/AudioFilter.cs | 51 ++++++++++--------- .../Audio/Effects/ITransformableFilter.cs | 7 ++- 3 files changed, 50 insertions(+), 35 deletions(-) diff --git a/osu.Game.Tests/Visual/Audio/TestSceneAudioFilter.cs b/osu.Game.Tests/Visual/Audio/TestSceneAudioFilter.cs index 211543a881..851e0eb2a1 100644 --- a/osu.Game.Tests/Visual/Audio/TestSceneAudioFilter.cs +++ b/osu.Game.Tests/Visual/Audio/TestSceneAudioFilter.cs @@ -34,6 +34,9 @@ namespace osu.Game.Tests.Visual.Audio beatmap = new WaveformTestBeatmap(audio); track = beatmap.LoadTrack(); + OsuSliderBar lowPassCutoff; + OsuSliderBar highPassCutoff; + Add(new FillFlowContainer { Children = new Drawable[] @@ -43,33 +46,41 @@ namespace osu.Game.Tests.Visual.Audio lowpassText = new OsuSpriteText { Padding = new MarginPadding(20), - Text = $"Low Pass: {lowpassFilter.Cutoff.Value}hz", + Text = $"Low Pass: {lowpassFilter.Cutoff}hz", Font = new FontUsage(size: 40) }, - new OsuSliderBar + lowPassCutoff = new OsuSliderBar { Width = 500, Height = 50, Padding = new MarginPadding(20), - Current = { BindTarget = lowpassFilter.Cutoff } }, highpassText = new OsuSpriteText { Padding = new MarginPadding(20), - Text = $"High Pass: {highpassFilter.Cutoff.Value}hz", + Text = $"High Pass: {highpassFilter.Cutoff}hz", Font = new FontUsage(size: 40) }, - new OsuSliderBar + highPassCutoff = new OsuSliderBar { Width = 500, Height = 50, Padding = new MarginPadding(20), - Current = { BindTarget = highpassFilter.Cutoff } } } }); - lowpassFilter.Cutoff.ValueChanged += e => lowpassText.Text = $"Low Pass: {e.NewValue}hz"; - highpassFilter.Cutoff.ValueChanged += e => highpassText.Text = $"High Pass: {e.NewValue}hz"; + + lowPassCutoff.Current.ValueChanged += e => + { + lowpassText.Text = $"Low Pass: {e.NewValue}hz"; + lowpassFilter.Cutoff = e.NewValue; + }; + + highPassCutoff.Current.ValueChanged += e => + { + highpassText.Text = $"High Pass: {e.NewValue}hz"; + highpassFilter.Cutoff = e.NewValue; + }; } [SetUpSteps] diff --git a/osu.Game/Audio/Effects/AudioFilter.cs b/osu.Game/Audio/Effects/AudioFilter.cs index ee48bdd7d9..0152254945 100644 --- a/osu.Game/Audio/Effects/AudioFilter.cs +++ b/osu.Game/Audio/Effects/AudioFilter.cs @@ -4,7 +4,6 @@ using System.Diagnostics; using ManagedBass.Fx; using osu.Framework.Audio.Mixing; -using osu.Framework.Bindables; using osu.Framework.Graphics; namespace osu.Game.Audio.Effects @@ -21,10 +20,25 @@ namespace osu.Game.Audio.Effects private readonly BQFParameters filter; private readonly BQFType type; + private int cutoff; + /// - /// The current cutoff of this filter. + /// The cutoff frequency of this filter. /// - public BindableNumber Cutoff { get; } + public int Cutoff + { + get => cutoff; + set + { + if (value == cutoff) + return; + + int oldValue = cutoff; + cutoff = value; + + updateFilter(oldValue, cutoff); + } + } /// /// A Component that implements a BASS FX BiQuad Filter Effect. @@ -36,33 +50,25 @@ namespace osu.Game.Audio.Effects this.mixer = mixer; this.type = type; - int initialCutoff; - switch (type) { case BQFType.HighPass: - initialCutoff = 1; + cutoff = 1; break; case BQFType.LowPass: - initialCutoff = MAX_LOWPASS_CUTOFF; + cutoff = MAX_LOWPASS_CUTOFF; break; default: - initialCutoff = 500; // A default that should ensure audio remains audible for other filters. + cutoff = 500; // A default that should ensure audio remains audible for other filters. break; } - Cutoff = new BindableNumber(initialCutoff) - { - MinValue = 1, - MaxValue = MAX_LOWPASS_CUTOFF - }; - filter = new BQFParameters { lFilter = type, - fCenter = initialCutoff, + fCenter = cutoff, fBandwidth = 0, fQ = 0.7f // This allows fCenter to go up to 22049hz (nyquist - 1hz) without overflowing and causing weird filter behaviour (see: https://www.un4seen.com/forum/?topic=19542.0) }; @@ -70,8 +76,6 @@ namespace osu.Game.Audio.Effects // Don't start attached if this is low-pass or high-pass filter (as they have special auto-attach/detach logic) if (type != BQFType.LowPass && type != BQFType.HighPass) attachFilter(); - - Cutoff.ValueChanged += updateFilter; } private void attachFilter() @@ -86,40 +90,41 @@ namespace osu.Game.Audio.Effects mixer.Effects.Remove(filter); } - private void updateFilter(ValueChangedEvent cutoff) + private void updateFilter(int oldValue, int newValue) { // Workaround for weird behaviour when rapidly setting fCenter of a low-pass filter to nyquist - 1hz. if (type == BQFType.LowPass) { - if (cutoff.NewValue >= MAX_LOWPASS_CUTOFF) + if (newValue >= MAX_LOWPASS_CUTOFF) { detachFilter(); return; } - if (cutoff.OldValue >= MAX_LOWPASS_CUTOFF && cutoff.NewValue < MAX_LOWPASS_CUTOFF) + if (oldValue >= MAX_LOWPASS_CUTOFF) attachFilter(); } // Workaround for weird behaviour when rapidly setting fCenter of a high-pass filter to 1hz. if (type == BQFType.HighPass) { - if (cutoff.NewValue <= 1) + if (newValue <= 1) { detachFilter(); return; } - if (cutoff.OldValue <= 1 && cutoff.NewValue > 1) + if (oldValue <= 1) attachFilter(); } var filterIndex = mixer.Effects.IndexOf(filter); + if (filterIndex < 0) return; if (mixer.Effects[filterIndex] is BQFParameters existingFilter) { - existingFilter.fCenter = cutoff.NewValue; + existingFilter.fCenter = newValue; // required to update effect with new parameters. mixer.Effects[filterIndex] = existingFilter; diff --git a/osu.Game/Audio/Effects/ITransformableFilter.cs b/osu.Game/Audio/Effects/ITransformableFilter.cs index e4de4cf8ff..fb6a924f68 100644 --- a/osu.Game/Audio/Effects/ITransformableFilter.cs +++ b/osu.Game/Audio/Effects/ITransformableFilter.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 osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Transforms; @@ -12,7 +11,7 @@ namespace osu.Game.Audio.Effects /// /// The filter cutoff. /// - BindableNumber Cutoff { get; } + int Cutoff { get; set; } } public static class FilterableAudioComponentExtensions @@ -40,7 +39,7 @@ namespace osu.Game.Audio.Effects public static TransformSequence CutoffTo(this T component, int newCutoff, double duration, TEasing easing) where T : class, ITransformableFilter, IDrawable where TEasing : IEasingFunction - => component.TransformBindableTo(component.Cutoff, newCutoff, duration, easing); + => component.TransformTo(nameof(component.Cutoff), newCutoff, duration, easing); /// /// Smoothly adjusts filter cutoff over time. @@ -49,6 +48,6 @@ namespace osu.Game.Audio.Effects public static TransformSequence CutoffTo(this TransformSequence sequence, int newCutoff, double duration, TEasing easing) where T : class, ITransformableFilter, IDrawable where TEasing : IEasingFunction - => sequence.Append(o => o.TransformBindableTo(o.Cutoff, newCutoff, duration, easing)); + => sequence.Append(o => o.TransformTo(nameof(o.Cutoff), newCutoff, duration, easing)); } } From ae4dcbd8297b2803ca1df71c33722118a4a99134 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Oct 2021 13:26:20 +0900 Subject: [PATCH 2429/2442] Improve `PlayerLoader` audio and visual transitions --- osu.Game/Screens/Play/PlayerLoader.cs | 71 ++++++++++++++++----------- 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 94a61a4ef3..cf5bff57cf 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -35,6 +35,8 @@ namespace osu.Game.Screens.Play { protected const float BACKGROUND_BLUR = 15; + private const double content_out_duration = 300; + public override bool HideOverlaysOnEnter => hideOverlays; public override bool DisallowExternalBeatmapRulesetChanges => true; @@ -135,36 +137,39 @@ namespace osu.Game.Screens.Play muteWarningShownOnce = sessionStatics.GetBindable(Static.MutedAudioNotificationShownOnce); batteryWarningShownOnce = sessionStatics.GetBindable(Static.LowBatteryNotificationShownOnce); - InternalChild = (content = new LogoTrackingContainer + InternalChildren = new Drawable[] { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - }).WithChildren(new Drawable[] - { - MetadataInfo = new BeatmapMetadataDisplay(Beatmap.Value, Mods, content.LogoFacade) + (content = new LogoTrackingContainer { - Alpha = 0, Anchor = Anchor.Centre, Origin = Anchor.Centre, - }, - PlayerSettings = new FillFlowContainer + RelativeSizeAxes = Axes.Both, + }).WithChildren(new Drawable[] { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 20), - Margin = new MarginPadding(25), - Children = new PlayerSettingsGroup[] + MetadataInfo = new BeatmapMetadataDisplay(Beatmap.Value, Mods, content.LogoFacade) { - VisualSettings = new VisualSettings(), - new InputSettings() - } - }, - idleTracker = new IdleTracker(750), + Alpha = 0, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + PlayerSettings = new FillFlowContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 20), + Margin = new MarginPadding(25), + Children = new PlayerSettingsGroup[] + { + VisualSettings = new VisualSettings(), + new InputSettings() + } + }, + idleTracker = new IdleTracker(750), + }), lowPassFilter = new AudioFilter(audio.TrackMixer) - }); + }; if (Beatmap.Value.BeatmapInfo.EpilepsyWarning) { @@ -195,7 +200,6 @@ namespace osu.Game.Screens.Play epilepsyWarning.DimmableBackground = b; }); - lowPassFilter.CutoffTo(500, 100, Easing.OutCubic); Beatmap.Value.Track.AddAdjustment(AdjustableProperty.Volume, volumeAdjustment); content.ScaleTo(0.7f); @@ -240,15 +244,15 @@ namespace osu.Game.Screens.Play public override bool OnExiting(IScreen next) { cancelLoad(); + contentOut(); - content.ScaleTo(0.7f, 150, Easing.InQuint); - this.FadeOut(150); + // Ensure the screen doesn't expire until all the outwards fade operations have completed. + this.Delay(content_out_duration).FadeOut(); ApplyToBackground(b => b.IgnoreUserSettings.Value = true); BackgroundBrightnessReduction = false; Beatmap.Value.Track.RemoveAdjustment(AdjustableProperty.Volume, volumeAdjustment); - lowPassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF, 100, Easing.InCubic); return base.OnExiting(next); } @@ -344,6 +348,7 @@ namespace osu.Game.Screens.Play content.FadeInFromZero(400); content.ScaleTo(1, 650, Easing.OutQuint).Then().Schedule(prepareNewPlayer); + lowPassFilter.CutoffTo(1000, 650, Easing.OutQuint); ApplyToBackground(b => b?.FadeColour(Color4.White, 800, Easing.OutQuint)); } @@ -353,8 +358,9 @@ namespace osu.Game.Screens.Play // Ensure the logo is no longer tracking before we scale the content content.StopTracking(); - content.ScaleTo(0.7f, 300, Easing.InQuint); - content.FadeOut(250); + content.ScaleTo(0.7f, content_out_duration * 2, Easing.OutQuint); + content.FadeOut(content_out_duration, Easing.OutQuint); + lowPassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF, content_out_duration); } private void pushWhenLoaded() @@ -381,7 +387,7 @@ namespace osu.Game.Screens.Play contentOut(); - TransformSequence pushSequence = this.Delay(250); + TransformSequence pushSequence = this.Delay(content_out_duration); // only show if the warning was created (i.e. the beatmap needs it) // and this is not a restart of the map (the warning expires after first load). @@ -400,6 +406,11 @@ namespace osu.Game.Screens.Play }) .Delay(EpilepsyWarning.FADE_DURATION); } + else + { + // This goes hand-in-hand with the restoration of low pass filter in contentOut(). + this.TransformBindableTo(volumeAdjustment, 0, content_out_duration, Easing.OutCubic); + } pushSequence.Schedule(() => { From 26a1e40d2471a9d4e7e44ea0f98b1b1fbc6231c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Oct 2021 13:47:49 +0900 Subject: [PATCH 2430/2442] Fix storyboard outro during fail test not being lenient enough --- .../Visual/Gameplay/TestSceneStoryboardWithOutro.cs | 6 +++++- osu.Game/Screens/Play/FailAnimation.cs | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs index 3ed274690e..48a97d54f7 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs @@ -90,8 +90,12 @@ namespace osu.Game.Tests.Visual.Gameplay CreateTest(() => { AddStep("fail on first judgement", () => currentFailConditions = (_, __) => true); - AddStep("set storyboard duration to 1.3s", () => currentStoryboardDuration = 1300); + + // Fail occurs at 164ms with the provided beatmap. + // Fail animation runs for 2.5s realtime but the gameplay time change is *variable* due to the frequency transform being applied, so we need a bit of lenience. + AddStep("set storyboard duration to 0.6s", () => currentStoryboardDuration = 600); }); + AddUntilStep("wait for fail", () => Player.HasFailed); AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= currentStoryboardDuration); AddUntilStep("wait for fail overlay", () => Player.FailOverlay.State.Value == Visibility.Visible); diff --git a/osu.Game/Screens/Play/FailAnimation.cs b/osu.Game/Screens/Play/FailAnimation.cs index e250791b72..ea158c5789 100644 --- a/osu.Game/Screens/Play/FailAnimation.cs +++ b/osu.Game/Screens/Play/FailAnimation.cs @@ -22,7 +22,7 @@ namespace osu.Game.Screens.Play { /// /// Manage the animation to be applied when a player fails. - /// Single file; automatically disposed after use. + /// Single use and automatically disposed after use. /// public class FailAnimation : CompositeDrawable { From 29dfe33465aa61ba7b7866b4e136b7b11ce0fe84 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Oct 2021 15:13:35 +0900 Subject: [PATCH 2431/2442] Rewrite `AudioFilter` to be easier to follow (and fix tests) --- .../Visual/Audio/TestSceneAudioFilter.cs | 83 ++++++++---- osu.Game/Audio/Effects/AudioFilter.cs | 123 +++++++++--------- 2 files changed, 119 insertions(+), 87 deletions(-) diff --git a/osu.Game.Tests/Visual/Audio/TestSceneAudioFilter.cs b/osu.Game.Tests/Visual/Audio/TestSceneAudioFilter.cs index 851e0eb2a1..0107632f6e 100644 --- a/osu.Game.Tests/Visual/Audio/TestSceneAudioFilter.cs +++ b/osu.Game.Tests/Visual/Audio/TestSceneAudioFilter.cs @@ -6,6 +6,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Track; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -18,84 +19,112 @@ namespace osu.Game.Tests.Visual.Audio { public class TestSceneAudioFilter : OsuTestScene { - private OsuSpriteText lowpassText; - private AudioFilter lowpassFilter; + private OsuSpriteText lowPassText; + private AudioFilter lowPassFilter; - private OsuSpriteText highpassText; - private AudioFilter highpassFilter; + private OsuSpriteText highPassText; + private AudioFilter highPassFilter; private Track track; private WaveformTestBeatmap beatmap; + private OsuSliderBar lowPassSlider; + private OsuSliderBar highPassSlider; + [BackgroundDependencyLoader] private void load(AudioManager audio) { beatmap = new WaveformTestBeatmap(audio); track = beatmap.LoadTrack(); - OsuSliderBar lowPassCutoff; - OsuSliderBar highPassCutoff; - Add(new FillFlowContainer { Children = new Drawable[] { - lowpassFilter = new AudioFilter(audio.TrackMixer), - highpassFilter = new AudioFilter(audio.TrackMixer, BQFType.HighPass), - lowpassText = new OsuSpriteText + lowPassFilter = new AudioFilter(audio.TrackMixer), + highPassFilter = new AudioFilter(audio.TrackMixer, BQFType.HighPass), + lowPassText = new OsuSpriteText { Padding = new MarginPadding(20), - Text = $"Low Pass: {lowpassFilter.Cutoff}hz", + Text = $"Low Pass: {lowPassFilter.Cutoff}hz", Font = new FontUsage(size: 40) }, - lowPassCutoff = new OsuSliderBar + lowPassSlider = new OsuSliderBar { Width = 500, Height = 50, Padding = new MarginPadding(20), + Current = new BindableInt + { + MinValue = 0, + MaxValue = AudioFilter.MAX_LOWPASS_CUTOFF, + } }, - highpassText = new OsuSpriteText + highPassText = new OsuSpriteText { Padding = new MarginPadding(20), - Text = $"High Pass: {highpassFilter.Cutoff}hz", + Text = $"High Pass: {highPassFilter.Cutoff}hz", Font = new FontUsage(size: 40) }, - highPassCutoff = new OsuSliderBar + highPassSlider = new OsuSliderBar { Width = 500, Height = 50, Padding = new MarginPadding(20), + Current = new BindableInt + { + MinValue = 0, + MaxValue = AudioFilter.MAX_LOWPASS_CUTOFF, + } } } }); - lowPassCutoff.Current.ValueChanged += e => + lowPassSlider.Current.ValueChanged += e => { - lowpassText.Text = $"Low Pass: {e.NewValue}hz"; - lowpassFilter.Cutoff = e.NewValue; + lowPassText.Text = $"Low Pass: {e.NewValue}hz"; + lowPassFilter.Cutoff = e.NewValue; }; - highPassCutoff.Current.ValueChanged += e => + highPassSlider.Current.ValueChanged += e => { - highpassText.Text = $"High Pass: {e.NewValue}hz"; - highpassFilter.Cutoff = e.NewValue; + highPassText.Text = $"High Pass: {e.NewValue}hz"; + highPassFilter.Cutoff = e.NewValue; }; } + #region Overrides of Drawable + + protected override void Update() + { + base.Update(); + highPassSlider.Current.Value = highPassFilter.Cutoff; + lowPassSlider.Current.Value = lowPassFilter.Cutoff; + } + + #endregion + [SetUpSteps] public void SetUpSteps() { AddStep("Play Track", () => track.Start()); + + AddStep("Reset filters", () => + { + lowPassFilter.Cutoff = AudioFilter.MAX_LOWPASS_CUTOFF; + highPassFilter.Cutoff = 0; + }); + waitTrackPlay(); } [Test] - public void TestLowPass() + public void TestLowPassSweep() { AddStep("Filter Sweep", () => { - lowpassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF).Then() + lowPassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF).Then() .CutoffTo(0, 2000, Easing.OutCubic); }); @@ -103,7 +132,7 @@ namespace osu.Game.Tests.Visual.Audio AddStep("Filter Sweep (reverse)", () => { - lowpassFilter.CutoffTo(0).Then() + lowPassFilter.CutoffTo(0).Then() .CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF, 2000, Easing.InCubic); }); @@ -112,11 +141,11 @@ namespace osu.Game.Tests.Visual.Audio } [Test] - public void TestHighPass() + public void TestHighPassSweep() { AddStep("Filter Sweep", () => { - highpassFilter.CutoffTo(0).Then() + highPassFilter.CutoffTo(0).Then() .CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF, 2000, Easing.InCubic); }); @@ -124,7 +153,7 @@ namespace osu.Game.Tests.Visual.Audio AddStep("Filter Sweep (reverse)", () => { - highpassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF).Then() + highPassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF).Then() .CutoffTo(0, 2000, Easing.OutCubic); }); diff --git a/osu.Game/Audio/Effects/AudioFilter.cs b/osu.Game/Audio/Effects/AudioFilter.cs index 0152254945..d2a39e9db7 100644 --- a/osu.Game/Audio/Effects/AudioFilter.cs +++ b/osu.Game/Audio/Effects/AudioFilter.cs @@ -20,6 +20,8 @@ namespace osu.Game.Audio.Effects private readonly BQFParameters filter; private readonly BQFType type; + private bool isAttached; + private int cutoff; /// @@ -33,10 +35,8 @@ namespace osu.Game.Audio.Effects if (value == cutoff) return; - int oldValue = cutoff; cutoff = value; - - updateFilter(oldValue, cutoff); + updateFilter(cutoff); } } @@ -50,73 +50,58 @@ namespace osu.Game.Audio.Effects this.mixer = mixer; this.type = type; - switch (type) - { - case BQFType.HighPass: - cutoff = 1; - break; - - case BQFType.LowPass: - cutoff = MAX_LOWPASS_CUTOFF; - break; - - default: - cutoff = 500; // A default that should ensure audio remains audible for other filters. - break; - } - filter = new BQFParameters { lFilter = type, - fCenter = cutoff, fBandwidth = 0, - fQ = 0.7f // This allows fCenter to go up to 22049hz (nyquist - 1hz) without overflowing and causing weird filter behaviour (see: https://www.un4seen.com/forum/?topic=19542.0) + // This allows fCenter to go up to 22049hz (nyquist - 1hz) without overflowing and causing weird filter behaviour (see: https://www.un4seen.com/forum/?topic=19542.0) + fQ = 0.7f }; - // Don't start attached if this is low-pass or high-pass filter (as they have special auto-attach/detach logic) - if (type != BQFType.LowPass && type != BQFType.HighPass) - attachFilter(); + Cutoff = getInitialCutoff(type); } - private void attachFilter() + private int getInitialCutoff(BQFType type) { - Debug.Assert(!mixer.Effects.Contains(filter)); - mixer.Effects.Add(filter); - } - - private void detachFilter() - { - Debug.Assert(mixer.Effects.Contains(filter)); - mixer.Effects.Remove(filter); - } - - private void updateFilter(int oldValue, int newValue) - { - // Workaround for weird behaviour when rapidly setting fCenter of a low-pass filter to nyquist - 1hz. - if (type == BQFType.LowPass) + switch (type) { - if (newValue >= MAX_LOWPASS_CUTOFF) - { - detachFilter(); - return; - } + case BQFType.HighPass: + return 1; - if (oldValue >= MAX_LOWPASS_CUTOFF) - attachFilter(); + case BQFType.LowPass: + return MAX_LOWPASS_CUTOFF; + + default: + return 500; // A default that should ensure audio remains audible for other filters. + } + } + + private void updateFilter(int newValue) + { + switch (type) + { + case BQFType.LowPass: + // Workaround for weird behaviour when rapidly setting fCenter of a low-pass filter to nyquist - 1hz. + if (newValue >= MAX_LOWPASS_CUTOFF) + { + ensureDetached(); + return; + } + + break; + + // Workaround for weird behaviour when rapidly setting fCenter of a high-pass filter to 1hz. + case BQFType.HighPass: + if (newValue <= 1) + { + ensureDetached(); + return; + } + + break; } - // Workaround for weird behaviour when rapidly setting fCenter of a high-pass filter to 1hz. - if (type == BQFType.HighPass) - { - if (newValue <= 1) - { - detachFilter(); - return; - } - - if (oldValue <= 1) - attachFilter(); - } + ensureAttached(); var filterIndex = mixer.Effects.IndexOf(filter); @@ -131,12 +116,30 @@ namespace osu.Game.Audio.Effects } } + private void ensureAttached() + { + if (isAttached) + return; + + Debug.Assert(!mixer.Effects.Contains(filter)); + mixer.Effects.Add(filter); + isAttached = true; + } + + private void ensureDetached() + { + if (!isAttached) + return; + + Debug.Assert(mixer.Effects.Contains(filter)); + mixer.Effects.Remove(filter); + isAttached = false; + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - - if (mixer.Effects.Contains(filter)) - detachFilter(); + ensureDetached(); } } } From e36c484060edeb764626b49a23b4ed45cf73ae11 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 13 Oct 2021 15:30:58 +0900 Subject: [PATCH 2432/2442] Ensure rewinding before the spinner's start time --- .../TestSceneSpinnerRotation.cs | 29 ++++++------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs index 9da583a073..52ab39cfbd 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs @@ -30,6 +30,9 @@ namespace osu.Game.Rulesets.Osu.Tests { public class TestSceneSpinnerRotation : TestSceneOsuPlayer { + private const double spinner_start_time = 100; + private const double spinner_duration = 6000; + [Resolved] private AudioManager audioManager { get; set; } @@ -77,7 +80,7 @@ namespace osu.Game.Rulesets.Osu.Tests double finalTrackerRotation = 0, trackerRotationTolerance = 0; double finalSpinnerSymbolRotation = 0, spinnerSymbolRotationTolerance = 0; - addSeekStep(5000); + addSeekStep(spinner_start_time + 5000); AddStep("retrieve disc rotation", () => { finalTrackerRotation = drawableSpinner.RotationTracker.Rotation; @@ -90,7 +93,7 @@ namespace osu.Game.Rulesets.Osu.Tests }); AddStep("retrieve cumulative disc rotation", () => finalCumulativeTrackerRotation = drawableSpinner.Result.RateAdjustedRotation); - addSeekStep(2500); + addSeekStep(spinner_start_time + 2500); AddAssert("disc rotation rewound", // we want to make sure that the rotation at time 2500 is in the same direction as at time 5000, but about half-way in. // due to the exponential damping applied we're allowing a larger margin of error of about 10% @@ -102,7 +105,7 @@ namespace osu.Game.Rulesets.Osu.Tests // cumulative rotation is not damped, so we're treating it as the "ground truth" and allowing a comparatively smaller margin of error. () => Precision.AlmostEquals(drawableSpinner.Result.RateAdjustedRotation, finalCumulativeTrackerRotation / 2, 100)); - addSeekStep(5000); + addSeekStep(spinner_start_time + 5000); AddAssert("is disc rotation almost same", () => Precision.AlmostEquals(drawableSpinner.RotationTracker.Rotation, finalTrackerRotation, trackerRotationTolerance)); AddAssert("is symbol rotation almost same", @@ -140,7 +143,7 @@ namespace osu.Game.Rulesets.Osu.Tests [Test] public void TestSpinnerNormalBonusRewinding() { - addSeekStep(1000); + addSeekStep(spinner_start_time + 1000); AddAssert("player score matching expected bonus score", () => { @@ -201,24 +204,9 @@ namespace osu.Game.Rulesets.Osu.Tests AddAssert("spm almost same", () => Precision.AlmostEquals(expectedSpm, drawableSpinner.SpinsPerMinute.Value, 2.0)); } - private Replay applyRateAdjustment(Replay scoreReplay, double rate) => new Replay - { - Frames = scoreReplay - .Frames - .Cast() - .Select(replayFrame => - { - var adjustedTime = replayFrame.Time * rate; - return new OsuReplayFrame(adjustedTime, replayFrame.Position, replayFrame.Actions.ToArray()); - }) - .Cast() - .ToList() - }; - private void addSeekStep(double time) { AddStep($"seek to {time}", () => Player.GameplayClockContainer.Seek(time)); - AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, Player.DrawableRuleset.FrameStableClock.CurrentTime, 100)); } @@ -241,7 +229,8 @@ namespace osu.Game.Rulesets.Osu.Tests new Spinner { Position = new Vector2(256, 192), - EndTime = 6000, + StartTime = spinner_start_time, + Duration = spinner_duration }, } }; From db5099de3ad38a6a986f4870ed30e4e97df51bfa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Oct 2021 15:45:01 +0900 Subject: [PATCH 2433/2442] Add missing licence header --- osu.Game.Tests/Database/GeneralUsageTests.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tests/Database/GeneralUsageTests.cs b/osu.Game.Tests/Database/GeneralUsageTests.cs index 245981cd9b..3e8b6091fd 100644 --- a/osu.Game.Tests/Database/GeneralUsageTests.cs +++ b/osu.Game.Tests/Database/GeneralUsageTests.cs @@ -1,3 +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.Threading; using System.Threading.Tasks; From 93d7cdc509c8ec384c85e82228345ca778d429f7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Oct 2021 15:50:05 +0900 Subject: [PATCH 2434/2442] Don't check whether the source realm was closed or not Based on what we now know, this is not required, as long as there is another realm context open on the same thread. --- osu.Game/Database/RealmLive.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/RealmLive.cs b/osu.Game/Database/RealmLive.cs index 71fb44f617..abb69644d6 100644 --- a/osu.Game/Database/RealmLive.cs +++ b/osu.Game/Database/RealmLive.cs @@ -102,7 +102,7 @@ namespace osu.Game.Database } } - private bool originalDataValid => isCorrectThread && data.IsValid && !data.Realm.IsClosed; + private bool originalDataValid => isCorrectThread && data.IsValid; // this matches realm's internal thread validation (see https://github.com/realm/realm-dotnet/blob/903b4d0b304f887e37e2d905384fb572a6496e70/Realm/Realm/Native/SynchronizationContextScheduler.cs#L72) private bool isCorrectThread From 5e934cdd2bdb4badf923c90fd8d66c0340f2f8a4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 13 Oct 2021 17:42:55 +0900 Subject: [PATCH 2435/2442] Make CFS error and fail the job --- .github/workflows/ci.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 29cbdd2d37..128ae8f409 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -79,9 +79,12 @@ jobs: run: | # TODO: Add ignore filters and GitHub Workflow Command Reporting in CFS. That way we don't have to do this workaround. # FIXME: Suppress warnings from templates project - dotnet codefilesanity | while read -r line; do - echo "::warning::$line" - done + exit_code=0 + while read -r line; do + echo "::error::$line" + exit_code=1 + done <<< $(dotnet codefilesanity) + exit $exit_code # Temporarily disabled due to test failures, but it won't work anyway until the tool is upgraded. # - name: .NET Format (Dry Run) From e12249f1270a22cf5811a8bb7a9ee44f2c0250db Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 13 Oct 2021 17:56:33 +0900 Subject: [PATCH 2436/2442] Exclude empty lines --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 128ae8f409..0da1f9636b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -81,8 +81,10 @@ jobs: # FIXME: Suppress warnings from templates project exit_code=0 while read -r line; do + if [[ ! -z "$line" ]]; then echo "::error::$line" exit_code=1 + fi done <<< $(dotnet codefilesanity) exit $exit_code From 6d1b9be7fd4d5a482ac04dd9f01e0fe051bde2ff Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 13 Oct 2021 17:56:56 +0900 Subject: [PATCH 2437/2442] Test CFS failure --- osu.Game/OsuGame.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 020cdebab6..1c277a3bd7 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1,6 +1,3 @@ -// 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.Diagnostics; From 08bbdc70fc96a26832911db0a133c46ca289d2cf Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 13 Oct 2021 17:57:09 +0900 Subject: [PATCH 2438/2442] Revert "Test CFS failure" This reverts commit 6d1b9be7fd4d5a482ac04dd9f01e0fe051bde2ff. --- osu.Game/OsuGame.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 1c277a3bd7..020cdebab6 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1,3 +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.Diagnostics; From 2af1e6acc7bb0ffedda8fad659a215eed64ca3be Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Oct 2021 18:02:22 +0900 Subject: [PATCH 2439/2442] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 956093b2ac..db62667fc2 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 184c9d3f63..c6121ddd5f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 38b920420b..110de79285 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From ceec81d54d8727986415a308686369453420b7ca Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 13 Oct 2021 20:26:54 +0900 Subject: [PATCH 2440/2442] Fix another spinner test failure --- osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutoplay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutoplay.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutoplay.cs index 0ba775e5c7..37f1a846ad 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutoplay.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutoplay.cs @@ -45,8 +45,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { new Spinner { - Duration = 2000, - Position = OsuPlayfield.BASE_SIZE / 2 + Duration = 6000, + Position = OsuPlayfield.BASE_SIZE / 2, } } }, From fdf714271117211dc4ed3897c307c2466203ad90 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 13 Oct 2021 21:24:54 +0900 Subject: [PATCH 2441/2442] Remove Room.Position --- osu.Game/Online/Rooms/Room.cs | 8 -------- osu.Game/Screens/OnlinePlay/Components/RoomManager.cs | 2 -- .../OnlinePlay/Lounge/Components/RoomsContainer.cs | 2 +- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index 5f71b4be4a..39fc7f1da8 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -130,12 +130,6 @@ namespace osu.Game.Online.Rooms set => MaxAttempts.Value = value; } - /// - /// The position of this in the list. This is not read from or written to the API. - /// - [JsonIgnore] - public readonly Bindable Position = new Bindable(-1); // Todo: This does not need to exist. - public Room() { Password.BindValueChanged(p => HasPassword.Value = !string.IsNullOrEmpty(p.NewValue)); @@ -192,8 +186,6 @@ namespace osu.Game.Online.Rooms RecentParticipants.Clear(); RecentParticipants.AddRange(other.RecentParticipants); } - - Position.Value = other.Position.Value; } public void RemoveExpiredPlaylistItems() diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs index 381849189d..97377278a6 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs @@ -116,8 +116,6 @@ namespace osu.Game.Screens.OnlinePlay.Components if (ignoredRooms.Contains(room.RoomID.Value.Value)) return; - room.Position.Value = -room.RoomID.Value.Value; - try { foreach (var pi in room.Playlist) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index 907b7e308a..85efdcef1a 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -129,7 +129,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private void updateSorting() { foreach (var room in roomFlow) - roomFlow.SetLayoutPosition(room, room.Room.Position.Value); + roomFlow.SetLayoutPosition(room, -(room.Room.RoomID.Value ?? 0)); } protected override bool OnClick(ClickEvent e) From 47b6fb05f7cf0064f3cf900a048d142e4d4287da Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Oct 2021 00:12:38 +0900 Subject: [PATCH 2442/2442] Add missing github sponsors funding spec --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 0c6b80e97e..fc61573416 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1,2 @@ +github: ppy custom: https://osu.ppy.sh/home/support